回答:
3>&4-
はbashでもサポートされているksh93拡張であり、これはの短縮形です3>&4 4>&-
。つまり、3は4が使用されていた場所を指し、4は閉じられているため、4が指していたものは3に移動しました。
典型的な使用法は、次のように、複製したstdin
場合やstdout
コピーを保存して復元したい場合です。
変数にstdoutを残したまま、コマンドのstderr(およびstderrのみ)をキャプチャするとします。
コマンド置換var=$(cmd)
、パイプを作成します。パイプの書き込み側はcmd
のstdout(ファイル記述子1)になり、もう一方の側はシェルによって読み取られて変数がいっぱいになります。
ここでstderr
、変数に移動する場合は、次の操作を実行できますvar=$(cmd 2>&1)
。これで、fd 1(stdout)と2(stderr)の両方がパイプ(最終的には変数)に移動します。これは、必要なものの半分にすぎません。
を実行するとvar=$(cmd 2>&1-)
(の略var=$(cmd 2>&1 >&-
)、cmd
パイプのstderr のみがパイプに移動しますが、fd 1は閉じられます。場合cmd
試みはして戻ってくる任意の出力、書き込みにEBADF
エラーが発生し、それがファイルを開いた場合、それは最初のフリーFDを取得し、開いているファイルは、それを割り当てられますstdout
ことに対してコマンドガードしない限り!私たちが望むものでもありません。
stdoutをcmd
そのままにしておきたい場合、つまり、コマンド置換の外側を指しているのと同じリソースを指す場合は、何らかの方法でそのリソースをコマンド置換の内側にする必要があります。そのために、コマンド置換のstdout
外側のコピーを実行して、それを内部に取り込むことができます。
{
var=$(cmd)
} 3>&1
これは、より簡潔な記述方法です。
exec 3>&1
var=$(cmd)
exec 3>&-
(これは、fd 3を最後に閉じるのではなく復元するという利点もあります)。
次に{
(またはexec 3>&1
)からに至る}
まで、fd 1と3は両方とも、最初に指していた同じリソースfd 1を指します。fd 3はコマンド置換内のそのリソースも指します(コマンド置換はfd 1、stdoutのみをリダイレクトします)。上記のように、cmd
fds 1、2、3:
変更する場合:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
その後、次のようになります。
これで、必要なものが得られました。stderrはパイプに移動し、stdoutはそのまま残ります。ただし、fd 3をに漏らしていますcmd
。
コマンドは(慣例により)fds 0から2がオープンで、標準入力、出力、エラーであると想定していますが、他のfdsを想定していません。ほとんどの場合、fd 3はそのままです。別のファイル記述子が必要な場合はopen()/dup()/socket()...
、最初に利用可能なファイル記述子を返すだけです。(そうするシェルスクリプトのようにexec 3>&1
)それらをfd
具体的に使用する必要がある場合、最初にそれを何かに割り当てます(そしてそのプロセスで、fd 3が保持するリソースはそのプロセスによって解放されます)。
fd 3 cmd
は使用しないので、このfd 3を閉じることをお勧めしますが、を呼び出す前に割り当てられたままにしておくことは大したことではありませんcmd
。問題は次のとおりです。それcmd
(および潜在的にそれが生成する他のプロセス)で利用可能なfdが1つ少ない。潜在的にさらに深刻な問題は、そのfdが指すリソースが、それによってcmd
バックグラウンドで生成されたプロセスによって保持される可能性がある場合です。そのリソースがパイプまたは他のプロセス間通信チャネル(スクリプトがとして実行されている場合など)である場合、懸念される可能性がありscript_output=$(your-script)
ます。バックグラウンドプロセスは終了します。
したがって、ここでは、次のように書く方が良いでしょう。
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
これは、次のbash
ように短縮できます。
{
var=$(cmd 2>&1 >&3-)
} 3>&1
まれにしか使用されない理由を要約すると:
>&3
、>&3-
orの代わりに行うだけです>&3 3>&-
。あなたが知っているように、それがめったに使用されないという証拠は、それがbashで偽であることです。bash compound-command 3>&4-
またはleaveでは、any-builtin 3>&4-
fd 4 が戻った後、compound-command
またはany-builtin
戻ってきました。この問題を修正するパッチが入手可能になりました(2013-02-19)。
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
1を閉じる際のタイプミスではありませんか?
$(...)
)。
{...}
、fd 3はfd 1が指していたものを指し、fd 1は閉じます。次に$(...)
、fd 1は、を供給するパイプに設定され$var
、次にcmd
2に対しても、1から3を指すようになりますto、それは外側の1です。1がその後閉じられたままになるという事実はbashのバグです。それを報告します。その機能が由来するksh93には、そのバグはありません。
他のファイル記述子と同じ場所を指すようにすることを意味します。あなたは離れて標準エラー記述子の明白な別々の取り扱いから、非常に稀にこれを実行しないする必要があります(stderr
、fd 2
、/dev/stderr -> /proc/self/fd/2
)。いくつかの複雑な場合に便利です。
高度なBashスクリプトガイドには、この長いログレベルの例と次のスニペットがあります。
# 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.
Source Mageのソーサリーでは、たとえば、同じコードブロックから異なる出力を識別するために使用します。
(
# everything is set, so run the actual build infrastructure
run_build
) 3> >(tee -a $C_LOG >> /dev/stdout) \
2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
> >(tee -a $C_LOG > $VOYEUR_STDOUT)
ロギングの理由で追加のプロセス置換が追加されています(VOYEURはデータを画面に表示するか、単にログに記録するかを決定します)が、一部のメッセージは常に表示する必要があります。それを実現するために、それらをファイル記述子3に出力し、特別に処理します。
Unixでは、ファイルはファイル記述子によって処理されます(たとえば、標準入力が0、標準出力が1、標準エラーが2などの小さな整数です。他のファイルを開くと、通常は最小の未使用記述子が割り当てられます)。あなたがプログラムのinardsを知っている、とあなたが標準出力にファイルディスクリプタ5に行くの出力を送信したいのであれば、あなたはどこだ1に記述5移動したい2> errors
から来ている、などの構造2>&1
への重複エラーにします出力ストリーム。
したがって、ほとんど使用されませんでした(25年以上のほぼ排他的なUnixの使用で怒りで1〜2回使用したことを漠然と覚えています)が、必要な場合は絶対に必要です。
5>&1
1が行く先に5を送信する場合、5を1>&5-
閉じる以外に正確に何をしますか?