これはBashマニュアルのリダイレクトセクションのタイプミスですか?


13
Note that the order of redirections is significant.  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 duplicated from the standard output before the standard
   output was redirected to dirlist.

さて、最後の部分は私を混乱させます。その場合、標準エラーは端末に出力され、STDOUTはdirlistファイルに出力されます。それが起こることですが、それは私がマニュアルを理解する方法ではありません。

「標準出力がdirlistにリダイレクトされた後、標準エラーが標準出力から複製されたため」と言う必要があるようです。STDOUTがファイルに送信される前にSTDERRがSTDOUTに送信された場合、そのファイルにはSTDOUT AND STDERRが含まれていませんか?

誰かが私のためにこれをクリアしてくれますか?それは私の側の読解力が悪いだけですか?この文脈では、重複という言葉の使用は少し奇妙に思えます。多分それは私を投げている。



1
「値による」対「参照による」操作を混在させる古典的なケース。ファイル記述子を複製する場合、値による操作です。プログラミングにおいて、a = 1; b = a; a = 2あなたa == 2 && b == 1が真実であることを期待した後。リダイレクト2>&1b = a割り当てに似ています-参照ではなく値によるものです。 2>&1永遠にファイル記述子2をファイル記述子1に結合しません-それらはまだ2つの別個のファイル記述子であり、それらはたまたま同じファイルを指し示しています。
jw013

回答:


23

複製ここでは、が本当に重要な部分です。

リダイレクトの前にファイル記述子がどこに行くのか見てみましょう。これは通常、現在の端末です。例:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

ここで、ls -lリダイレクトなしで呼び出すと、出力およびエラーメッセージがの下の端末に送られ/dev/pts/1ます。

最初にSTDOUTファイルにリダイレクトする場合(ls -l > dirlist)、次のようになります。

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

私たちはときに、その後リダイレクトSTDERR重複STDOUTのファイルディスクリプタ(ls -l > dirlist 2>&1)、STDERRの複製に行きます/home/bon/dirlist

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

最初にのファイル記述子()のSTDERR複製にリダイレクトする場合:STDOUTls -l 2>&1

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

そしてその後、 STDOUTファイル(にls -l 2>&1 > dirlist)、我々はこれを得るでしょう:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

ここに、 STDERRは、まだターミナルに行きます。

ご覧のとおり、manページの順序は正しいです。


リダイレクトのテスト

これで、自分でテストできます。を使用すると、現在のプロセスの(fd 1で)および(fd 2で)ls -l /proc/$$/fd/どこに行くのかがわかります。STDOUTSTDERR

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

ファイル記述子が指している場所を示す小さなシェルスクリプトを作成しましょう。このようにしてls、呼び出しシェルからのリダイレクトを含め、を呼び出すときに常に状態を取得します。

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(を使用するとCtrlD、ファイルの終わりを送信するため、catからのコマンド読み取りを停止しSTDINます。)

次に、さまざまなリダイレクトの組み合わせでこのスクリプトを呼び出します。

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

ファイル記述子1(for STDOUT)および2(for STDERR)が異なることがわかります。楽しみのために、リダイレクトSTDINして結果を確認することもできます。

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(質問は読者に任されました:ファイル記述子255はどこを指しますか?;-))


+1-優れた答え。非常によく書かれた素晴らしい例。ありがとうございました!!!
slm

私の誤解は、リダイレクトが後続のすべてのコマンドに対して永続的であり、残りの行のSTDERRがSTDOUTに送られるということであったと思います。
グレッグレベンタール

2

いいえ、マニュアルは正しいです。

最初の1が端末を指し、2も端末を指す場合、次のようになります。

command  2>&1   1>somewhere

リダイレクトの評価は、左から右に行われます。

したがって、最初に評価され2>&1、最初にfd 1が指すもの(つまりthe terminal、通常は/ dev / tty のファイル記述子)をfdにコピーします2

その2ため、fdは、fd 1が(the terminal)を指していた場所を指すようになりました

