以下を含む「test」という名前のテストファイルを作成しました。
xxx
yyy
zzz
私はコマンドを実行しました:
(sed '/y/ q'; echo aaa; cat) < test
そして私は得ました:
xxx
yyy
aaa
zzz
それから私は走った:
cat test | (sed '/y/ q'; echo aaa; cat)
そして得た:
xxx
yyy
aaa
質問
sed
は、 'y'を含む行に出会うまで読み取りと出力を行い、その後停止します。最初のケースでは、2番目のケースではなく、猫は残りを読み取り、出力します。
誰かがこの行動の違いの背後にある現象を説明できますか?
また、Ubuntu 16.04とCentos 6ではこのように機能しますが、Centos 7ではどちらのコマンドも「zzz」を出力しません。
入力ファイルがseekable(通常のファイルからの読み取りと同様)またはun-seekable(パイプからの読み取りと同様)、sed
(およびその他の標準ユーティリティ)の動作は異なります(_ このリンク のINPUT FILES
セクションを読み取ります)。
ドキュメントからの引用:
標準のユーティリティがシーク可能な入力ファイルを読み取り、ファイルの終わりに達する前にエラーなしで終了した場合、ユーティリティは、開いているファイルの説明のファイルオフセットが、ユーティリティによって処理された最後のバイトのすぐ後に正しく配置されるようにします。
だから:
(sed '/y/ q'; echo aaa; cat) < test
sed
はEOFに到達する前にq
uitコマンドを実行したため、zzz
行の先頭にファイルオフセットが残っているため、cat
は残りの行の印刷を続行できます(GNU sed一部の条件ではPOSIXに準拠していません。以下を参照してください)。
そして、ドキュメントから続けます:
シークできないファイルの場合、そのファイルの開いているファイルの説明におけるファイルオフセットの状態は指定されていません
この場合の動作は規定されていません。 sed
を含むほとんどの標準ツールは、入力を可能な限り使用します。ファイルオフセットを復元せずにyyy
行とq
uitを読み取って渡すため、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
...