一時ファイルなしでSTDERRとSTDOUTを別の変数にリダイレクトする

一時ファイルなしでSTDERRとSTDOUTを別の変数にリダイレクトする
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 /xstdoutとstderrに何かを印刷するためのコマンド例はどこにありますか?)

を使用すると、zsh次の操作も実行できます。

(){ out=$(ls /dev/null /x 2> $1) status=$? err=$(<$1);} =(:)

=(cmd)一時ファイルと匿名関数を使用したプロセス置換形式です(){ code; } args。)

とにかく一時ファイルを使用したいと思います。パイプを使用するすべてのソリューションは、出力が大きいとデッドロックが発生しやすくなります。 2つの別々のパイプを介してstdoutとstderrを読み込み、ループからselect()/poll()およびいくつかの読み取りを使用してロックを生成せずに2つのパイプからデータを読み取ることができますが、これは非常に複雑で、AFAIKは組み込みサポートのみがzsh可能select()で、yashrawインターフェイスは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があります。

おすすめ記事