web-dev-qa-db-ja.com

ハドソン:「はい:標準出力:壊れたパイプ」

ハドソンでシェルスクリプトを実行する必要があります。そのスクリプトには、ユーザーからの回答が必要です。自動応答を行うために、次のコマンドラインを実行しました。

yes | ./MyScript.sh

これはUbuntuターミナルでうまく機能します。しかし、Hudsonジョブで同じコマンドを使用すると、スクリプトが自動化され、必要なすべての作業が実行されますが、最後に、次の2行のエラーが発生します。

yes: standard output: Broken pipe
yes: write error

そして、これは私のハドソンの仕事に失敗を引き起こします。

ハドソンでうまく機能するようにコマンドラインを変更するにはどうすればよいですか?

18
Farah

しかし、スクリプトの実行中にこのエラーが発生しないことをどのように説明しますかローカル、しかしスクリプトの実行時にエラーが発生しますリモートハドソンの仕事から?

ターミナルで実行している場合(ローカル); yesは、_MyScript.sh_がすでに終了しているときにパイプに書き込もうとしたときに生成されるSIGPIPEシグナルによって強制終了されます。

ハドソンでコマンド(remotely)を実行するものは何でも、そのシグナルをトラップします(ハンドラーを_SIG_IGN_に設定します。trapコマンドを実行して検索することで、テストできます。出力のSIGPIPEの場合)、新しい子プロセス(yesおよび_MyScript.sh_を実行するもの(この場合はshなど)のシグナルを復元しません)。シグナルの代わりに書き込みエラー(EPIPE)が発生します。 yesは書き込みエラーを検出し、報告します。

エラーメッセージは無視してかまいません。

_yes 2>/dev/null | ./MyScript.sh
_

パイプラインを実行するコンポーネントに対するバグを報告することもできます。バグは、子がフォークされた後、SIGPIPEをデフォルトのハンドラーに復元しないことです。これは、プログラムがPOSIXシステムの端末で実行されるときに期待するものです。 Javaベースのプログラムでそれを行う標準的な方法があるかどうかはわかりませんが。 jvmはおそらくすべての書き込みエラーに対して例外を発生させるので、SIGPIPEで死なないことはJavaプログラムにとって問題ではありません。

ハドソンプロセスなどのデーモンがSIGPIPEシグナルを無視することは一般的です。通信しているプロセスが停止し、とにかく書き込みエラーをチェックするという理由だけでデーモンが停止することは望ましくありません。

ターミナルで実行するように作成された通常のプログラムは、すべてのprintf()のステータスでエラーをチェックしませんが、パイプラインのプログラムが停止した場合、たとえば_source | sink_パイプラインを実行した場合にそれらを停止させます。通常、sourceが終了した場合、sinkプロセスをできるだけ早く終了する必要があります。

EPIPEシグナルが無効になっている場合(ハドソンの場合のように)、またはプログラムがそれを受信して​​も停止しない場合(SIGPIPEプログラムが定義されていない場合)、yes書き込みエラーが返されます。 SIGPIPEのハンドラーなので、シグナルを受信すると停止するはずです)。

エラーを無視したくありません。正しいコマンドを実行するか、エラーを取り除くために修正したいと思います。

唯一の方法 yesプロセスは、強制終了されるか、書き込みエラーが発生すると停止しますSIGPIPEシグナルが(親によって)無視されるように設定されていて、他のシグナルがプロセスを強制終了しない場合、yesは_./MyScript.sh_終了時に書き込みエラーを受け取ります。 yesプログラムを使用する場合、他のオプションはありません。

SIGPIPEシグナルとEPIPEエラーは、まったく同じ情報を伝達します-パイプが壊れていますSIGPIPEyesプロセスに対して有効になっている場合、エラーは表示されません。そして、あなたがそれを見たという理由だけで。新しいことは何も起こりません。これは、_./MyScript.sh_が終了したことを意味します(成功または失敗-問題ではありません)。

25
jfs

Yesプログラムを使用してスクリプトにパイプしようとしていますか?またはスクリプトにyesをエコーし​​ますか?プロセスがjenkinsを介して機能している場合は、シェルコマンドの最後に「; true」を追加します。

1
Electrawn

yes./MyScript.shはそれぞれ明示的なサブシェルで実行できるため、yesコマンドをバックグラウンドで実行し、yespid./MyScript.shに送信することができます。サブシェルを作成し、そこにEXITにトラップを実装して、yesコマンドを手動で終了します。 (EXITのトラップは、パイプされたコマンドシーケンスの最後のコマンドのサブシェルに常に実装する必要があります)。

# avoid hangup or "broken pipe" error message when parent process set SIGPIPE to be ignored
# sleep 0 or cat /dev/null: do nothing but with external command (for a Shell builtin command see: help :)
(
trap "" PIPE
( (sleep 0; exec yes) & echo ${!}; wait ${!} ) | 
   ( 
     trap 'trap - EXIT; kill "$yespid"; exit 0' EXIT
     yespid="$(head -n 1)"
     head -n 10  # replacement for ./MyScript.sh
   )
echo ${PIPESTATUS[*]}
)

終了コード0yesサブシェルを終了する場合は、次のようにすることもできます。

# avoid hangup or "broken pipe" error message when parent process set SIGPIPE to be ignored
# set exit code of yes subshell to 0
(
trap "" PIPE
   (
      trap 'trap - TERM; echo "kill from yes subshell ..." 1>&2; kill "${!}"; exit 0' TERM 
      subshell_pid="$(bash -c 'echo "$PPID"')"
      (sleep 0; exec yes) & echo "${subshell_pid}"; wait ${!} 
   ) | 
   ( 
      trap 'trap - EXIT; kill -s TERM "$subshell_pid"; exit' EXIT
      subshell_pid="$(head -n 1)"
      head -n 10  # replacement for ./MyScript.sh
   )
echo ${PIPESTATUS[*]}
)
0
cazu

このエラーが発生しましたが、問題はyes: standard output: Broken pipeを出力することではなく、エラーコードを返すことです。

スクリプトを bash strict mode include -o pipefailで実行しているため、「エラー」が発生すると、スクリプトでエラーが発生します。

エラーを回避する方法

私がこれを避けた方法は次のようなものです:

bash -c "yes || true" | my-script.sh
0
Robin Winslow