Bashパイプ、ストリームコピー、各行で異なるコマンドを実行する

Bashパイプ、ストリームコピー、各行で異なるコマンドを実行する

出力の2行目で他のコマンドをどのように実行できますか?

たとえば、echo foo | sed pどのような出力がありますか?

foo
foo

sed 's/foo/bar/'私はそれらのいずれかを実行して取得したいと思います。

bar
foo

答え1

存在する

$ echo foo | sed p
foo
foo

最初のものはfooコマンドによって印刷されp、2番目はパターンスペースがループの終わり(次の行を読む直前)に印刷される-nため、印刷されません。sed

fooだからそれを取得するにはbar

$ echo foo | sed 'p;s/foo/bar/'
foo
bar

または:

$ echo foo | sed -n 'p;s/foo/bar/p'
foo
bar

bar置換がある場合にのみ印刷します(s置換が成功した場合はフラグ付きコマンドで)。p

bar、を取得するには、元の元の行を取得し、foo交換h結果を印刷して復元します。

$ echo foo | sed 'h;s/foo/bar/p;g'
bar
foo

答え2

データの最初の行に影響を与えないコマンドを実行するには、サブシェルでそのコマンドを実行し、次のように最初の行を読み書きするコマンドを前に付けます。

read -r line; echo "$line"   # pass first line unmodified
sed 's/foo/bar/'             # operate on rest of lines

全体の例は次のとおりです。

$ echo foo | sed p | ( read -r line; echo "$line"; sed 's/foo/bar/')
foo
bar

答え3

入力行番号に応じて他の操作を実行できるコマンドにそれらをパイプすることをお勧めします。

たとえば、次のようになりますsed

$ printf 'foo\nfoo\n' | sed '2,$s/foo/bar/'
foo
bar

これは、sedのアドレス指定構文を使用して、操作を2行s/foo/bar/からファイルの終わりまで()までの行に制限します。2,$

man sedGNUバージョン、より読みやすくフォーマットを少し変更しました):

Sedコマンドはアドレスなしで実行できます。この場合、コマンドはアドレスを持つすべての入力ラインで実行されます。この場合、コマンドはそのアドレスに一致する入力ラインでのみ実行されます。この場合、命令は、第1のアドレスから始まり第2のアドレスまで続くラインの包含範囲に一致する全ての入力ラインに対して実行される。

アドレス範囲について注意する3つの点は次のとおりです。

  • 構文はaddr1、addr2です(つまり、カンマでアドレスを区切ります)。

  • addr2 が前の行を選択しても、addr1 と一致する行は常に許可されます。

  • addr2が正規表現の場合、addr1と一致する行はテストされません。

またはawk:

$ printf 'foo\nfoo\n' | awk 'NR > 1 {gsub(/foo/,"bar")};1'
foo
bar

またはパール:

$ printf 'foo\nfoo\n' | perl -p -e 's/foo/bar/ if $. > 1'
foo
bar

awkとperlのバージョンは、行番号(NRawkの場合、$.perlの場合)が1より大きいかどうかをテストします。

関連情報