次の3つのスクリプトが与えられました。
printf 'a\nb\nc\n' > file && { head -n 1; cat; } < file
printf 'a\nb\nc\n' | { head -n 1; cat; }
{ head -n 1; cat; } < <(printf 'a\nb\nc\n')
それぞれについて私が期待する結果は次のとおりです。
a
b
c
しかし、これらのシステムの一部、一部のシステムではそうではありません。たとえば、cygwinでは:
$ printf 'a\nb\nc\n' > file && { head -n 1; cat; } < file
a
b
c
$ printf 'a\nb\nc\n' | { head -n 1; cat; }
a
$ { head -n 1; cat; } < <(printf 'a\nb\nc\n')
a
このスクリプトの出力が異なる原因は何ですか?
追加情報 - これは単なるhead
問題ではないようです。
$ printf 'a\nb\nc\n' | { sed '1q'; cat; }
a
$ printf 'a\nb\nc\n' | { awk '1;{exit}'; cat; }
a
$ { sed '1q'; cat; } < <(printf 'a\nb\nc\n')
a
$ { awk '1;{exit}'; cat; } < <(printf 'a\nb\nc\n')
a
入力がパイプから来ているかファイルから来ているかにかかわらず、入力からいくつかの行を読み取り、残りの部分は他のコマンドに委ねるシェルの強力なPOSIX方式です(つまり、すべての操作を実行するためにawkまたは同様のものを一度だけ呼び出すことはありません) ?
この質問は、回答の下のコメントに触発されました。特定の列の値に基づいて.csv全体をソートします。。
ベストアンサー1
head
可能完全な入力を読んでください。それ〜しなければならない少なくとも出力される内容を読んでください(そうでなければ論理的に不可能です)。しかし、もっと読むことができます。
通常、head
オペレーティングシステムは固定サイズのデータを読み取る必要があります。バッファーread
(システムコールなどを呼び出して)それからそのバッファで改行文字を探し、必要な行数に達するまで出力を印刷します。
みんなPOSIX互換head
呼び出された実装は、lseek
入力のファイル位置を出力にコピーされたセクションの最後の直後にリセットします。ただし、これはファイルを検索できる場合にのみ可能です。これには通常のファイルが含まれますが、パイプは含まれません。入力がパイプの場合、head
読み取った内容はすべてパイプから削除され、元に戻すことはできません。これは、<file
(一般ファイル)|
と(パイプライン)<()
の間で観察された違いを説明します。
上記規格の関連部分は次のとおりです。
標準ユーティリティが検索可能な入力ファイルを読み取り、ファイルの終わりに到達する前にエラーなしで終了する場合、ユーティリティは、開かれたファイル記述のファイルオフセットがユーティリティが処理した最後のバイトの後に正しく配置されていることを確認する必要があります。検索できないファイルの場合、ファイルの開かれたファイル記述のファイルオフセット状態は指定されません。互換アプリケーションは、次の3つのコマンドが同じであると仮定してはいけません。
tail -n +2 file (sed -n 1q; cat) < file cat file | (sed -n 1q; cat)
2番目のコマンドは、ファイルを検索できる場合にのみ最初のコマンドと同じです。 3 番目のコマンドは、開いたファイル記述のファイル・オフセットを指定しないままにします。 head、read、shなどの他のユーティリティも同様のプロパティを持ちます。
ksh93の組み込み実装(以降はアクティブになり、ビルド時に含まれる)head
などの一部の実装では、ksh93の場合、入力を取得できないときに出力の最後の行にカーソルを保持したくない場合があります。入力をバイトごとに一度読み込むか(シェル組み込みが通常実行する)、または次のように読みます。head
builtin head
read
現れるこれが可能なシステム(Linuxではない)でパイプの内容を読む前に。ただし、パフォーマンスに重大な影響を与えるため、これは例外です。