bash:プロセス置換エラーをどのように伝播しますか?

bash:プロセス置換エラーをどのように伝播しますか?

シェルスクリプトを使用して実行されたコマンドが失敗するたびに失敗したいと思います。

通常私はこうする:

set -e
set -o pipefail

(通常私もset -uそう追加します)

問題は、上記の方法のいずれもプロセスの交換には効果がないことです。このコードは "ok" を印刷し、戻りコード = 0 で終了します。一方、私は失敗したいと思います。

#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)

プロセスの交換に加えて、「パイプの失敗」に対応するものはありますか?コマンドの出力をファイルに渡しますが、これらのプログラムが失敗するたびにエラーを発生させる別の方法はありますか?

貧しい人々の解決策は、これらのコマンドがstderrに書き込まれるかどうかを検出することです(ただし、一部のコマンドは成功した場合はstderrに書き込みます)。

別のposix互換ソリューションは名前付きパイプを使用することですが、コンパイルされたコードから動的に構築された単一のライナーでプロセス置換を使用してそのコマンドを開始する必要があり、名前付きパイプを作成すると作業が複雑になります(追加のコマンド、エラー捕獲)。削除など)

答え1

この問題は、次の方法でのみ解決できます。

cat <(false || kill $$) <(echo ok)
other_command

SIGTERMスクリプトのサブシェルは、2番目のコマンドを実行する前に()を完了しますother_command。コマンドはecho ok「時々」実行されます。問題は、プロセスの交換が非同期であることです。kill $$命令が実行されるという保証はありません。今後または後ろに注文するecho ok。これはオペレーティングシステムのスケジューリングの問題です。

次のbashスクリプトを考えてみましょう。

#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"

このスクリプトの出力は次のとおりです。

$ ./script
Terminated
$ echo $?
143           # it's 128 + 15 (signal number of SIGTERM)

または:

$ ./script
Terminated
$ pre
post

$ echo $?
143

試してみてください。数回試行すると、出力に2つの異なる順序が表示されます。最初のコマンドでは、echo他の2つのコマンドがファイル記述子に書き込む前にスクリプトが終了します。 2番目のコマンドでは、falseまたはkillコマンドをechoコマンドの後に予約できます。

あるいは、より正確には、シェルプロセスにシグナルを送信するユーティリティのシステムコールは、signal()echoシステムコールより遅くまたは早く予約(または転送)されます。killSIGTERMwrite()

ただし、スクリプトが停止し、終了コードがゼロではありません。だからそれはあなたの問題を解決する必要があります。

別のソリューションもちろん、これには名前付きパイプを使用します。ただし、名前付きパイプまたは上記の回避策を実装するスクリプトがどれほど複雑かによって異なります。

引用:

答え2

ちなみに、答えと意見が良くて役に立ったにもかかわらず、結局他のものを実装するようになりました。 (親プロセスでシグナルを受信するにはいくつかの制限がありましたが、質問には記載されていません。)

基本的に私は次のように終わりました。

command <(subcomand 2>error_file && rm error_file) <(....) ...

その後、エラーファイルを確認してください。存在する場合、どのサブコマンドが失敗したかがわかります(そしてerror_fileの内容が役に立つかもしれません)。最初に望んでいたよりも冗長でハッキング的ですが、1行のbashコマンドで名前付きパイプを作成するよりも簡単です。

答え3

この例では、killとともに使用する方法を示しますtrap

#! /bin/bash
failure ()
{
  echo 'sub process failed' >&2
  exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )

ただし、kill子プロセスから親プロセスに戻りコードを渡す方法はありません。

答え4

私が見つけた最も信頼できる方法は、関数のコンテキストと同様に、子プロセスのエラーコードを一時ファイルに保存することです。

function() {
  ERR=$(mktemp)
  VAR=$(some_command; echo $? > $ERR)
  if [[ $(cat $ERR && rm $ERR) -gt 0 ]]; then
    echo "An error occurred in the sub shell" > /dev/stderr
    return 1
  fi
}

関連情報