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


20

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

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

回答:


43

次の2つのことを知る必要があります。

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

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

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

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

参考文献


1
ファイルの説明ではなく、ファイルの説明を開く必要があります。ファイル自体よりも、ファイルがどのように開かれたかの記録に関するものです。これは、少なくともPOSIX、Linux、Solaris、およびGNUのドキュメントで使用されている用語です。
ステファンシャゼラス

16

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

できるよ:

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

または

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

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

&> 書くのが簡単な方法です 2>&1


2
なぜないls 1>&0ls 0>&0、まだLSの出力を示して?
イヴァン

>>作品を使用して驚いた。独立したオフセットを持つ2つのファイル記述の問題がないのはなぜですか?@JdeBP、知ってる?追加モードでファイルを開くことは、書き込みモードで開くことと同等であり、最終位置までシークし、それ以降のシークを許可しないと考えました。
JOL

4
@jlmg:追加モードのファイルはシーク可能ですが、すべての書き込みの先頭には暗黙のシークが最後に付きます。その暗黙のシークがアトミックであるかどうかは、私にはあまり明確ではありません。
ケビン

1
それは完全に後続の質問に依存します。これに関連するこのようなものは、答えが不完全であることを示しています。そして、完全な答えを知りたくなくても、人々が後続の質問を調べることはまずありません。したがって、このような質問のフォローには、情報が重複する回答が含まれている可能性が高いため、重複として閉じられます。
trlkly

1
@Kevin、完全にPOSIX準拠のファイルシステムでは、暗黙のO_APPENDシークはアトミックです。ただし、すべてのファイルシステムが関連するセマンティクスを適切に実装しているわけではありません-たとえば、NFS(少なくともv3以前-v4では明確ではありません)には、ワイヤープロトコルに関連するサポートが組み込まれていないため、サーバーはクライアントがでファイルを開いたかどうかを知る方法はありませんO_APPEND
チャールズダフィー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.