stderrのみパイプにリダイレクト

stderrのみパイプにリダイレクト

このコードスニペットのソースは次のとおりです。高度なバッシュスクリプトガイド

# Redirecting only stderr to a pipe.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Thanks, S.C.

コメントでは、コードが「grep」に対してfd 3のみをオフにすることを説明します。しかし、fd 3を2回閉じます。なぜ私たちはこれをすべきですか? 「grep」に対してfd 3を一度だけ閉じるのは間違っていますか?

ls -l 2>&1 >&3 | grep bad 3>&-    

答え1

lsfd 3で何も開く必要はないのでgrep(そのfdを使用しない)、閉じることをお勧めします(不要なリソースを解放します)。 fd 3を使用してlsstdoutを元の出力に復元します(実行前ls)。

パイプラインでは、すべてのコマンドが独自のプロセスで同時に実行されることに注意してください。リダイレクトはそれぞれに個別に適用されるため、1つのfd 3を閉じると、もう1つのfd 3を閉じることはできません。

この場合、閉じないとファイルディスクリプタの数の制限に早く到達できることを除いて、閉じたり閉じたりしなくても実際に大きな違いはありません。

他の場合(たとえば、コマンド自体が別のプロセスを開始した場合など)、これはリソースを束ねて問題を引き起こす可能性があります。たとえば、stdoutがパイプの場合、バックグラウンドプロセスは最終的にfdを継承し、プロセスが返されるまでパイプリーダーでEOFを見ることができなくなります。

より良い書き方は次のとおりです。

{ ls -l 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1

このように、fd 3はそのコマンドグループ期間中に一時的にのみリダイレクトされます(そして後で復元または閉じます)。

違いを確認してください:

$ { ls -l /proc/self/fd 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575886]
lr-x------ 1 stephane stephane 64 Apr  2 09:29 3 -> /proc/20918/fd/
$ { ls -l /proc/self/fd 2>&1 >&3  | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575900]
lrwx------ 1 stephane stephane 64 Apr  2 09:29 3 -> /dev/pts/4
lr-x------ 1 stephane stephane 64 Apr  2 09:29 4 -> /proc/20926/fd/

2番目の呼び出しでは、lsfd 3も何らかの理由で端末に開きます(パイプを実行するとstdoutで開きます)。

ksh93では、を使用すると、execそのfdを閉じる必要はありません。 0、1、2以外のfdは、命令実行時に自動的に閉じられるからです。

$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 3 -> /proc/21105/fd
$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/ 3>&3'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 3 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 4 -> /proc/21108/fd

なぜそのような言葉が出てくるのかわかりません。(ただし「ls」ではない)上記はバグのようです(おそらく私;-)。

答え2

fd3スクリプトから2つのサブシェルを分岐したので、2回閉じます。それぞれは親ファイル記述子を継承してコピーします。各サブシェル内の終了は、fd3他のサブシェル(親シェルを含む)には影響しません。

したがって、コメント行は非常に不明瞭であり、誤解を招くのは簡単です。

stderパイプにリダイレクトする場合は、次のものを使用できますprocess substitution

ls -l 2> >(grep bad)

または交換stderrしてくださいstdout

ls -l 3>&1 1>&2 2>&3 | grep bad

答え3

「高度なBashスクリプトガイド」のコードを理解するのではなく、質問の要約のためにここに来た他の人にとっては、他の答えを推論するのは難しいことがわかりました。次の方法は私の目的に適しており、その意図を読んで理解する方が簡単です。

$ ls -l 2>&1 1>&- | grep bad

私にとって、これはstdoutそれを閉じて無視し、次のコマンドにパイプできるようにリダイレクトしstderrたいことを意味します。stdout

関連情報