シェルスクリプトだけを使用してテキストファイルから特定の行を取得する


100

テキストファイルから特定の行を取得しようとしています。

これまでのところ、オンラインではsedのようなものしか見ていません(bashやsedなどではなく、shしか使用できません)。これを行うには、基本的なシェルスクリプトのみを使用する必要があります。

cat file | while read line
    do
       #do something
    done

上記のように、行を反復する方法を知っていますが、特定の行のコンテンツを取得する必要がある場合はどうなりますか?


行番号を知っていますか?
Mehul Rathod 2013年

1
次に、カウントします。
Ignacio Vazquez-Abrams

はい、行番号は5です@MehulRathod
GangstaGraham

3
なぜcat大丈夫sedですか?それは意味がありません。
William Pursell 2013年

5
誰もがノーと言うことができないのでcat。わぁ…かわいいcat

回答:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

shコマンドについてはどうですか、sed、awkは使用できません。これを質問でもっと明確にすべきです。
GangstaGraham 2013年

@GangstaGrahamあなたはラインを反復する方法を知っていると言いました、カウンターを追加するのはどうですか?カウンターがターゲットの行番号に達した場合は、行を取得してループを解除します。それは役立ちますか?
ケント

4
@KanagaveluSugumarがsedの情報ページを読みました。5!dは、5を除くすべての行を削除することを意味します。シェル変数が可能です。二重引用符が必要です。
ケント

13
別のバリアントを追加することをお勧めします。sed -n 5pこれ-nは「デフォルトでは出力がない」ことをp意味し、「印刷」を意味するため、初心者には覚えておくとより論理的です。別の意味です)。
Josip Rodin

1
あなたが正しい@JosipRodinは-n '5p'、この問題でも機能します。ここでの違いは、変更をファイルに書き戻すために5!d追加できること-iです。ただし、この質問については、もう一度言う-n 5p必要がありますがsed -n '5p' f > f2&& mv f2 f、私はあなたの意見に同意します。
ケント

21

line必要な行番号を保持する変数であると仮定するheadtail、and を使用できる場合、それは非常に簡単です:

head -n $line file | tail -1

そうでなければ、これはうまくいくはずです:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

この-eq比較は整数のため、行の内容($line)ではなく行番号が必要です。これは、たとえばwant=5ループの前に定義し、次に-eq比較を使用して修正する必要があります$want。[却下された編集から移動]
Josip Rodin

1
@JosipRodin私は同意するので、私はあなたのコメントに基づいて独立した編集提案をしました。うまくいけば、今回は拒否されません。
ビクターザマニアン2017


11

最高のパフォーマンス方法

sed '5q;d' file

sed5行目以降の行の読み取りを停止するため

ロジャーデュエック氏の実験を更新

wcanadian-insane(6.6MB)をインストールし、timeコマンドを使用してsed -n 1p / usr / share / dict / wordsとsed '1q; d' / usr / share / dict / wordsを比較しました。最初は0.043秒、2番目は0.002秒しかかからなかったため、「q」を使用するとパフォーマンスが向上します。


1
これはまた、一般的に書かれている:sed -n 5q
ウィリアムPursell

3
sed5行目以降の行の読み取りを停止するため、このソリューションが好きです。
Anthony Geoghegan 2016年

1
wcanadian-insane(6.6MB)をインストールし、コマンドを比較sed -n 1p /usr/share/dict/wordsしてsed '1q;d' /usr/share/dict/words使用しましたtime。最初は0.043秒、2番目は0.002秒しかかからなかったため、「q」を使用するとパフォーマンスが向上します。
Roger Dueck

5

たとえば、ファイルの10〜20行目を取得する場合は、次の2つの方法をそれぞれ使用できます。

head -n 20 york.txt | tail -11

または

sed -n '10,20p' york.txt 

p 上記のコマンドは印刷を意味します。

表示される内容は次のとおりです。 ここに画像の説明を入力してください


2

この種のことを行う標準的な方法は、外部ツールを使用することです。シェルスクリプトの作成中に外部ツールの使用を禁止するのはばかげています。ただし、外部ツールを使いたくない場合は、次のコマンドで5行目を印刷できます。

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

これは論理行5を印刷することに注意してください。つまり、input-file行の継続が含まれている場合、それらは1行としてカウントされます。この動作-rは、readコマンドに追加することで変更できます。(これはおそらく望ましい動作です。)


1
$((++i))バシズムのように見える; OPが外部ツールの使用を制限されている場合、それらがプレーン以上のものにアクセスできるとは思いません/bin/sh
Josip Rodin

@JosipRodinいいえ、これはPOSIX機能です(ただし、++増分のサポートは特にオプションとしてマークされています)。
tripleee

@tripleeeそれは/ bin / shのようなモダンなダッシュでは動作しないので、私はそれに依存しません。
Josip Rodin

しかし、$((i+=1))Dashで動作するような単純な回避策もあります。
Tripleee、

$(($i+1))私が考えていた簡単な回避策です。
Josip Rodin

1

ウィリアムパーセルの回答と並行して、オリジナルのv7 Bourneシェル(およびBashが利用できない場所)でも機能する単純な構成を次に示します。

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

break探していたラインを取得したときに、ループの外への最適化にも注意してください。


0

特に好きな答えはありませんでした。

ここに私がそれをした方法があります。

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

perlで簡単!ファイルから1、3、5行目を取得したい場合は、/ etc / passwdと言います。

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'しかしsmartmatchは実験的なものであり、使用は推奨されていません
Sorin

他のソリューションのどれもこれほど簡潔ではなく、これほど多くの柔軟性を可能にしません。(なぜ、時間を節約し、物事を容易にするすべてが「賢い人々」によって「
非難

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
少なくとも、なぜこの作業が質問をした人にそれをより明確にするために少し説明できますか?
2016年

したがって、最初のgrepは、先頭に行番号を追加するすべての行を選択します。次に、2番目のgrepは、開始時に行番号を照合して特定の行を選択します。そして最後に、エコーの行頭から行番号が削除されます。
Oder

これは、に比べて複雑で非効率的ですsed -n 5pが、もちろん、次のようなものに最適化できますsed -n '5!d;p;q'
tripleee
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.