stdin/stdout のリダイレクトの例が 3 つありますが、そのうち 1 つだけが意図したとおりに動作しています。どなたか説明していただければ幸いです。
目標は、file1 の内容を並べ替え、変更を同じファイルに保存することです。
sort file1 | tee file1 > /dev/null --------> 動作します
sort file1 | tee file1 --------> file1の内容は消去されます
sort file1 | tee file1 > file2 --------> file1の内容は消去されます
PS. tee は標準入力を各 FILE にコピーし、標準出力にもコピーします。
最初の例が機能する理由は何でしょうか?
答え1
Debian Wheezy での私のテストによれば、3 つのシナリオすべてが両方の結果につながる可能性があります (file1 がソートされて自身に書き戻されるか、何もソートされず、file1 に何も書き込まれません)。
これは正常な動作であり、Linux のファイル処理方法に起因するものだと思います。コマンドについて考えてみましょう。sort コマンドは、file1 の読み取りを開始し、その出力をすぐに tee に送信します。tee は出力を読み取り、それを file1 に書き戻して、/dev/null に出力します。sort が file1 全体を読み取るのに十分な速さである場合、tee はソートされた出力を取得します。ただし、tee がファイルのロックを取得した場合、そのファイルは消去されます (tee は、append オプションが使用されている場合を除き、常に出力ファイルを消去します)。これは、3 つのシナリオすべてで発生していることとほぼ同じです。
簡単に言うと、sort が file1 を読み取るのに十分速くない場合があります。そのような場合、tee は sort がファイルを読み取る前にファイルを消去します。
次の手順をお勧めします。
cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
stdout でソートされた出力を確認したい場合は、次のようにします。
cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
マルチプロセッサ システムで 1 つのファイルに対して 2 つの異なるコマンドを実行するのは得策ではありません。どちらが最初に実行されるかはわかりません。シングル スレッド システムでは、動作は異なり、順番に実行されます。
答え2
その動作が予測可能であるとは思えません (そして、それに依存することもないでしょう)。tee
コマンドは、おそらく新しいプロセスを開始して、その入力を「他の」宛先に送信します。オペレーティング システムは、宛先ファイルを作成し、そのファイルに一時バッファを書き込む時点まで、出力を「バッファリング」します。これが発生する正確なタイミング (およびソースの上書き) は、おそらく次の要因に依存します。
- ファイルのサイズとバッファに使用可能なメモリ
- 経過時間
- パイプからの入力が
tee
終了した場合
これは、より深い意味を持ちますbash
。プログラムの動作方法によってbash
開始されます。シェルは、入力したコマンドを解釈し、コマンドを実行するために必要なプログラムを起動するだけです。シェルは、各プログラムの動作を制御することはなく、それらのプログラムがどのように相互作用するかについては制御しません。プログラム (またはプログラム セット) に入力ファイルからデータを取得し、同じ文で同じ入力ファイルに結果を書き込むように要求するのは、ユーザーの責任です。
bash はユーザー コマンドのインタープリターにすぎないことを忘れないでください。これは、shell
ユーザーの意図をシステム コールに変換するオペレーティング システム周辺の機能にすぎません。
そしてそれは文書化されたも!またはこのメール同様の問題に対処する。あるいはこれStackOverflow スレッド。 またはこの Serverfault スレッド。
これは、 : のリダイレクトでも発生する可能性があることに注意してください。stdin
ファイル : から入力コマンドを取得する場合 $ myprog < commandfile
。myprog
がコマンドファイルに書き込む場合、 のすべてのコマンドが実行される保証はありませんcommandfile
。
本当に基本的な例えは、次の指示リストのようなものです。
- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
まずコピーを作成すると思いますか?(コマンドは高度な Bash スクリプト ガイド)
答え3
つまり、ファイルの元の内容はそのままにして、変更を加えたファイルを追加したいということですか?
デフォルトでは上書きされるため、-a フラグを使用して変更をファイルに追加してみてください。
答え4
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1
ファイルをプレースホルダーに書き込み、オリジナルの名前をバックアップに変更してから、プレースホルダーをオリジナルに移動することができます。