func() {
echo 'hello'
echo 'This is an error' >&2
}
a=$(func)
b=???
b
一時ファイルを生成せずにstderrを変数にリダイレクトしたいと思います。
echo $b
# output should be: "This is an error"
動作しますが、一時ファイルを使用するソリューション:
touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt
問題はどのようにstderr
リダイレクトしますか?強く打つ一時ファイルなしでfunc
変数に機能を適用しますか?b
ベストアンサー1
zsh
書き込み可能な一時ファイルを使用して、Linuxおよびここにある文書(たとえば、5.1または5.1より前)を実装するシェルでbash
次のことを実行できます。
{
out=$(
chmod u+w /dev/fd/3 && # needed for bash5.0
ls /dev/null /x 2> /dev/fd/3
)
status=$?
err=$(cat<&3)
} 3<<EOF
EOF
printf '%s=<%s>\n' out "$out" err "$err" status "$status"
(ls /dev/null /x
stdoutとstderrに何かを印刷するためのコマンド例はどこにありますか?)
を使用すると、zsh
次の操作も実行できます。
(){ out=$(ls /dev/null /x 2> $1) status=$? err=$(<$1);} =(:)
(=(cmd)
一時ファイルと匿名関数を使用したプロセス置換形式です(){ code; } args
。)
とにかく一時ファイルを使用したいと思います。パイプを使用するすべてのソリューションは、出力が大きいとデッドロックが発生しやすくなります。 2つの別々のパイプを介してstdoutとstderrを読み込み、ループからselect()
/poll()
およびいくつかの読み取りを使用してロックを生成せずに2つのパイプからデータを読み取ることができますが、これは非常に複雑で、AFAIKは組み込みサポートのみがzsh
可能select()
で、yash
rawインターフェイスは1つだけです。pipe()
(詳細は参照シェルリダイレクトを使用して同じファイル記述子を読み書きする)。
別のアプローチは、ストリームの1つを一時ファイルの代わりに一時メモリに格納することである。次のような(zsh
またはbash
構文):
{
IFS= read -rd '' err
IFS= read -rd '' out
IFS= read -rd '' status
} < <({ out=$(ls /dev/null /x); } 2>&1; printf '\0%s' "$out" "$?")
(コマンドがNULを出力しないと仮定)
これには$err
末尾の改行が含まれます。
別のアプローチは、stdoutとstderrを別々に装飾し、読み取るときに装飾を削除することです。
out= err= status=
while IFS= read -r line; do
case $line in
(out:*) out=$out${line#out:}$'\n';;
(err:*) err=$err${line#err:}$'\n';;
(status:*) status=${line#status:};;
esac
done < <(
{
{
ls /dev/null /x |
grep --label=out --line-buffered -H '^' >&3
echo >&3 "status:${PIPESTATUS[0]}" # $pipestatus[1] in zsh
} 2>&1 |
grep --label=err --line-buffered -H '^'
} 3>&1
)
GNUgrep
と行が十分に短いとします。ラインがPIPEBUF(Linuxの場合4K)より大きい場合、2秒の出力ラインが最終的にチャンクに分割される可能性grep
があります。