Speichern und Wiederherstellen aller Shell-Optionen einschließlich des Errexit

9

Ich habe viele Fragen auf verschiedenen Stack-Exchange-Sites und Unix- Hilfeforen gelesen, wie man Shell-Optionen ändert und sie dann wiederherstellt .

Die empfangene Weisheit scheint entweder zu sein , um das Ergebnis zu speichern aus set +ooder shopt -pound dann evalspäter die vorherigen Einstellungen wiederherzustellen.

Bei meinen eigenen Tests mit bash 3.x und 4.x wird die errexitOption jedoch beim Ersetzen von Befehlen nicht korrekt gespeichert.

Hier ist ein Beispielskript, um das Problem zu zeigen:

set -o errexit
set -o nounset
echo "LOCAL SETTINGS:"
set +o
OLDOPTS=$(set +o)
echo
echo "SAVED SETTINGS:"
echo "$OLDOPTS"

Und Ausgabe (ich habe einige der irrelevanten Variablen herausgeschnitten):

LOCAL SETTINGS:
set -o errexit
set -o nounset

SAVED SETTINGS:
set +o errexit
set -o nounset

Das scheint extrem gefährlich. Die meisten Skripte, die ich schreibe, hängen davon ab errexit, die Ausführung anzuhalten, wenn Befehle fehlschlagen. Ich habe gerade einen Fehler in einem meiner Skripte gefunden, der dadurch verursacht wurde, dass die Funktion, die errexitam Ende wiederhergestellt werden sollte, ihn überschrieb und ihn für die Dauer des Skripts auf den Standardwert " Aus" zurücksetzte.

Ich möchte in der Lage sein, Funktionen zu schreiben, mit denen Optionen nach Bedarf festgelegt und dann alle Optionen vor dem Beenden ordnungsgemäß wiederhergestellt werden können. Aber es scheint, als ob in der Subshell, die durch die Befehlsersetzung aufgerufen wird, errexitnicht vererbt wird.

Ich weiß nicht, wie ich das Ergebnis sparen kann, set +oohne Befehlsersetzung zu verwenden oder durch FIFO-Rahmen zu springen. Ich kann lesen, $SHELLOPTSaber es ist nicht beschreibbar oder im eval-able-Format.

Ich weiß, dass eine Alternative darin besteht, eine Subshell-Funktion zu verwenden , aber das bringt viele Kopfschmerzen mit sich, wenn es darum geht, Ausgaben zu protokollieren und mehrere Variablen zurückzugeben.

Wahrscheinlich verwandt: /programming/29532904/bash-subshell-errexit-semantics (anscheinend gibt es eine Problemumgehung für Bash 4.4 und höher, aber ich hätte lieber eine tragbare Lösung)

Eliot
quelle
Dadurch werden die Bash- shoptSet-Optionen (wie nullglob) nicht wiederhergestellt .
Isaac

Antworten:

6

Was Sie tun, sollte funktionieren. Aber bash deaktiviert die errexitOption bei Befehlsersetzungen, sodass alle Optionen außer dieser beibehalten werden. Dies ist spezifisch für Bash und spezifisch für die errexitOption. Bash bleibt erhalten, errexitwenn es im POSIX-Modus ausgeführt wird. Seit Bash 4.4 wird Bash auch errexitbei einer Befehlsersetzung nicht gelöscht, wenn dies shopt -s inherit_errexitwirksam ist.

Da die Option deaktiviert ist, bevor Code innerhalb der Befehlsersetzung ausgeführt wird, müssen Sie ihn außerhalb überprüfen.

OLDOPTS=$(set +o)
case $- in
  *e*) OLDOPTS="$OLDOPTS; set -e";;
  *) OLDOPTS="$OLDOPTS; set +e";;
esac

Wenn Ihnen diese Komplexität nicht gefällt, verwenden Sie stattdessen zsh.

setopt local_options
Gilles 'SO - hör auf böse zu sein'
quelle
3
Beachten Sie, dass bash4.4jetzt local -(à la ash) als Äquivalent zu zsh's setopt localoptions(oder was ksh88 standardmäßig tut)
Stéphane Chazelas
Siehe auch shopt -s lastpipe; set +o | IFS= read -rd '' OLDOPTS || :(die beiden Optionssätze sind eine weitere bashBesonderheit).
Stéphane Chazelas
Einfacher : OLDOPTS="$(set +o); set -$-".
Isaac
2

Nachdem ich das Alte von oben auf alpine 3.6 ausprobiert habe, habe ich jetzt den folgenden viel einfacheren Ansatz gewählt:

OLDOPTS="$(set +o); set -${-//c}"
set -euf -o pipefail

... my stuff

# restore original options
set +vx; eval "${OLDOPTS}"

Gemäß Dokumentation enthält "$ -" die Liste der derzeit aktiven Optionen. Scheint großartig zu funktionieren, vermisse ich etwas?

Erich Eichinger
quelle
@Isaac Ich benutze 'set + euf', weil $ - nur die Liste der aktiven Optionen enthält. dh beim Zurücksetzen deaktiviere ich zuerst alles und reaktiviere dann nur die Liste der zuvor aktiven Optionen (ich wähle 'euf', da dies die einzigen Optionen sind, die ich normalerweise ändere - für den allgemeinen Gebrauch sollte es wahrscheinlich die vollständige Liste der möglichen Optionen enthalten). Guter Punkt zu 'set + vx', ich habe das obige Beispiel entsprechend geändert
Erich Eichinger
das kann für errexit funktionieren, aber nicht zB für bounds checking oder globbing ( set -uf)
Erich Eichinger
@Isaac ich stehe korrigiert. Ich weiß nicht, was ich falsch gemacht habe. Vielen Dank für Ihre Hilfe! Auch gut zu sehen, dass Sie auf das -c Flag Problem gestoßen sind und es gelöst haben - ich wünschte ich hätte Ihren Kommentar früher gesehen;)
Erich Eichinger
Ich habe die obige Lösung basierend auf @Isaacs Feedback aktualisiert
Erich Eichinger
1

errexit wird in Prozesssubstitutionen propagiert.

set -e

# Backup restore commands into an array
declare -a OPTS
readarray -t OPTS < <(shopt -po)

set +e

# Restore options
declare cmd
for cmd in "${OPTS[@]}"; do
    eval "$cmd"
done

Prüfen:

$  shopt -po errexit
set -o errexit

Bash-Version:

$ bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Sergej Alikov
quelle
1

Die einfache Lösung besteht darin, die errexitEinstellung an Folgendes anzuhängen OLDOPTS:

OLDOPTS="$(set +o)"
[ "${BASH_VERSION:+x}" ] && shopt -qo errexit && OLDOPTS+=";set -e" || true

Erledigt.

Isaac
quelle
Siehe auch [[ -o errexit ]](ksh / bash / zsh) und [ -o errexit ](bash / ksh / yash)
Stéphane Chazelas
Das shoptist ein gültiger Befehl für Bash, es gibt keinen sinnvollen Grund, dies zu vermeiden (ich bin nur eine Frage Ihrer persönlichen Präferenz, wie Antworten geschrieben werden sollen). Keine sinnvolle Veränderung. @ StéphaneChazelas
Isaac