あなたはすでにいくつかの非常に良い答えを得ました。ただし、ここには2つの異なる概念が関係していることを強調しておきます。これらの概念の理解は非常に役立ちます。
背景:ファイル記述子とファイルテーブル
ファイル記述子は0 ... nの数字であり、これはプロセスのファイル記述子テーブルのインデックスです。規約により、STDIN = 0、STDOUT = 1、STDERR = 2(STDIN
ここでの用語などは、一部のプログラミング言語およびマニュアルページの規約で使用されている単なるシンボル/マクロであり、STDINと呼ばれる実際の「オブジェクト」はありません。この議論の目的、STDIN は 0など)。
そのファイル記述子テーブル自体には、実際のファイルが何であるかについての情報は一切含まれていません。代わりに、別のファイルテーブルへのポインタが含まれています。後者には、実際の物理ファイル(またはブロックデバイス、パイプ、またはLinuxがファイルメカニズムを介してアドレス指定できるもの)に関する情報と、より多くの情報(つまり、読み取り用か書き込み用か)が含まれます。
したがって、シェルを使用するとき、>
または<
シェルで使用するときは、それぞれのファイル記述子のポインターを別の何かを指すように置き換えるだけです。構文は2>&1
、記述子2を1が指す場所を指すだけです。> file.txt
単にfile.txt
書き込み用に開き、STDOUT(ファイルdecsriptor 1)がそれを指すようにします。
その他の利点もあります2>(xxx)
(例:実行xxx
中の新しいプロセスの作成、パイプの作成、新しいプロセスのファイル記述子0をパイプの読み取り側に接続し、元のプロセスのファイル記述子2をパイプの書き込み側に接続しますパイプ)。
これは、シェル以外のソフトウェアの「ファイルハンドルマジック」の基礎でもあります。たとえば、Perlスクリプトでdup
、STDOUTファイル記述子を別の(一時的な)記述子に連結してから、新しく作成された一時ファイルに対してSTDOUTを再度開くことができます。この時点から、独自のPerlスクリプトからのすべてのSTDOUT出力およびsystem()
そのスクリプトのすべての呼び出しは、その一時ファイルになります。完了したらdup
、STDOUTを保存した一時記述子に再保存できます。これで、前と同じようになります。その間に一時記述子に書き込むこともできるため、実際のSTDOUT出力は一時ファイルに出力されますが、実際の出力は実際の STDOUT(通常はユーザー)に出力できます。
回答
上記の背景情報を質問に適用するには:
シェルはコマンドとストリームリダイレクトをどの順序で実行しますか?
左から右へ。
<command> > file.txt 2>&1
fork
新しいプロセスから。
file.txt
ポインターを開いて、ファイル記述子1(STDOUT)に保存します。
- STDERR(ファイル記述子2)に、現在fd 1が指しているもの(これも
file.txt
もちろん既に開かれている)をポイントします。
exec
その <command>
これにより、明らかにstderrが最初にstdoutにリダイレクトされ、次に、結果のstdoutがfile.txtにリダイレクトされます。
これは、テーブルが1つしかない場合に意味がありますが、上で説明したように2つあります。ファイル記述子は相互に再帰的にポイントしていないため、「STDERRをSTDOUTにリダイレクトする」と考えるのは意味がありません。正しい考え方は、「STDERRがSTDOUTの指す場所を指す」ことです。後でSTDOUTを変更した場合、STDERRはそのままであり、STDOUTにさらに変更が加えられても魔法のようには進みません。