STDOUTのn番目の行を取得するコマンド


210

STDOUTのn行目を取得できるbashコマンドはありますか?

つまり、これを取るようなもの

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

そして、次のようなことをする

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

これは、再利用することを意図したスクリプトを作成する場合は悪い習慣になると思いますが、シェルを日常的に使用する場合は、そのような方法でSTDOUTをフィルタリングできると便利です。

また、これは書くのが簡単なコマンド(バッファーSTDOUT、特定の行を返す)であることも理解していますが、スクリプトを配置しなくても使用できる標準のシェルコマンドがあるかどうかを知りたいです。


5
私はこれを単語の使用に賛成票を投じましたmagic-command
セブンアース

回答:


332

sedさまざまな目的でを使用する:

ls -l | sed -n 2p

この代替方法を使用すると、必要な行が印刷されると入力の読み取りが停止するため、より効率的に見えますが、フィードプロセスでSIGPIPEが生成され、不要なエラーメッセージが生成される場合があります。

ls -l | sed -n -e '2{p;q}'

私は通常、最初のものを使用するのに十分な頻度でこれを確認しました(とにかく入力が簡単です)が、lsSIGPIPEを取得したときに文句を言うコマンドではありません。

一連の行の場合:

ls -l | sed -n 2,4p

いくつかの範囲の線について:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

pはどういう意味ですか?2p 3pのような?2/3行目だと思うのですが、その背後にある理論・理解とは?
レオUfimtsev

4
@LeoUfimtsev:その前の行の範囲によって識別される行を出力pするsedステートメントです。したがって2,4p、2行目、3行目、4行目を印刷することを意味します
Jonathan Leffler 2017

3
そしてn、他のすべての行を印刷しないことを意味します
Timo

85
ls -l | head -2 | tail -1

13
+2、しかし私はお勧めしますhead -n 2 | tail -n 1—現代の表と裏はそうでなければ警告を出す傾向があります。
マイケルクレリン-ハッカー2009年

2
2つのプロセスを使用してデータをフィルター処理するのは少々やりすぎです(しかし、最近のマシンの能力を考えると、おそらく対処するでしょう)。1つの範囲を処理するための一般化は完全に簡単ではありません(範囲N..Mの場合はを使用しますhead -n M | tail -n M-N+1)。複数の範囲を処理するための一般化は不可能です。
ジョナサンレフラー

3
頭は尾にパイプでつながっていますか?恐ろしい。それを行うための複数のより良い方法。
camh

16
* nixコマンドラインの
優れた

1
別の方法で行の範囲を印刷する方法:($ ls -l | awk '{if (NR>=4 && NR<=64) print}'条件を簡単に追加できる、複数の範囲などを追加できます)
user10607

41

素敵なヘッド/テールウェイの代替:

ls -al | awk 'NR==2'

または

ls -al | sed -n '2p'

1
私のawkの方がいいですが、sedはいいです。
マイケルクレリン-ハッカー2009年

「if」の必要はありません-ハッカーの回答を参照してください(システム上のすべてのファイルを検索する部分を除く)。;-)
追って通知があるまで一時停止。

何の'2p'略ですか?
細断処理

1
@shredding short answer print 2行目; long->使用すると、パターンに一致する行のみにアドレスの後にコマンドが与えられます。簡単に言うと、/ pフラグと一緒に使用すると、一致する行が2回出力されます:sed '/ PATTERN / p'ファイル。そしてもちろん、PATTERNはさらに
Medhat 2017年

2変数の場合はどうなりますか?すべての例で一重引用符を使用していますが、varでは二重引用符が必要です。varsを使用する場合、awkを単一引用符で処理するように「プログラム」できますか?例line =1; ls | awk 'NR==$line'。私はそれbashが変数で二重引用符を使用することを知っています。
ティモ

21

sed1lineから:

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

awk1lineから:

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

9

完全を期すために;-)

短いコード

find / | awk NR==3

短命

find / | awk 'NR==3 {print $0; exit}'

あなたは完全性について冗談ではありません!
追って通知があるまで一時停止。

私はそうではありません;-)実際には、なぜ誰もが使用しているのかわかりません。ls元の質問は誰かの質問だったSTDOUTので、もっと大きくした方がよいと思いました。
マイケルクレリン-ハッカー2009年

少なくともGNU awkでは、デフォルトのアクションは{ print $0 }なので、 `awk 'NR == 3'は同じことを書く短い方法です。
ephemient

そのアクションに「; exit」を追加する必要があります。残りの行を何も処理しない場合、残りの行を処理しても意味がありません。
camh

@camh:Jonathan Lefferの回答を参照してください。「給餌プロセスでSIGPIPEが生成され、不要なエラーメッセージが生成される場合があります」。
ephemient


6

awkを使用できます。

ls -l | awk 'NR==2'

更新

上記のコードは、off-by-oneエラーのため、必要な結果が得られません。ls - lコマンドの最初の行は合計行です。そのためには、次の改訂されたコードが機能します。

ls -l | awk 'NR==3'

4

Perlは簡単に利用できますか?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

当然のことながら、7を必要な数に置き換えます。


私がしているの後、個人的にあったものに一般に最も簡単であるように思われますので、私のfavでおき、(。$%7 == 0){印刷する場合は、」n番目、perlの-n -e exit(0); } '
mat kelcey

@matkelceyでは$ awk 'NR % 7' filename、ファイルの7行ごとに印刷することもできます。
user10607

3

別のポスターが提案されました

ls -l | head -2 | tail -1

しかし、ヘッドをテールにパイプすると、ラインNまでのすべてが2回処理されるように見えます。

尾を頭に配管

ls -l | tail -n +2 | head -n1

より効率的でしょうか?


0

はい、(Jonathan Lefflerが既に指摘したように)最も効率的な方法は、sedをprint&quitで使用することです。

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

pipefailとPIPESTATUSの使用は非常に興味深いものであり、これはこのページに多くを追加します。
Mike S

0

うーん

私の場合、sedは機能しませんでした。私が提案する:

「奇数」行の場合1,3,5,7 ... ls | awk '0 ==(NR + 1)%2'

「偶数」行の場合2,4,6,8 ls | awk '0 ==(NR)%2'


-1

より完全にするために...

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

2行目になるまで行を捨て、その後1行目を印刷します。したがって、3行目を印刷します。

2行目なら…

ls -l | (read; head -n1)

必要なだけの「読み取り」を入れます。


-1

これを.bash_profileに追加しました

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

そしてそれはうまくいく

gdf <file name>

私は困惑しています—これは質問とどう関係しているのですか?
ジョナサンレフラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.