So erzwingen Sie „set -o pipefail“ beim Fehlschlagen des ersten Befehls in der Pipe

So erzwingen Sie „set -o pipefail“ beim Fehlschlagen des ersten Befehls in der Pipe

Ich versuche, Daten aus einer Postgres-Datenbank in eine Datei in Bash zu exportieren. Ich möchte jedoch sicherstellen, dass die Datei nur überschrieben wird, wenn die Verbindung zur Datenbank nicht fehlschlägt (d. h. ich erhalte einige Daten zurück).

Versuchte die Verwendung derRohrfehlerOption, aber wenn der erste Befehl mit einem Fehler fehlschlägt (Host existiert beispielsweise nicht), wird der Cat-Befehl trotzdem ausgeführt und generiert eine leere Datei (wobei der letzte gute Inhalt gelöscht wird, was ich verhindern möchte). Im folgenden Beispiel ist myhost ein ungültiger Host, sodass der psql-Befehl einfach fehlschlagen würde.

Die größere Frage ist also, wie sichergestellt werden kann, dass bei gesetztem Pipefail nachfolgende Befehle nicht ausgeführt werden, wenn der erste Befehl fehlschlägt.

#!/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

Antwort1

set -o pipefail -o errexitverhindert zwar die Ausführung nachfolgender Befehle, aber das hilft Ihnen nicht, da Sie nicht versuchen, einenanschließendBefehl wird nicht ausgeführt. In einer Pipeline werden producer | consumerdie Befehle producerund consumerausgeführtparallel zu. Sie können den Start nicht verhindern, consumerwenn producerein Fehler auftritt, da der Motor, sofern es nicht zu einem unerwarteten Zeitunfall kommt, bereits gestartet ist.

Wenn die einzigen beiden Möglichkeiten „ consumererfolgreich und erzeugt eine nicht leere Ausgabe“ und „ consumerfehlschlägt und erzeugt keine Ausgabe“ sind, können Sie verwendenifnevon Joey Hess' moreutils.

producer | ifne consumer

Ich glaube allerdings nicht, dass das in Ihrem Anwendungsfall funktioniert – es könnte sein, dass keine übereinstimmenden Zeilen vorhanden sind (falsch-negativ und Sie erhalten veraltete Daten), die Datenbankverbindung könnte mittendrin verloren gehen (falsch-positiv und Sie erhalten abgeschnittene Daten).

Wenn Sie wissen müssen, ob der Produzent erfolgreich war, müssen Sie warten, bis er fertig ist, bevor Sie den Verbraucher starten. Und da der Verbraucher noch nicht da ist, muss die Ausgabe irgendwo gespeichert werden.

Wenn die Ausgabe keine Nullbytes enthält, mit genau einem Zeilenumbruchzeichen endet und nicht zu groß ist, können Sie sie in einer Shell-Variable speichern.

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

In zsh und einigen anderen Shells, einschließlich ksh93 und bash, kann die letzte Zeile vereinfacht werden zu consumer <<<"$output".

Beachten Sie, dass die Befehlsersetzung nachfolgende Zeilenumbrüche entfernt. Wenn nachfolgende leere Zeilen relevant sind, besteht eine Problemumgehung darin, die erste Zeile in

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

$outputenthält dann die vollständige Ausgabe, einschließlich der nachfolgenden Zeilenumbruchzeichen (sofern vorhanden). Verwenden Sie dann printf %s "$output"anstelle von , printf '%s\n' "$output"um es an weiterzugeben consumer.

Wenn die Ausgabe möglicherweise zu groß ist oder Nullbytes enthält, speichern Sie sie in einer temporären Datei.

Antwort2

Wie DopeGhoti sagte:pipefail... bedeutet lediglich, dass Fehler an jedem Punkt einer Pipe-Kette für den Exit-Code gespeichert werden.[einer Pipeline].

Um das Skript bei einem Fehler zu beenden, verwenden Sie set -e.

Um die Erstellung der Datei zu verhindern, erstellen Sie eine temporäre Datei und benennen Sie sie bei Erfolg um, und zwar:

set -e 
psql $PG_HOST -At -F$'\t' -c \
    "SELECT * FROM mytable"  >  /tmp/mytable.txt~
                          # ^^^ cf. Useless Use of Cat
mv /tmp/mytable.txt~ /tmp/mytable.txt

Ich benutze immermachenfür diese Art von Dingen, weil es bei einem Fehler stoppt und mir ermöglicht, neu startbare Pipelines zu erstellen.

verwandte Informationen