パイプラインの最初のコマンドが失敗したときにset -oパイプラインの失敗を強制する方法

パイプラインの最初のコマンドが失敗したときにset -oパイプラインの失敗を強制する方法

PostgresデータベースのデータをBashのファイルにエクスポートしようとしています。ただし、データベース接続が失敗しない場合にのみファイルを上書きしたい(たとえば、一部のデータを返す)。

試してみてくださいパイプ故障オプションですが、最初のコマンドがエラーのために失敗した場合(たとえば、ホストが存在しない場合)、catコマンドは引き続き実行され、空のファイルを生成します(保護したい最後の良い項目を消去します)。次の例では、myhostは無効なホストなので、psqlコマンドは失敗します。

したがって、大きな質問は、パイプ障害が設定されたときに最初のコマンドが失敗した場合に後続のコマンドが実行されないようにする方法です。

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

PG_HOST=myhost

psql $PG_HOST -At -F$'\t' -c "SELECT * FROM mytable" | cat > /tmp/mytable.txt

ベストアンサー1

set -o pipefail -o errexit後続のコマンドが実行されるのを防ぎますが、防止しようとしないため役に立ちません。フォローコマンドが実行されます。パイプラインでのproducer | consumer実行producerconsumerコマンド平行に。失敗した場合、異常なタイミング事故がない限り、すでに起動しているため、consumer起動を防ぐことはできません。producer

2 つの可能性のみが「consumer成功して非 null 出力を生成する」と「consumer失敗して出力なしを生成」の場合は、次のようになります。ifneJoey Hessのmoreutils

producer | ifne consumer

私はこれがあなたのユースケースではうまくいかないと思います。一致する行がない可能性があり(肯定エラー、古いデータを取得)、データベース接続が途中で切断される可能性があります(肯定エラー、切り捨てられたデータを取得)。

生産者が成功したかどうかを知る必要がある場合は、消費者を開始する前に生産者が完了するのを待つ必要があります。消費者はまだ表示されていないため、出力を保存する必要があります。

出力にヌルバイトが含まれておらず、単一の改行文字で終わり、大きすぎない場合は、シェル変数に保存できます。

output=$(producer); producer_status=$?
if [ "$producer_status" -ne 0 ]; then
  echo >&2 "Producer failed with status $producer_status"
  exit "$producer_status"
fi
printf '%s\n' "$output" | consumer

zshや他のいくつかのシェル(ksh93とbashを含む)では、最後の行をconsumer <<<"$output"

コマンドの置き換えは末尾の改行を削除することに注意してください。末尾の空行が関連している場合、回避策は最初の行を次に変更することです。

output=$(producer; ret=$?; echo .; exit "$?")
producer_status=$? output=${output%?}

$outputこれにより、末尾の改行文字(存在する場合)を含む完全な出力が含まれます。次に、提供するprintf %s "$output"代わりに使用してください。printf '%s\n' "$output"consumer

出力が大きすぎるかヌルバイトを含む可能性がある場合は、一時ファイルに保存してください。

おすすめ記事