stderrとstdoutをファイルにリダイレクトするとエラーが発生しますか?

stderrとstdoutをファイルにリダイレクトするとエラーが発生しますか?

filenameファイル(...)をパラメータとして使用し、stdoutとstderrをというファイルにリダイレクトするスクリプトを作成していますfilename.output。ファイルには一度だけbashしたいコマンドが含まれています(ファイル内のコマンドを複数回実行してはいけない場合、つまりmkdirまたはrmdir)。パラメーターを繰り返すと、このコマンドが機能することがわかりました。

bash $a &> "$(basename $a).output"

.outputただし、何らかの理由で作成に失敗した場合に備えて、このコマンドのエラー状態を使用したいと思います。

if ! bash $a &> "$(basename $a).output"
    then
        echo >&2 "failed to create $(basename $a).output"
fi

これにより、私のifステートメントは常にtrueと評価されます。私の考えではstdout または stderr が失敗するにはリダイレクトが必要ですが、どちらも必要ないためです。私がテストしているコマンドはstderrとstdoutの両方を生成することはほとんどありません。つまり、このコマンドはdatestdoutを生成しますがstderrは生成しないため、stderrをリダイレクトできないため、ゼロ以外の戻り値が生成されます。

コードが正しく機能しない理由の私の分析は正しいですか?それでは、stdoutリダイレクトとstderrリダイレクトの失敗を別々にチェックして、いつ作成されなかったのかを.output正確に知る方法はありますか?

編集する:エラーを生成したコマンドを含むファイルがあるbash "$a"たびに、if文全体がtrueと評価されたため、実際に問題がコンポーネントにあることがわかりました。$a今私の質問は、stdoutとstderrをリダイレクトすると、私が知る必要があるエラーが発生する状況がありますか?

答え1

そうすれば

if ! bash $a &> "$(basename $a).output"; then

リダイレクトでエラーが発生した場合、またはスクリプトが実行されたがゼロ以外の終了状態を返した場合は、if文のデフォルトブランチが実行されます。この&> file演算子はstdoutとstderrと同じです> file 2>&1が、関連するファイルは1つだけなので、あるリダイレクトは成功し、他のリダイレクトは失敗する方法を見つけるのが難しいでしょう。権限の問題、存在しないパス、ディスクがいっぱいになるなど、ファイルを作成するさまざまな理由でリダイレクトが失敗する可能性があります。

テストスクリプトを書いてみましょう。これは何かを印刷するだけで、引数を指定するとエラーで終了します。

$ cat test.sh
#!/bin/bash
echo test script
if [ "$#" != 0 ]; then
    echo exiting with error
    exit 1
fi
$ chmod +x test.sh

ここではリダイレクトが失敗します(./non-existing-dir/実際には存在しないため)。

$ if ! ./test.sh > ./non-existing-dir/test.output; then 
    echo "it failed (for some reason)"; fi
bash: ./non-existing-dir/test.output: No such file or directory
it failed (for some reason)

ここでは、リダイレクトは成功し、出力はファイルに収集されますが、スクリプト自体は失敗ステータスを返します。

$ rm -f test.output
$ if ! ./test.sh 1 > ./test.output; then
    echo "it failed (for some reason)"; fi
it failed (for some reason)

$ cat test.output 
test script
exiting with error

リダイレクト時にエラーを見逃すことはできません。エラーのため、コマンドはゼロ以外の状態で終了します。ただし、正確な値は定義されておらず、シェルによって異なるため、これを使用してリダイレクトの失敗とスクリプト自体の失敗を区別することはできません。 POSIX シェルコマンド言語定義は、2.8.2 コマンドの終了状態で表されます。

単語の拡張またはリダイレクト中にコマンドが失敗した場合、終了ステータスは1から125(含む)の間でなければなりません。

Bashのマニュアルにも同様の内容が記載されています。3.7.1 簡単なコマンド拡張


リダイレクトエラーを具体的に確認するには、次の手順を実行します。できるこれを行うには、プログラムを実行する前に個別にリダイレクトをオンにするだけです。たとえば、次のスクリプトを使用してプログラムを実行できます。

$ cat redir.sh 
#!/bin/sh

outfile="${1?please define output file}"
cmd="${2?please define command to run}"
shift 2

if ! exec 9> "$outfile"; then
    echo "error: cannot create redirection to '$outfile'"
    exit 1
fi

if ! "$cmd" "$@" >&9 2>&9; then
    echo "error: script '$cmd' failed"
    exit 1
fi
exec 9>&-         # close the output fd

これで、失敗したリダイレクトが次のように検出されます。

$ bash redir.sh ./non-existing-dir/test.output ./test.sh
redir.sh: line 8: ./non-existing-dir/test.output: No such file or directory
error: cannot create redirection to './non-existing-dir/test.output'

スクリプトも失敗します。

$ bash redir.sh ./test.output ./test.sh 1
error: script './test.sh' failed

関連情報