ファイル記述子移動の実際の使用

ファイル記述子移動の実際の使用

Bashのマニュアルページによると:

リダイレクト演算子

   [n]<&digit-

ファイル記述子をファイル記述子に移動するdigitか、指定されていない場合はn標準入力(ファイル記述子0)に移動します。コピー後に閉じます。ndigitn

あるファイル記述子を別のファイル記述子に「移動」することはどういう意味ですか?この慣行の一般的な状況は何ですか?

答え1

3>&4-bashもサポートするksh93の拡張です。3>&4 4>&-今、3の略語は4があった位置を指し、4は今閉じているので、4が指すのは3に移動しました。

一般的な使用法は、コピーを作成または保存してstdin復元したい場合です。たとえば、次のようになります。stdout

変数にstdoutだけを残して、コマンドのstderr(およびstderrのみ)をキャプチャするとします。

コマンド置換はvar=$(cmd)パイプラインを生成します。パイプの書き込みの終わりはcmdstdout(ファイル記述子1)になり、もう一方の端はシェルから読み込まれ、変数が入力されます。

stderrここで変数にアクセスするには、次のようにしますvar=$(cmd 2>&1)。 fd 1(stdout)と2(stderr)の両方がパイプに入り(最終的に変数に入る)、これは私たちが望むものの半分です。

これによりvar=$(cmd 2>&1-)(略語でvar=$(cmd 2>&1 >&-cmdstderrだけがパイプに入り、fd1は閉じます。cmd出力を書き込もうとするとエラーが返されます。EBADFファイルを開くと、最初の利用可能なfdが得られ、stdoutコマンドがそれを防止しない限り、開いたファイルがここに割り当てられます。私たちが望むものではありません。

stdoutが独立した状態を維持するようにするにはcmd、つまり、コマンド置換の外部で指す同じリソースを指すようにするには、何らかの方法でそのリソースをコマンド置換内にインポートする必要があります。これを行うには、次のコピーを作成できます。stdout 外部コマンド置換はそれを内部に入れます。

{
  var=$(cmd)
} 3>&1

もっと簡潔に書く方法は次のとおりです。

exec 3>&1
var=$(cmd)
exec 3>&-

(これは最終的にfd 3を閉じるのではなくfd 3を復元する利点もあります)。

{次に(またはexec 3>&1)、}fd 1と3はどちらもfd 1が元のポイントと同じリソースを指します。 fd 3はまた、コマンド置換内の対応するリソースを指します(コマンド置換はfd 1、stdoutのみをリダイレクトします)。したがって、上からcmdfds 1、2、3 を取得します。

  1. varによるパイプ
  2. 影響を受けない
  3. 命令置換以外を指す1と同じ

次のように変更すると:

{
  var=$(cmd 2>&1 >&3)
} 3>&1-

これにより、次のようになります。

  1. 命令置換以外を指す1と同じ
  2. varによるパイプ
  3. 命令置換以外を指す1と同じ

今私たちは欲しいものを手に入れました。 stderrはパイプに入り、stdoutは変更されていません。しかし、我々はfd 3をcmd

コマンドは通常、fd 0から2までがオープンで標準入力、出力、エラーであると仮定しますが、他のfdについては何も仮定しません。彼らはfd 3を変更せずに維持する可能性が高い。他のファイル記述子が必要な場合は、最初open()/dup()/socket()...に使用可能なファイル記述子を返す操作を実行するだけです。 (シェルスクリプトなどexec 3>&1)特別に使用する必要がある場合は、fdまずそれを何かに割り当てます(そしてその過程でfd 3が保持しているリソースはそのプロセスによって解放されます)。

fd 3を閉じることは使用しないので良い習慣ですがcmd、呼び出す前に割り当てられたままにしておくことは大きな問題ではありませんcmd。問題は、プロセスcmd(および作成できる他のプロセス)に利用可能なfdが少ないことです。潜在的に深刻な問題は、fdが指すリソースを最終的にcmdバックグラウンドプロセスによって生成されたプロセスによって保持できるかどうかです。これは、リソースがパイプまたは他のプロセス間通信チャネル(スクリプトが実行時に実行されている場合script_output=$(your-script))の場合に問題を引き起こす可能性があります。これは、もう一方の端から読み取るプロセスがファイルの終わりを見ることができないことを意味します。最後までリソース。バックグラウンドプロセスが終了します。

したがって、ここでは次のように書くことをお勧めします。

{
  var=$(cmd 2>&1 >&3 3>&-)
} 3>&1

そのうち with はbash次のように短縮できます。

{
  var=$(cmd 2>&1 >&3-)
} 3>&1

ほとんど使用されない理由をまとめると、次のようになります。

  1. 非標準で、単にフレーズ砂糖です。これらの一般的な機能に慣れていない人にとって、スクリプトの移植性を低下させ、あまり明確にすることと、いくつかのキーストロークを保存することのバランスを取る必要があります。
  2. コピー後に元のfdを閉じる必要性はほとんど見落とされることがよくあります。ほとんどの場合、結果が発生しないため、または>&3代わりに実行されるからです。>&3->&3 3>&-

ほとんど使用されていないことを証明してください。バッシュから偽。 Bashからcompound-command 3>&4-またはfd 4を離れると、返されたany-builtin 3>&4-後も閉じます。 ㅏcompound-commandany-builtin修理するこの問題は現在(2013-02-19)解決することができます。

答え2

これは、他のファイル記述子が指すのと同じ場所を指すようにすることを意味します。標準エラー記述子(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's Sorceryはこれを使用して、同じコードブロックのさまざまな出力を区別します。

  (
    # 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に印刷してから特別に処理します。

答え3

Unixでは、ファイルはファイル記述子(標準入力の場合は0、標準出力の場合は1、標準エラーの場合は2などの小さな整数、他のファイルを開くときは通常使用されない最小の記述子が割り当てられています)によって処理されます。 。したがって、プログラムの内部構造を知り、ファイル記述子5の出力を標準出力に送信したい場合は、記述子5を1に移動するだけです。これがファイルのソースで2> errorsあり、構造はエラーを2>&1出力ストリームにコピーするのが好きです。

そのため、ほとんど使用していませんが(25年以上Unixをほぼ排他的に使用しながら怒ってから1、2回使用した記憶が曖昧に記憶されますが)、必要なときは必ず必要です。

関連情報