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
chronic
moreutils は必要なほとんどすべての操作を行います。
chronic cmd
cmdのstdoutとstderrは、失敗したり終了したりしない限り破棄されますcmd
。この場合、chronic
stdout出力はstdoutに書き込まれ、stderr出力はメモリに保存されているstderrに書き込まれます。
chronic
perl
任意のデータを処理し、両方のストリームから独立して読み取るように書かれています。一方、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
答え2
私を正しい方向に導くのを助けてくれた上記のコメントのすべての人に感謝します。これが私が探していた答えだと思い、本当にインスピレーションを受けました。別のSEの答えは次のとおりです。:
#!/bin/bash
set -euo pipefail
shopt -s inherit_errexit
trap 'printf "%s\n" "$_ERR" >&2' ERR
{ _ERR=$({
# Arbitrary commands go here
openssl genpkey -algorithm RSA
} 2>&1 >&3 3>&-); } 3>&1
さらなる改善のためのアイデアを歓迎します。
ご提案いただいた@StéphaneChazelasに感謝します。