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
があります。
答え2
まあ、一時ファイルなしである変数からstderrをキャプチャし、別の変数からstdoutをキャプチャするのは簡単な作業ではありません。
これは有効な例です
func() {
echo 'hello'
echo 'This is an error' >&2
}
result=$(
{ stdout=$(func) ; } 2>&1
echo -e "mysuperuniqueseparator\n"
echo -e "${stdout}\n"
)
var_out=${result#*mysuperuniqueseparator$'\n'}
var_err=${result%$'\n'mysuperuniqueseparator*}
私はこれがstderrをstdoutにリダイレクトし、2つの変数を区切り文字を使用して変数に入れてから2つの部分に分割する汚れた方法なので、満足できません。
追加する:
明らかに、これはコマンドの標準出力または標準エラーに使用する区切り文字列を含む可能性があるため、信頼できません。
答え3
一時ファイル/fifo、興味深い評価/ファイル記述子などはありません。
x=$((echo 'this is stdout'; echo 'this is stderr' 1>&2; exit 123) 2> >(sed -r 's/^/2/g') 1> >(sed -r 's/^/1/g'))
echo $? ### exit code is preserved
# 123
echo "$x" | sed '/^2/d;s/^1//g' ### stdout
# this is stdout
echo "$x" | sed '/^1/d;s/^2//g' ### stderr
# this is stderr
注:大規模出力には効率的ではない可能性があります。