setze -e in eine Unterschale

8

Ich dachte, es set -ehätte den gleichen Effekt auf Subshells wie auf Top-Level-Shell. Anscheinend nicht. Diese:

(
  set -e
  false 
  true
) || echo false1


bash -ec '
  set -e
  false 
  true
' || echo false2


bash <<EOF || echo false3
  set -e
  false 
  true
EOF

bash <<EOF || echo false4
  false 
  true
EOF

bash <<EOF || echo false5
  false  &&
  true
EOF

Druckt

false2
false3
false5

Wo ist das dokumentiert? Kann ich Subshells dazu bringen, bei Fehlern zu beenden, ohne alle ihre Befehle mit &&(oder ohne || exit $?nach jedem Befehl) zu verbinden?

Bearbeiten:

Mein besonderer Anwendungsfall war so etwas wie:

set -e
  # ...
status=0
( false; true ) || status=$?
report_code $status
return $status

Wo der Inhalt der Subshell mein eigentlicher Code war. Das Problem dabei ist, dass der Status immer auf 0 gesetzt wird und das Ersetzen || durch ;einen unerwünschten Fehlerausgang aufgrund der äußeren Menge -e verursacht.

Ich habe es gelöst mit:

set -e
  # ...
set +e
( false; true ); status=$?
set -e
report_code $status
return $status

Ich wünschte, ich müsste das nicht tun, aber es scheint, dass alle gängigen Shells diese Dichotomie zwischen ausgeführter Subshell und nur gegabelter Subshell zeigen:

#!/bin/sh

echo FORK\'D:
export SH
for SH in dash bash ksh zsh; do
    $SH -c 'st=0; ( set -e; false; true ) || st=$?; printf "%s\t%s\n" $SH  $st; '
done

echo EXEC\'D:
for SH in dash bash ksh zsh; do
    $SH -c 'st=0; '$SH' -c " set -e; false; true " || st=$?; printf "%s\t%s\n" $SH $st; '
done

AUSGABE:

FORK'D:
dash    0
bash    0
ksh 0
zsh 0
EXEC'D:
dash    1
bash    1
ksh 1
zsh 1
PSkocik
quelle
1
Es ist beabsichtigt, den -eDokumentationsteil des Sets zu lesen .
Cuonglm

Antworten:

13

Beobachten:

$ ( set -e; false ; true ) || echo false1
$ ( set -e; false ; true ) ; echo code=$?
code=1

Ebenfalls:

$ ( set -e; false ; true; echo inside=$? ) || echo false1
inside=0

Offenbar , wenn der Unterschale durch eine folgt ||, set -everursacht nicht die Subshell Ausfahrt auf das Erreichen falseBefehl. Stattdessen wird die Unterschale fortgesetzt und ausgeführt true(und echo inside=$?).

Die Philosophie von set -eist typischerweise, dass es nur bei nicht erfassten Fehlern existiert. Hier ||scheint das Vorhandensein von außerhalb der Unterschale der Shell zu sagen, dass der Fehler innerhalb der Unterschale "abgefangen" wird und daher set -ekeinen Ausgang nachher verursacht false.

set -ehat viele überraschende Verhaltensweisen. Siehe "Warum macht set -e nicht das, was ich erwartet habe?"

Dokumentation

Das obige Verhalten wird in der Dokumentation unter man bash:

-e

Wird sofort beendet, wenn eine Pipeline (die aus einem einzelnen einfachen Befehl bestehen kann), eine Liste oder ein zusammengesetzter Befehl (siehe SHELL GRAMMAR oben) mit einem Status ungleich Null beendet wird. Die Shell wird nicht beendet, wenn der fehlgeschlagene Befehl Teil der Befehlsliste unmittelbar nach einer Weile oder bis zum Schlüsselwort ist, Teil des Tests nach den reservierten if- oder elif-Wörtern, Teil eines Befehls, der in einem && oder || ausgeführt wird Listemit Ausnahme des Befehls nach dem letzten && oder || ein beliebiger Befehl in einer Pipeline außer dem letzten oder wenn der Rückgabewert des Befehls mit! invertiert wird. Wenn ein anderer zusammengesetzter Befehl als eine Subshell einen Status ungleich Null zurückgibt, weil ein Befehl fehlgeschlagen ist, während -e ignoriert wurde, wird die Shell nicht beendet. Ein Trap auf ERR wird, falls gesetzt, ausgeführt, bevor die Shell beendet wird. Diese Option gilt für die Shell-Umgebung und jede Subshell-Umgebung separat (siehe BEFEHLSAUSFÜHRUNGSUMGEBUNG oben) und kann dazu führen, dass Subshells beendet werden, bevor alle Befehle in der Subshell ausgeführt werden. [Betonung hinzugefügt.]

John1024
quelle
"Die Philosophie von set -e ist typischerweise, dass es nur bei nicht erfassten Fehlern beendet wird. Hier scheint das Vorhandensein von || außerhalb der Unterschale der Shell zu sagen, dass der Fehler innerhalb der Unterschale 'abgefangen' ist und daher set -e nicht verursacht ein Ausgang nach false. " Ja, aber der Fehler in der Subshell wird nicht gemeldet. "false1" wird nicht gedruckt. Es maskiert nur den Fehler, als ob er überhaupt set -enicht wirksam wäre.
Jonas Berlin
Sie scheinen über den Fall zu sprechen ( set -e; false ; true ) || echo false1. Die Subshell wird mit dem Rückkehrcode des letzten Befehlslaufs beendet. Da ||der Exit danach unterdrückt wird false, ist der letzte in der Subshell ausgeführte Befehl true. Somit false1ist nicht , weil die Subshell Ausfahrten mit dem Rückkehrcode Null gedruckt.
John1024