そして、それは評価1>somewhereの部分を、ひいてはのファイルディスクリプタをコピーするsomewhereFDに1(そう、その時点で、fdは1ここのポイントをsomewhere、とFD 2にはまだポイントをthe terminal

1が変更される前に2が1から複製されたため、実際には1を「どこかに」、2を端末に出力します。

他の順序:

command  1>somewhere 2>&1

最初にfd 1somewhereにリダイレクトし、次に同じ参照をfd 2にコピーするため、最後に2もを指しsomewhereます。ただし、今後は「リンク」されません。それぞれを個別にリダイレクトできます。

例:

command  1>somewhere 2>&1
exec 2>/dev/null

その最後で、fdはを1指しsomewhere、fd 2/dev/null

fdの通常の名前1はSTDOUT(標準出力)で、fdの通常の名前2はSTDERR(標準エラー。STDOUTに干渉することなくエラーを表示するために一般的に使用されるため)


@ Michael-mrozek:編集に感謝しますが、私は「複製」ではなく「複製」と言うことを主張します。「複製」は、これからは両方が「同じもの」であると信じることができるからです。例:: cmd 1>somewhere 2>&1 ; exec 2>/dev/nullexecの後、2つだけが/ dev / nullにリダイレクトされます(1はまだ「どこか」に移動します)。私の代わりしかし...も...混乱しているように、「1 fdは」の「どのような1ポイントに」と言う方法を考え出す必要性の助けを行う
オリヴィエ・デュラック

1
どういう意味か分かりません。「コピー」から「複製」に変更したのはあなたです。やったすべての私は、私は言葉に変更していない、物事を活用し、フォーマットした
マイケルMrozek

ああ... ^^ごめんなさい。そして、何にコピーされるのかをより正確にするために再編集するために再度編集しました^^
オリビエデュラック

1

ここで紛らわしい部分は、stderrをstdoutにリダイレクトすると実際に2つのストリームが接続されるという誤解だと思います。

完全に合理的なアイデアですが、書き込み時に何が起こるか2>&1は、stderrがstdoutが同じ場所に書き込みを行い、同じ場所自体に書き込みを行うときにピーカブーを取得します。したがって、その後stdoutに他の場所に書き込むように指示しても、既に移動されたstderrの宛先には影響しません。

私はそれが少し直感に反していると思うが、それはそれがどのように機能するかです。最初に書き込み先を設定してから、全員に「コピーしてください」と伝えます。明確になることを願っています...


0

複製...

は重要ですが、むしろそれが多くの混乱の原因であるという意味で。本当に簡単です。この答えは単なる「ラジカル」な説明です。

受け入れられた答えは良いですが、長すぎて、「複製」を強調しています。

Qは次のように賢く終わります:

この文脈では、重複という言葉使用は少し奇妙に思えます。多分それは私を投げている。

bash表記を使用し、変数「1」と「two」をファイルハンドル「1」と「2」として定義します。(出力)リダイレクト演算子>は割り当て=です。&$「値」を意味します。

man bashの例(デフォルトでは「1」が追加されています)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

になる:

one=dirlist  two=$one

そして

two=$one   one=dirlist

そして、これでさえ私には自動ではなく、私が推測する他の人もいます。最初の行では$one$two「dirlist」が含まれています。もちろん。

2行目は、役に立たない割り当てで始まります。両方とも、定義として「TTY」(ビット記号)を方向として開始します。この割り当てによって値が変更されることはありません。また、ファイルハンドルのように変数を使用しても、魔法のようにリンクされるものはありません。変数twoは、次の影響を受けませんone=dirlist。もちろん違います。

ここのソムボディ(6年前)は、「コピー」または「複製」の代わりに「ポイント」を提案し、それから理解しました:それも混乱させるでしょう。

この複製またはポインターのセマンティクスは必要ありません。たぶん、もっと注意が必要なのはアンパサンドです。「値」演算子/トークン/何でも。

コンソールで驚くべきジョブ番号を取得する方法を探している場合にのみ- 「完了」メッセージに加えてボーナスとして「2」という名前のファイルを取得する場合、次のようになります。

ls 1>2& 2>/dev/null

それは自然に「コピー」/「重複」1〜2として読み取られ、両方が一緒にnullになります。しかし、考え方も間違っていますし、構文も間違っています。(ただし、構文エラーはありません。有効です)

それを計画する正しい方法は、2つのうちのいずれかをnullにリダイレクトしてから、OTHERを同じ場所にリダイレクトすることです。

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(先頭の「1」は残すことができます)

(OK、acc。Aは長すぎませんが、リストが多すぎます-または:非常に優れた視覚化、あまり良い説明ではありません)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.