web-dev-qa-db-ja.com

`command 1> file.txt 2> file.txt`の動作が` command 1> file.txt 2>&1`と異なるのはなぜですか?

Stdoutとstderrの両方を同じファイルにリダイレクトする場合は、command 1>file.txt 2>&1またはcommand &>file.txtを使用して行うことができます。しかし、command 1>file.txt 2>file.txtの動作が上記の2つのコマンドと異なるのはなぜですか?

以下は検証コマンドです。

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

結果を見る限り、command 1>file.txt 2>file.txtを実行すると、2番目のエコー文字列が最初のエコー文字列を上書きするように見えますが、なぜそうなるのかはわかりません。 (どこかに参照がありますか?)

21
fhiyo

次の2つのことを知っておく必要があります。

  • プロセスのアプリケーションモード側で既知のオープンファイル記述子は、ファイルと呼ばれる内部カーネルオブジェクトを参照しますdescription、開いているファイルのインスタンスです。ファイルごとに複数のファイル記述があり、ファイル記述を共有する複数のファイル記述子があります。
  • 現在のファイル位置は、ファイル記述の属性です。したがって、複数のファイル記述子が単一のファイル記述にマップされる場合、それらはすべて同じ現在のファイル位置を共有し、そのような1つのファイル記述子を使用して実行されたファイル位置への変更は、他のすべてのそのようなファイル記述子に影響します。

    このような変更は、read()/readv()write()/writev()lseek()などを呼び出すプロセスによって実行されます。システムコール。 echoコマンドは、もちろんwrite()/writev()を呼び出します。

だから何が起こるのですか:

  • command 1>file.txt 2>&1は、シェルがファイルを1度だけ開くため、1つのファイルの説明のみを作成します。シェルは、標準出力と標準エラーファイル記述子をその単一のファイル記述にマップ両方します。標準出力に標準出力にduplicatesします。したがって、いずれかのファイル記述子を介した書き込みは、共有された現在のファイル位置を移動します。各書き込みは、前の共通ファイル記述の書き込みの後に行われます。ご覧のように、echoコマンドの結果は互いに上書きしません。
  • command 1>file.txt 2>file.txtは、シェルが2つの明示的なリダイレクトに応答して同じファイルを2回開くため、twoファイルの説明を作成します。標準出力と標準エラーのファイル記述子は、2つの異なるファイルの説明にマップされ、次に同じ1つのファイルにマップされます。 2つのファイル記述には、完全に独立した現在のファイル位置があり、各書き込みは、同じファイル記述に対する直前の書き込みにすぐに行きます。結果を見るとわかるように、一方を介して書き込まれたものは、他方を介して書き込まれたものを、書き込みを実行する順序に応じてさまざまな方法で上書きできます。

参考文献

44
JdeBP

>を使用すると、ファイルが上書きされます。 stdoutとstderrが2つの異なる操作でファイルに書き込んでいるため、最後に書き込んだ操作が最初の操作を上書きします。

できるよ:

command 1>>file.txt 2>>file.txt

または

command &>file.txt bash v4以降のみ。

>>は、以前の操作の出力を置き換えないようにファイルを追加するように指示します。

&>2>&1を書くためのより簡単な方法です

16
jesse_b