Bashスクリプトからstderrを一時fdにリダイレクトする

Bashスクリプトからstderrを一時fdにリダイレクトする

Bashでのリダイレクトの使用に関する多くの質問がSEで提起され、回答されたことがわかりました。実装するしかし、誰も私の質問に答えていないようです。

私が達成したいのは、すべての出力を次にリダイレクトすることです。標準エラースクリプトから一時ファイルに保存し、次に復元します。標準エラースクリプトが正常に終了しない場合にのみ適用されます。コンテンツ復元機能標準エラー終了に失敗した場合、呼び出し側にエラーに関する情報を返すために必要です。リダイレクト標準エラー/dev/null関連のないノイズや有用な情報は削除されます。明示的な一時ファイルは他の多くの問題を追加するので、可能であれば避けるべきです。https://dev.to/philgibbs/avoiding-temporary-files-in-shell-scripts)。コンテンツ標準出力呼び出し元に透過的に返す必要があります。スクリプトが正常に終了しない場合、呼び出し側はこれを無視しますが、スクリプトはそれを気にする必要はありません。

その理由は、すべての出力を考慮する他のプログラムからスクリプトが呼び出されるためです。標準エラーゼロ以外の終了コードをテストする代わりにエラーが発生します。次の例ではOpenSSLエコ進行インジケータ標準エラーこれはエラーを示さず、コマンドが正常に完了した後に無視できます。

以下は私のスクリプトの単純化されたバージョンです。OpenSSLこれは、ランダムで複雑なガイドラインのプレースホルダーにすぎません.)

#!/bin/bash
set -euo pipefail

# Redirect stderr to temp fd 3
exec 3>&2

# In case of error, copy contents of fd 3 to stderr
trap 'cat <&3 >&2' ERR

# Upon exit, restore original stderr and close fd 3
trap 'exec 2>&3 3>&-' EXIT

# This nonetheless outputs the progress indicator to stderr
openssl genpkey -algorithm RSA

しかし、スクリプトが印刷され続けているので、明らかに何かが欠けています。標準エラー正常に終了しましたが、失敗するとブロックされます。リダイレクトする方法が混乱していると思います。実装する働く私は何が間違っていましたか?

ベストアンサー1

chronicmoreutils は必要なほとんどすべての操作を行います。

chronic cmd

cmdのstdoutとstderrは、失敗したり終了したりしない限り破棄されますcmd。この場合、chronicstdout出力はstdoutに書き込まれ、stderr出力はメモリに保存されているstderrに書き込まれます。

chronicperl任意のデータを処理し、両方のストリームから独立して読み取るように書かれています。一方、zsh以外のシェルはブロックまたはNUL文字のため、変数に任意の出力を格納することはできません。また、コマンドの置き換え(でもzsh)は末尾の改行を削除します。

ここから最初から削除しても一時ファイルを引き続き使用でき、これに関連する問題のほとんどを取り除くことができます(たとえば、ほとんどのシェルがここのドキュメントで実行する作業)。

#! /bin/bash -
set -o nounset -o errexit -o pipefail

tmp=$(mktemp)
exec 3>&2 2> "$tmp" 4< "$tmp"
rm -f -- "$tmp"
trap '[ "$?" -eq 0 ] || cat <&4 >&3' EXIT

cmd1
cmd2

おすすめ記事