次のシェルコマンドは、入力ストリームの奇数行だけを印刷すると予想されます。
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
しかし、最初の行だけを印刷しますaaa
。
-c
()オプションと一緒に使用すると、--bytes
同じことは発生しません。
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
コマンドは1234512345
期待どおりに出力されます。ただし、これは以下にのみ適用されます。コアツールこのユーティリティの実装head
。これ忙しい箱実装はまだ追加の文字を消費するので、出力は12345
。
この特定の実装は最適化目的のためのようです。行がどこで終わるのか分からないので、読みたい文字数がわからない。入力ストリームで追加の文字を使用しない唯一の方法は、ストリームをバイト単位で読み取ることです。しかし、ストリームから一度に1バイトずつ読み込むのは遅いことがあります。そのため、head
入力ストリームを十分に大きなバッファに読み込んでから、そのバッファの行数を計算したいと思います。
--bytes
オプションを使用しても同様です。この場合、読み取る必要があるバイト数がわかります。したがって、このバイト数だけを読み取ることができます。これコアライブラリ実装ではこの機会を活用していますが、忙しい箱それ以外の場合でも、バッファから必要以上のバイトを読み込みます。これはおそらく実装を簡素化するために行われたようです。
だからここに質問があります。head
ユーティリティは入力ストリームで必要以上の文字を正しく消費していますか? Unixユーティリティには一種の標準がありますか?それでは、この動作を指定しますか?
ポリスチレン
Ctrl+C
上記のコマンドを停止するには、キーを押す必要があります。 UnixユーティリティはEOF
。
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
私は単純さのためにそれを使用しません。
答え1
ヘッドユーティリティが入力ストリームで必要以上の文字を消費するのは正しいですか?
はい、許可されています(下記参照)。
Unixユーティリティには一種の標準がありますか?
では、この動作を指定しますか?
実際に紹介から:
標準ユーティリティが検索可能な入力ファイルを読み取り、ファイルの終わりに到達する前にエラーなしで終了する場合、ユーティリティは、開かれたファイル記述のファイルオフセットがユーティリティが処理した最後のバイトの後に正しく配置されていることを確認する必要があります。検索できないファイルの場合、ファイルの開かれたファイル記述のファイルオフセット状態は指定されません。
head
そのうちの一つ標準ユーティリティしたがって、POSIX準拠の実装では上記の動作を実装する必要があります。
牛に似た一種の栄養head
するファイル記述子を正しい場所に保持しようとしましたが、パイプに見つからないため、テストはその場所を復元しません。次のコマンドを使用してこれを表示できますstrace
。
$ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
...
read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
lseek(0, -13, SEEK_CUR) = -1 ESPIPE (Illegal seek)
...
read
17バイト(使用可能なすべての入力)を返し、head
そのうち4つを処理してから13バイト後ろに移動しようとしましたが、それはできません。 (GNUがhead
8KiBバッファを使用していることもここで見ることができます。)
head
Calculate the number of bytes(非標準)を指示すると、読み取るバイト数がわかっているため(この方法で実装されている場合)、それに応じて読み取りを制限できます。これがテストが機能するhead -c 5
理由です。 GNUはhead
5バイトのみを読み取るので、ファイル記述子の場所を回復しようとする必要はありません。
文書をファイルに書き込んでそのファイルを使用すると、必要な動作が得られます。
$ echo -e "aaa\nbbb\nccc\nddd\n" > file
$ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
aaa
ccc
答え2
これ頭ユーティリティは入力ファイルを標準出力にコピーし、各ファイルの出力を指定された時点で終了する必要があります。
head
入力からどのくらいの読みが必要かはわかりません。バイト単位で読み込むように要求するのは、ほとんどの場合非常に遅いため、愚かなことです。
ただし、これはread
組み込み/ユーティリティで解決されます。read
パイプで一度に1バイトずつすべてのシェルを見つけることができます。標準テキストこれは、その行を読むために次のことが必要であると解釈できます。
これ読むユーティリティは、標準入力から1つ以上のシェル変数で単一の論理行を読み取る必要があります。
シェルスクリプトで使用するためのread
一般的なユースケースは次のとおりです。
read someline
if something ; then
someprogram ...
fi
ここで、標準入力はsomeprogram
シェルの標準入力と同じですが、バッファリングされた読み取りの後に残ったものではなく、消費された最初の入力行someprogram
以降のすべての内容を読み取ることが期待できます。一方、あなたの例のように使用することははるかにまれです。read
read
head
本当に他のすべての行を削除するには、入力全体を一度に処理できるいくつかのツールを使用する方が良いです。
$ seq 1 10 | sed -ne '1~2p' # GNU sed
$ seq 1 10 | sed -e 'n;d' # works in GNU sed and the BSD sed on macOS
$ seq 1 10 | awk 'NR % 2'
$ seq 1 10 | perl -ne 'print if $. % 2'
答え3
awk '{if (NR%2) == 1) print;}'