入力ファイルがシーク可能(通常のファイルからの読み取りなど)またはシーク不可(パイプからの読み取りなど)の場合sed(およびその他の標準ユーティリティ)の動作は異なります(このリンクのINPUT FILESセクションを読む)。
ドキュメントからの引用:
標準ユーティリティがシーク可能な入力ファイルを読み取り、ファイルの終わりに到達する前にエラーなしで終了する場合、ユーティリティは、開いているファイルの説明のファイルオフセットが、ユーティリティによって処理された最後のバイトのすぐ後ろに適切に配置されるようにします。
だから:
(sed '/y/ q'; echo aaa; cat) < test
sedqEOFに達する前にuitコマンドを実行したため、zzz行の先頭にファイルオフセットcatが残っていたため、残りの行の印刷を続行できます(GNU sedは、ある条件ではPOSIXに準拠していません。以下を参照)。
そして、ドキュメントから継続:
シークできないファイルの場合、そのファイルの開いているファイルの説明のファイルオフセットの状態は指定されていません
この場合、動作は指定されていません。ほとんどの標準ツールinclude sedは、可能な限り入力を消費します。ファイルオフセットを復元せずにyyy行を渡すと、quitは何も残されませんcat。
GNU sedは標準に準拠しておらず、システムのstdio実装とglibcバージョンに依存しています。
$ (gsed '/y/ q'; echo aaa; cat) < test
xxx
yyy
aaa
ここでは、結果はMac OSX 10.11.6、仮想マシンCentos 7.2-glibc 2.17、Ubuntu 14.04-glibc 2.19から取得されました。これらはCEPHバックエンドを使用してOpenstackで実行されます。
これらのシステムでは、-uオプションを使用して標準の動作を実現できます。
(gsed -u '/y/ q'; echo aaa; cat) </tmp/test
およびパイプの場合:
$ cat test | (gsed -u '/y/ q'; echo aaa; cat)
xxx
yyy
aaa
zzz
sed一度に1バイトずつ読み取る必要があるため、パフォーマンスが非常に非効率的になります。からの部分的な出力strace:
$ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test'
...
[pid 5248] read(3, "", 4096) = 0
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "x", 1) = 1
[pid 5248] read(0, "\n", 1) = 1
xxx
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "y", 1) = 1
[pid 5248] read(0, "\n", 1) = 1
yyy
...
catstdinは実際のファイルにバインドされているため、最初のケースでは(サブシェルで)ファイル記述子を再利用できます。2番目のケースでは、stdinはパイプからのものであり、実際のファイルではありません。また(sed '/y/ q'; echo aaa; cat) < <(cat test)、印刷しないことに注意してくださいzzz。