このコードスニペットのソースは次のとおりです。高度なバッシュスクリプトガイド。
# 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
ls
fd 3で何も開く必要はないのでgrep
(そのfdを使用しない)、閉じることをお勧めします(不要なリソースを解放します)。 fd 3を使用してls
stdoutを元の出力に復元します(実行前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番目の呼び出しでは、ls
fd 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