- - PR -
ディスクリプタについて
1
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2004-10-03 23:13
こんにちは。
今回は bash について質問があります。 bash には標準出力やエラー出力が用意されていますが、 それをリダイレクトする際には何か制限などありますでしょうか? というのは、今回初めてリダイレクトについて 調べてみたのですが、イメージと違う結果になってしまう場合があります。 例えば、標準出力とエラー出力が出るスクリプトを作成し それぞれを同じファイルに出力してみます。 test.sh > log.txt 2>&1 これは意味がわかります。標準出力がファイルに、 エラー出力は標準出力と合わせてファイルに書き出されます。 test.sh 2> log.txt > log.txt これはエラー出力のみファイルに書き出されます。 先にエラー出力の方をファイルに切り替えて、 標準出力は上書きするのではなく、既にファイルオープンされてて、 書き込みができなかったのでしょうか? test.sh > log.txt 2> log.txt また、こうしても上記と結果が同じになります。なぜ? エラー出力の方が優先されるのでしょうか? この辺が疑問でハマりまくっています。 どなたか、この疑問を解消してくれる方はおりませんでしょうか? ご教授よろしくお願いします。 | ||||
|
投稿日時: 2004-10-03 23:23
追伸です。
test.sh 2> log.txt >& log.txt この場合は、標準出力もエラー出力もファイルに書かれます。 man bash したところ、>& は > log.txt 2>&1 と同等みたいですね。 この場合は、2> log.txt でエラー出力先がファイルになり、 >& log.txt で標準出力先がファイルになり、エラー出力も 標準出力と同じ場所に書き出されます。 これは、イメージ通りの結果となります。 しかし、下記のように順番を逆にすると結果が変わります。 test.sh >& log.txt 2> log.txt この場合はエラー出力のみがファイルに書き出されます。 >& log.txt は > log.txt 2>&1 と同等で、 標準出力はファイルに書き出されると思うのですが、 なぜエラー出力しか書き出されないのでしょうか。 下記のように変えても同じ結果です。う〜ん・・・。 test.sh >& log.txt > log.txt 先ほどの質問でも気になってるのですが、 標準出力とエラー出力に何か優先度の関係とかあるのでしょうか? | ||||
|
投稿日時: 2004-10-04 02:26
私もよくわかっていないので、うまく説明できないのですが、
man bash すると、次のように書かれています。
まあ、それはさておき。 リダイレクトを実現するにはシステムコールの open/dup2、ライブラリの open64/dup2 を使っています。 ですので、次のようにして strace や ltrace を使ってログを取って 調べると理解しやすいかも。 $ strace -o log1 bash -c 'echo hello >a 2>&1' $ strace -o log2 bash -c 'echo hello 2>&1 >a' $ ltrace -o log3 bash -c 'echo hello >a 2>&1' $ ltrace -o log4 bash -c 'echo hello 2>&1 >a' strace では open/dup2/write, ltraceではopen64/dup2/printf に注目。 今回の場合はstraceの方がわかりやすいかも。 | ||||
|
投稿日時: 2004-10-04 10:35
test.sh > log.txt 2> log.txt
また、こうしても上記と結果が同じになります。なぜ? エラー出力の方が優先されるのでしょうか? &無しでは、後書きした方が優先されているのでは。 -------------------- r.pl print STDERR "Oshikiri"; print "Moe"; の順を代えると,そうです [ メッセージ編集済み 編集者: MMX 編集日時 2004-10-04 10:37 ] | ||||
|
投稿日時: 2007-02-19 17:48
test.sh 2> log.txt > log.txt
これですが、エラー出力と標準出力を 同じファイルにしてしまっているため、 エラー出力を標準出力で上書きしていると思います。 両方出したいのであれば >> 追加リダイレクトして あげなければいけません。 test.sh >> log.txt 2>&1 また、下記のように記述すると、エラーはlog_error.txtに、 標準出力はlog.txt に書き込まれます test.sh 2> log_error.txt > log.txt [ メッセージ編集済み 編集者: MST 編集日時 2007-02-19 18:14 ] | ||||
|
投稿日時: 2007-02-22 11:17
おそらくですが、「ディスクリプタの決定の順番」と「出力処理の順番」を分けていないことが誤解の原因かと思います。
シェルに与えるリダイレクト記号は、「ディスクリプタを決定」するだけで、出力は後からまとめて行われます。この処理は、シェルに与えられたものが左から処理されていきます。 ディスクリプタの決定後、出力が行われますが、こちらは一般的なOSではディスクリプタのナンバ順、すなわち、 1. 標準入力(0) 2. 標準出力(1) 3. 標準エラー出力(2) の順に処理されていきます。 なので、まず、 ・シェルに与えられたリダイレクト記号を元に、ディスクリプタを決定する。これは左から順番に決定される。 ・決定されたディスクリプタに順番に出力する。これは標準入力→標準出力→エラー出力の順で固定。 という動作が行われている、ということを前提に考えていくときれいに解釈できるようになると思います。 ……というので普通は済む話なのですが、ちょっとだけシェルによる (たぶんbashをお使いかなと) の例外もありますが、大抵はこれで解釈できると思います。この例外については最後に書きました。 それぞれの例を順番に見てみましょう。 > test.sh > log.txt 2>&1 > これは意味がわかります。標準出力がファイルに、 > エラー出力は標準出力と合わせてファイルに書き出されます。 「ディスクリプタの決定」 1) test.shのディスクリプタ(1:標準出力)は log.txt になります。 2) test.shのディスクリプタ(2:エラー出力)を、ディスクリプタ(1)に変更します。 「出力処理」 3) ディスクリプタ(1:標準出力+エラー出力)を処理します。log.txtに書き込みます。 4) ディスクリプタ(2)は存在しません。 > test.sh 2> log.txt > log.txt > これはエラー出力のみファイルに書き出されます。 > 先にエラー出力の方をファイルに切り替えて、 > 標準出力は上書きするのではなく、既にファイルオープンされてて、 > 書き込みができなかったのでしょうか? 「ディスクリプタの決定」 1) test.shのディスクリプタ(2:エラー出力)を、log.txt にします。 2) test.shのディスクリプタ(1:標準出力)を、log.txt にします。 「出力処理」 3) ディスクリプタ(1:標準出力)を処理します。log.txtに書き込みます。 4) ディスクリプタ(2:エラー出力)を処理します。log.txtに書き込みます。リダイレクト指定は > でしたから、以前のlog.txtは破棄されます。 > test.sh > log.txt 2> log.txt > また、こうしても上記と結果が同じになります。なぜ? > エラー出力の方が優先されるのでしょうか? 「ディスクリプタの決定」 1) test.shのディスクリプタ(1:標準出力)を、log.txt にします。 2) test.shのディスクリプタ(2:エラー出力)を、log.txt にします。 「出力処理」 3) ディスクリプタ(1:標準出力)を処理します。log.txtに書き込みます。 4) ディスクリプタ(2:エラー出力)を処理します。log.txtに書き込みます。リダイレクト指定は > でしたから、以前のlog.txtは破棄されます。 > しかし、下記のように順番を逆にすると結果が変わります。 > test.sh >& log.txt 2> log.txt > この場合はエラー出力のみがファイルに書き出されます。 まずこのままでは分かりにくいので、">& log.txt" を展開します。 > test.sh > log.txt 2>&1 2> log.txt 「ディスクリプタの決定」 1) test.shのディスクリプタ(1:標準出力)を、log.txt にします。 2) test.shのディスクリプタ(2:エラー出力)を、ディスクリプタ(1)にします。 3) test.shのディスクリプタ(2:エラー出力)を、log.txt にします。2)で行った変更は上書きされ、無視されます。 「出力処理」 3) ディスクリプタ(1:標準出力)を処理します。log.txtに書き込みます。 4) ディスクリプタ(2:エラー出力)を処理します。log.txtに書き込みます。リダイレクト指定は > でしたから、以前のlog.txtは破棄されます。 > 下記のように変えても同じ結果です。う〜ん・・・。 > test.sh >& log.txt > log.txt で、これが問題の bash による問題です。zshだとこれは標準出力&エラー出力が記録されるのですが、bashだと謎なことに、エラー出力だけ、ということになると思います。 なんでやねーん、と思うかもしれませんが、こういうことです。 これも分かりにくいので、">& log.txt" を展開します。 > test.sh > log.txt 2>&1 > log.txt 「ディスクリプタの決定」 1) test.shのディスクリプタ(1:標準出力)を、log.txt にします。 2) test.shのディスクリプタ(2:エラー出力)を、ディスクリプタ(1)にします。 3) test.shのディスクリプタ(1:標準出力)を、log.txt にします。この時点ではエラー出力はディスクリプタ1として扱われていません。 結果として、ディスクリプタは以下のようになります。 ディスクリプタ1(標準出力)は、log.txtに記録されます。 ディスクリプタ2(エラー出力)は、log.txtに記録されます。これはディスクリプタ1と合わせる配慮をしません(なぜなら、3)で >& した状態からさらに書き換わったから)。 「出力処理」 3) ディスクリプタ(1:標準出力)を処理します。log.txtに書き込みます。 4) ディスクリプタ(2:エラー出力)を処理します。log.txtに書き込みます。以前のlog.txtは破棄されます。 と、いうような話です。 | ||||
|
投稿日時: 2007-02-22 11:58
こんにちは。
とりあえず、大幅にダウト。 詳細はまた後ほど。 [ メッセージ編集済み 編集者: angel 編集日時 2007-02-22 11:58 ] | ||||
|
投稿日時: 2007-02-22 13:10
詳細です。
まず、ファイルディスクリプタについて。 bash は標準入力・標準出力・標準エラーの区別を特にはつけていません。bash自身が行う処理を除いて。 勿論、それぞれ 0,1,2 がそうだという慣用を考慮はしていますが。 せいぜい、
の特例があるくらいでしょう。 次に、入力・出力の処理について。 bash は入力・出力には関与しません。bashが独自に行う処理を除いて。 あくまで入力・出力を行うのは、bash より起動されたプログラムの仕事であり、bash はプログラム起動に先駆けて、ディスクリプタをセットアップするだけです。 セットアップするのは bash の仕事であるため、起動されるプログラムは、ファイルディスクリプタがどんなファイルに紐付いているかは関与しません。( tty であるかどうか等、種類を判定することはあるでしょうが ) まとめると、
というのが基本の動作です。 ※ bashビルトインコマンドの場合や、パイプ、関数、サブシェル等々色々細かい話もあるでしょうが、今回は省略します。 最後にリダイレクト指定の違いについて。 &を使う場合は、書いてある順番が重要です。そうでない場合は順番にあまり意味はありません。 問題になりそうなパターンとして、
が考えられます。
[ メッセージ編集済み 編集者: angel 編集日時 2007-02-22 14:20 ] |
1