今日、stdout
とstderr
の両方をファイルにリダイレクトしようとしていましたが、これに遭遇しました。
<command> > file.txt 2>&1
これは明らかにstderr
を最初にstdout
にリダイレクトし、次に結果のstdout
がfile.txt
にリダイレクトされます。
しかし、なぜ<command> 2>&1 > file.txt
の順序ではないのですか?当然、これは最初に実行されるコマンド(左から右への実行を想定)、stderr
がstdout
にリダイレクトされ、次にstdout
がfile.txt
に書き込まれると読むでしょう。ただし、上記はstderr
のみを画面にリダイレクトします。
シェルは両方のコマンドをどのように解釈しますか?
<command> 2>&1 > file.txt
を実行すると、stderrは2>&1
によってstdoutの現在の場所であるターミナルにリダイレクトされます。その後、stdoutは>
によってファイルにリダイレクトされますが、stderrはリダイレクトされないため、端末出力のままになります。
<command> > file.txt 2>&1
を使用すると、stdoutは最初に>
によってファイルにリダイレクトされ、次に2>&1
がstderrをstdoutが行く場所(ファイル)にリダイレクトします。
そもそも直観に反するように思えるかもしれませんが、この方法でリダイレクトを考え、左から右に処理されることを覚えておくと、はるかに意味があります。
あなたがそれを追跡するなら、それは理にかなっているかもしれません。
最初は、stderrとstdoutは同じもの(通常、ここではpts
と呼ぶ端末)に移動します。
fd/0 -> pts
fd/1 -> pts
fd/2 -> pts
ここでは、stdin、stdout、およびstderrをファイル記述子番号で参照しています。これらはそれぞれファイル記述子0、1、および2です。
さて、最初のリダイレクトのセットには、> file.txt
と2>&1
があります。
そう:
> file.txt
:fd/1
はfile.txt
に移動します。 >
では、1
は何も指定されていない場合の暗黙的なファイル記述子であるため、これは1>file.txt
です。
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> pts
2>&1
:fd/2
はfd/1
現在がどこにでも行くようになりました:
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> file.txt
一方、2>&1 > file.txt
を使用すると、順序が逆になります。
2>&1
:fd/2
はfd/1
が現在行っている場所に移動するため、何も変更されません。
fd/0 -> pts
fd/1 -> pts
fd/2 -> pts
> file.txt
:fd/1
がfile.txt
に移動しました:
fd/0 -> pts
fd/1 -> file.txt
fd/2 -> pts
重要な点は、リダイレクトは、リダイレクトされたファイル記述子がターゲットファイル記述子に対する将来のすべての変更に従うことを意味しないことです。 current状態のみを取ります。
シェルが左側のリダイレクトを最初に設定し、次のリダイレクトを設定する前にcompleteを設定すると考えると役立つと思います。
The Linux Command Line by William Shotts
最初に標準出力をファイルにリダイレクトし、次にファイル記述子2(標準エラー)をファイル記述子1(標準出力)にリダイレクトします
これは理にかなっていますが、その後
リダイレクトの順序が重要であることに注意してください。標準エラーのリダイレクトは、標準出力のリダイレクト後に必ず発生する必要があります。そうしないと機能しません。
しかし、実際には、stderrを同じ効果でファイルにリダイレクトした後、stdoutをstderrにリダイレクトできます。
$ uname -r 2>/dev/null 1>&2
$
そのため、command > file 2>&1
では、シェルはstdoutをファイルに送信し、stderrをstdout(ファイルに送信中)に送信します。一方、command 2>&1 > file
では、シェルはまずstderrをstdoutにリダイレクトし(つまり、stdoutが通常行く端末に表示します)、その後、stdoutをファイルにリダイレクトします。 TLCLは、stdoutを最初にリダイレクトする必要があると言って誤解を招きます。stderrを最初にファイルにリダイレクトしてからstdoutを送信できるためです。できないことは、stdoutをstderrにリダイレクトすること、またはその逆beforeにリダイレクトすることです。もう一つの例
$ strace uname -r 1>&2 2> /dev/null
4.8.0-30-generic
これはstdoutをstderrと同じ場所に廃棄すると思うかもしれませんが、そうではありません。stdoutを最初にstderr(画面)にリダイレクトし、次にstderrのみをリダイレクトします。
これが少し光をもたらすことを願っています...
あなたはすでにいくつかの非常に良い答えを得ました。ただし、ここには2つの異なる概念が関係していることを強調しておきます。これらの概念の理解は非常に役立ちます。
ファイル記述子は、0 ... nの数字で、プロセスのファイル記述子テーブルのインデックスです。慣例により、STDIN = 0、STDOUT = 1、STDERR = 2(ここでSTDIN
などの用語は、一部のプログラミング言語およびマニュアルページで慣例により使用される単なるシンボル/マクロであり、STDINと呼ばれる実際の「オブジェクト」はありません。この議論のために、STDIN is 0など)。
そのファイル記述子テーブル自体には、実際のファイルが何であるかについての情報は一切含まれていません。代わりに、別のファイルテーブルへのポインタが含まれています。後者には、実際の物理ファイル(またはブロックデバイス、パイプ、またはLinuxがファイルメカニズムを介してアドレス指定できるもの)に関する情報と詳細情報(つまり、読み取り用か書き込み用か)が含まれます。
したがって、シェルで>
または<
を使用する場合は、それぞれのファイル記述子のポインターを単に置き換えて、他の何かを指すようにします。構文2>&1
は、記述子2を1が指すところを指すだけです。 > file.txt
は、単に書き込み用にfile.txt
を開き、STDOUT(ファイルdecsriptor 1)がそれを指すようにします。
他にも便利なものがあります。 2>(xxx)
(つまり、xxx
を実行する新しいプロセスを作成し、パイプを作成し、新しいプロセスのファイル記述子0をパイプの読み取り側に接続し、元のプロセスのファイル記述子2をパイプ)。
これは、シェル以外のソフトウェアの「ファイルハンドルマジック」の基礎でもあります。たとえば、Perlスクリプトで、dup
licatedを使用してSTDOUTファイル記述子を別の(一時的な)記述子に追加し、STDOUTを新しく作成された一時ファイルに対して再度開きます。この時点から、独自のPerlスクリプトおよびすべてのsystem()
呼び出しからのすべてのSTDOUT出力は、その一時ファイルになります。完了したら、STDOUTを保存した一時記述子にre -dup
できます。これで、すべては以前と同じです。その間に一時記述子に書き込むこともできるため、実際のSTDOUT出力は一時ファイルに出力されますが、実際にはreal STDOUT(通常はユーザー)に出力できます。
上記の背景情報を質問に適用するには:
シェルはコマンドとストリームリダイレクトをどの順序で実行しますか?
左から右へ。
<command> > file.txt 2>&1
fork
は新しいプロセスをオフにします。file.txt
を開き、そのポインターをファイル記述子1(STDOUT)に格納します。file.txt
です)。exec
the <command>
これにより、明らかにstderrが最初にstdoutにリダイレクトされ、次に、結果のstdoutがfile.txtにリダイレクトされます。
oneテーブルしかない場合、これは理にかなっていますが、上で説明したように2つあります。ファイル記述子は相互に再帰的にポイントしていないため、「STDERRをSTDOUTにリダイレクトする」と考えるのは意味がありません。正しい考え方は、「STDERRがSTDOUTの指す場所を指す」ことです。後でSTDOUTを変更した場合、STDERRはそのままであり、STDOUTにさらに変更が加えられても魔法のようには進みません。
常に左から右に...
Mathと同様に、乗算と除算が加算と減算の前に行われることを除いて左から右に行われます。ただし、括弧(+-)内の演算は乗算と除算の前に行われます。
ここのBash初心者ガイド( Bash初心者ガイド )にあるように、最初に来るもの(左から右の前)の階層には8つの順序があります。
だから、それは常に左から右です...時を除いて...
順序は左から右です。 Bashのマニュアルは、あなたが尋ねる内容をすでにカバーしています。マニュアルのREDIRECTION
セクションから引用:
Redirections are processed in the
order they appear, from left to right.
そして数行後:
Note that the order of redirections is signifi‐
cant. For example, the command
ls > dirlist 2>&1
directs both standard output and standard error
to the file dirlist, while the command
ls 2>&1 > dirlist
directs only the standard output to file
dirlist, because the standard error was dupli‐
cated from the standard output before the stan‐
dard output was redirected to dirlist.
コマンドが実行される前にリダイレクトが最初に解決されることに注意することが重要です! https://askubuntu.com/a/728386/295286 を参照してください