Bashのシェルブロックの競争条件はありますか?

Bashのシェルブロックの競争条件はありますか?

更新:この動作はLinux用のWindowsサブシステムで観察されました。ここでは2つの問題を扱っているようです。

  1. システム内のいくつかのエラー/競合状態。これは間違っていました。答えを参照してください。

  2. デフォルトのバッファサイズですhead

(2)の場合、@kusalandaが述べたように、特定のポイントheadまで入力を消費するデフォルトのバッファサイズがあるかもしれません。 ArchLinuxではi < 10tailLinux用のWindowsサブシステムの場合も同様です(つまり、一貫性のない出力はありませんtail)。 (1)に関して、Linux用のWindowsサブシステム自体に、これらの競合状態を引き起こすいくつかの内部バグがある可能性があります。 ArchLinuxではこの動作を観察できなかったためです。これは間違っていました。答えを参照してください。 「ポイント1」がありますが違います。

bashバージョンで次のコマンドを実行しようとしています4.4.19

{ for ((i = 0; i < 1000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }

時には予想される結果が表示されます。

$ ~ { for ((i = 0; i < 1000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
999
$ ~

しかし、次のような場合がよくあります。

$ ~ { for ((i = 0; i < 1000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
$ ~

私はこれが競争条件だと思います。しかし、2番目のコマンドブロックの先頭にスリープモードを追加すると、「競合状態」が発生し続けます。

$ ~ { for ((i = 0; i < 1000; ++i)); do echo $i; done; } | { sleep 10; head -n 1; echo ...; tail -n 1; }
0
...
$ ~

これは実際に競争条件ですか? 2番目のコードブロックで完全な入力を見るにはどうすればよいですか?10000代わりにを使用すると、1000この問題は発生しません(幸運な状況になる可能性があります)。

$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~ { for ((i = 0; i < 10000; ++i)); do echo $i; done; } | { head -n 1; echo ...; tail -n 1; }
0
...
9999
$ ~

ベストアンサー1

これは競争条件ではありません。エラーなしWSLまたはArchLinuxで。

あなたが言及したように、これはあなたがhead「しなければならない」以上のものを読んだときに働くことが十分でないか、まったく残らないからですtail。ただし、標準や他の場所では、head特定のバイト数だけを読み取る必要があるという内容はありません。ファイル全体を読み、最初の行を除くすべてのコンテンツを削除することもできます。

すべての可能な場合にこの問題を「修正」するには、head常に入力をバイト単位で読み取る必要があります(つまり、各バイトに対してシステムコールを実行する必要があります)。これは非常に非効率的で、99.999%の場合、役に立たない場合は絶対に不可能です。

これを避けたい場合はできます

1) パイプの代わりに一時ファイルを使用します。

{ head -n 2 <tmpfile; tail -n 3 <tmpfile; }

期待どおりに動作します。

2)頭/尾の組み合わせを別のものとして再実装してください。存在するawk

$ seq 10000 20000 | awk -vH=2 -vT=3 '{if(NR<=H)print; else a[i++%T]=$0}END{if((j=i-T)>0)print "..."; else j=0; while(j<i)print a[j++%T]}'
10000
10001
...
19998
19999
20000

おすすめ記事