読者のために、このレシピはこちら
- stderrを変数にキャッチするワンライナーとして再利用できます
- それでもコマンドの戻りコードにアクセスできます
- 一時ファイル記述子3を犠牲にします(もちろん、変更可能です)
- そして、この一時ファイル記述子を内部コマンドに公開しません
あなたstderrが何かを捕まえたいならcommand、varあなたはそうすることができます
{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
その後、あなたはそれをすべて持っています:
echo "command gives $? and stderr '$var'";
commandが(のようなものではなくa | b)単純な場合は、内部を{}省略できます。
{ var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;
簡単に再利用可能なbash-functionにラップされます(おそらくバージョン3以降が必要ですlocal -n):
: catch-stderr var cmd [args..]
catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }
説明:
local -nエイリアス "$ 1"(の変数ですcatch-stderr)
3>&1 ファイル記述子3を使用してstdoutポイントを保存します
{ command; } (または "$ @")次に、出力キャプチャ内でコマンドを実行します $(..)
- ここでは正確な順序が重要であることに注意してください(間違った方法でファイル記述子を誤ってシャッフルします)。
2>&1stderr出力キャプチャにリダイレクトします$(..)
1>&3stdout出力キャプチャから離れて、ファイル記述子3に保存され$(..)た「外部」にリダイレクトstdoutします。なおstderr、FD 1が以前にポイントした場所を参照していることに注意してください。$(..)
3>&-次に、不要になったファイル記述子3を閉じます。これにより、command不明なオープンファイル記述子が突然表示されることがなくなります。外側のシェルはまだFD 3が開いているが、command表示されないことに注意してください。
- 後者のプログラムは重要
lvmです。予期しないファイル記述子について不満を言うようなプログラムもあります。そしてlvm文句を言うstderr-私たちがキャプチャーしようとしているものだけ!
それに応じて調整すれば、このレシピで他のファイル記述子をキャッチできます。もちろんファイル記述子1を除きます(ここではリダイレクトロジックは間違っていますが、ファイル記述子1の場合はvar=$(command)通常どおりに使用できます)。
これはファイル記述子3を犠牲にすることに注意してください。そのファイル記述子が必要になった場合は、自由に数値を変更してください。ただし、一部のシェル(1980年代のもの)は99>&1、9その後に続く引数として理解する場合があることに注意してください9>&1(これはには問題ありませんbash)。
また、このFD 3を変数で構成可能にするのは簡単ではないことにも注意してください。これは物事を非常に読みにくくします:
: catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
catch-var-from-fd-by-fd()
{
local -n v="$1";
local fd1="$2" fd2="$3";
shift 3 || return;
eval exec "$fd2>&1";
v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
eval exec "$fd2>&-";
}
セキュリティ上の注意:への最初の3つの引数catch-var-from-fd-by-fdは、サードパーティから取得してはなりません。常に「静的」な方法で明示的に指定してください。
だからノーノーノーcatch-var-from-fd-by-fd $var $fda $fdb $command、決してこれをしないでください!
たまたま変数変数名を渡した場合は、少なくとも次のようにしてください。
local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command
これは依然としてすべてのエクスプロイトからユーザーを保護するわけではありませんが、少なくとも一般的なスクリプトエラーを検出して回避するのに役立ちます。
ノート:
catch-var-from-fd-by-fd var 2 3 cmd.. と同じです catch-stderr var cmd..
shift || return正しい数の引数を与えるのを忘れた場合の醜いエラーを防ぐための単なる方法です。おそらく、シェルを終了することは別の方法です(ただし、これによりコマンドラインからのテストが困難になります)。
- ルーチンは、理解しやすいように書かれています。関数を必要としないように書き換えることができますが
exec、その場合は本当に醜くなります。
- このルーチン
bashは、を必要としないように、同様に書き換えることができますlocal -n。ただし、ローカル変数は使用できず、非常に醜くなります。
- また、
evalsは安全に使用されていることに注意してください。通常evalは危険と見なされます。ただし、この場合は"$@"(任意のコマンドを実行するために)を使用するのと同じくらい悪です。ただし、ここに示すように、正確で正しい引用を使用してください(そうしないと、非常に危険になります)。
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)