Wie kann man den Wert von Shell-Optionen wie `set -x` wiederherstellen?

47

Ich möchte set -xam Anfang meines Skripts beginnen und es anschließend "rückgängig machen" (zum Status zurückkehren, bevor ich es festgelegt habe), anstatt es blind einzustellen +x. Ist das möglich?

PS: Ich habe bereits überprüft hier ; Das schien meine Frage nicht zu beantworten, soweit ich es beurteilen konnte.

Roney Michael
quelle

Antworten:

58

Abstrakt

Um a umzukehren, führen Sie set -xeinfach a aus set +x. Die meiste Zeit, das Gegenteil einer Zeichenfolge set -strist die gleiche Zeichenfolge mit ein +: set +str.

Im Allgemeinen können Sie zum Wiederherstellen aller (im Folgenden beschriebenen errexit) Shell-Optionen (geändert mit dem setBefehl) Folgendes tun (im Folgenden beschriebene Bash- shoptOptionen):

oldstate="$(set +o); set -$-"                # POSIXly store all set options.
.
.
set -vx; eval "$oldstate"         # restore all options stored.

Längere Beschreibung

Bash

Dieser Befehl:

shopt -po xtrace

generiert eine ausführbare Zeichenfolge, die den Status der Option widerspiegelt. Das pFlag bedeutet Drucken, und das oFlag gibt an, dass wir nach den vom setBefehl festgelegten Optionen fragen (im Gegensatz zu den vom shoptBefehl festgelegten Optionen ). Sie können diese Zeichenfolge einer Variablen zuweisen und die Variable am Ende Ihres Skripts ausführen, um den ursprünglichen Zustand wiederherzustellen.

# store state of xtrace option.
tracestate="$(shopt -po xtrace)"

# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"

# restore the value of xtrace to its original value.
eval "$tracestate"

Diese Lösung funktioniert für mehrere Optionen gleichzeitig:

oldstate="$(shopt -po xtrace noglob errexit)"

# change options as needed
set -x
set +x
set -f
set -e
set -x

# restore to recorded state:
set +vx; eval "$oldstate"

Durch set +vxdas Hinzufügen wird das Drucken einer langen Liste von Optionen vermieden.


Und wenn Sie keine Optionsnamen auflisten,

oldstate="$(shopt -po)"

Es gibt Ihnen die Werte aller Optionen. Wenn Sie die Markierung oweglassen, können Sie mit den folgenden shoptOptionen dasselbe tun :

# store state of dotglob option.
dglobstate="$(shopt -p dotglob)"

# store state of all options.
oldstate="$(shopt -p)"

Wenn Sie testen müssen, ob eine setOption festgelegt ist, ist die idiomatischste Methode (Bash), dies zu tun:

[[ -o xtrace ]]

Das ist besser als die beiden anderen ähnlichen Tests:

  1. [[ $- =~ x ]]
  2. [[ $- == *x* ]]

Bei allen Tests funktioniert Folgendes:

# record the state of the xtrace option in ts (tracestate):
[ -o xtrace ] && ts='set -x' || ts='set +x'

# change xtrace as needed
echo "some commands with xtrace as externally selected"
set -x
echo "some commands with xtrace set"

# set the xtrace option back to what it was.
eval "$ts"

So testen Sie den Status einer shoptOption:

if shopt -q dotglob
then
        # dotglob is set, so “echo .* *” would list the dot files twice.
        echo *
else
        # dotglob is not set.  Warning: the below will list “.” and “..”.
        echo .* *
fi

POSIX

Eine einfache, POSIX-kompatible Lösung zum Speichern aller setOptionen ist:

set +o

was im POSIX-Standard beschrieben wird als:

+ o

    Schreiben Sie die aktuellen Optionseinstellungen in die Standardausgabe in einem Format, das für die erneute Eingabe in die Shell als Befehle geeignet ist, mit denen dieselben Optionseinstellungen erzielt werden.

Also einfach:

oldstate=$(set +o)

behält Werte für alle Optionen bei, die mit dem setBefehl festgelegt wurden.

Um die ursprünglichen Werte der Optionen wiederherzustellen, muss die Variable erneut ausgeführt werden:

set +vx; eval "$oldstate"

Dies entspricht genau der Verwendung von Bashs shopt -po. Beachten Sie, dass dies nicht alle möglichen Bash- Optionen abdeckt , da einige davon von festgelegt werden shopt.

bash Sonderfall

Es gibt viele andere Shell-Optionen, die shoptin bash aufgelistet sind :

$ shopt
autocd          off
cdable_vars     off
cdspell         off
checkhash       off
checkjobs       off
checkwinsize    on
cmdhist         on
compat31        off
compat32        off
compat40        off
compat41        off
compat42        off
compat43        off
complete_fullquote  on
direxpand       off
dirspell        off
dotglob         off
execfail        off
expand_aliases  on
extdebug        off
extglob         off
extquote        on
failglob        off
force_fignore   on
globasciiranges off
globstar        on
gnu_errfmt      off
histappend      on
histreedit      off
histverify      on
hostcomplete    on
huponexit       off
inherit_errexit off
interactive_comments    on
lastpipe        on
lithist         off
login_shell     off
mailwarn        off
no_empty_cmd_completion off
nocaseglob      off
nocasematch     off
nullglob        off
progcomp        on
promptvars      on
restricted_shell    off
shift_verbose   off
sourcepath      on
xpg_echo        off

Diese können an die oben angegebene Variable angehängt und auf die gleiche Weise wiederhergestellt werden:

$ oldstate="$oldstate;$(shopt -p)"
.
.                                   # change options as needed.
.
$ eval "$oldstate" 

Es ist möglich (das $-wird angehängt, um sicherzustellen, dass errexites erhalten bleibt):

oldstate="$(shopt -po; shopt -p); set -$-"

set +vx; eval "$oldstate"             # use to restore all options.

Hinweis : Jede Shell hat eine etwas andere Methode, um die Liste der gesetzten oder nicht gesetzten Optionen zu erstellen (ganz zu schweigen von den definierten Optionen), sodass die Zeichenfolgen nicht zwischen Shells übertragbar sind, sondern für dieselbe Shell gelten.

zsh Sonderfall

zshfunktioniert auch korrekt (nach POSIX) seit Version 5.3. In früheren Versionen folgte es POSIX nur teilweise, set +oindem es Optionen in einem Format druckte, das für die erneute Eingabe in die Shell als Befehle geeignet war, jedoch nur für festgelegte Optionen (es druckte keine nicht festgelegten Optionen).

MKSH Sonderfall

Das mksh (und folglich lksh) ist noch nicht dazu in der Lage (MIRBSD KSH R54 2016/11/11). Das mksh-Handbuch enthält Folgendes:

In einer zukünftigen Version verhält sich set + o POSIX-konform und gibt Befehle aus, um stattdessen die aktuellen Optionen wiederherzustellen.

set -e Spezialfall

In bash wird der Wert von set -e( errexit) in Unterschalen zurückgesetzt, was es schwierig macht, seinen Wert set +oin einer $ (…) -Unterschale zu erfassen .

Um dieses Problem zu umgehen, verwenden Sie:

oldstate="$(set +o); set -$-"
Sorontar
quelle
21

Mit der Almquist-Shell und Derivaten ( mindestens dashNetBSD / FreeBSD sh) und bash4.4 oder höher können Sie Optionen für eine Funktion lokal festlegen mit local -(machen Sie die $-Variable lokal, wenn Sie möchten):

$ bash-4.4 -c 'f() { local -; set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace

Das bedeutet nicht anzuwenden sourced Dateien, aber Sie können neu definieren , sourcewie source() { . "$@"; }zu umgehen , dass.

Mit ksh88werden Optionsänderungen standardmäßig lokal für die Funktion ausgeführt. Mit ksh93gilt dies nur für Funktionen, die mit der function f { ...; }Syntax definiert wurden (und der Gültigkeitsbereich ist statisch im Vergleich zum dynamischen Gültigkeitsbereich, der in anderen Shells, einschließlich ksh88, verwendet wird):

$ ksh93 -c 'function f { set -x; echo test; }; f; echo no trace'
+ echo test
test
no trace

In zsh, das ist mit der localoptionsOption erledigt :

$ zsh -c 'f() { set -o localoptions; set -x; echo test; }; f; echo no trace'
+f:0> echo test
test
no trace

POSIXly können Sie tun:

case $- in
  (*x*) restore=;;
  (*) restore='set +x'; set -x
esac
echo test
{ eval "$restore";} 2> /dev/null
echo no trace

Einige Shells geben jedoch + 2> /dev/nullbeim Wiederherstellen ein aus (und Sie werden natürlich die Spur dieses caseKonstrukts sehen, wenn set -xes bereits aktiviert war). Dieser Ansatz ist auch nicht neu (wie wenn Sie dies in einer Funktion tun, die sich selbst aufruft, oder in einer anderen Funktion, die denselben Trick verwendet).

In https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh (lokaler Gültigkeitsbereich für Variablen und Optionen für POSIX-Shells) erfahren Sie, wie ein Stapel implementiert wird, der darauf aufbaut.

In jeder Shell können Sie Subshells verwenden, um den Umfang der Optionen einzuschränken

$ sh -c 'f() (set -x; echo test); f; echo no trace'
+ echo test
test
no trace

Dies schränkt jedoch den Umfang von allem ein (Variablen, Funktionen, Aliase, Umleitungen, aktuelles Arbeitsverzeichnis ...), nicht nur Optionen.

Stéphane Chazelas
quelle
bash 4.4 erschien vor 4 Tagen (16. September 2016), daher müssen Sie es wahrscheinlich selbst neu kompilieren, aber warum nicht
edi9999
Es scheint mir, dass dies oldstate=$(set +o)eine einfachere (und POSIX) Möglichkeit ist, alle Optionen zu speichern.
Sorontar
@sorontar, ein guter Punkt, der jedoch bei Shell-Implementierungen wie pdksh/ mksh(und anderen pdksh-Derivaten) nicht funktioniert oder bei zshdenen set +onur die Abweichungen von den Standardeinstellungen ausgegeben werden. Das würde für Bash / Dash / Yash funktionieren, ist aber nicht portabel.
Stéphane Chazelas
13

Sie können die $-Variable am Anfang lesen , um zu sehen, ob sie -xgesetzt ist oder nicht, und sie dann in einer Variablen speichern, z

if [[ $- == *x* ]]; then
  was_x_set=1
else
  was_x_set=0
fi

Aus dem Bash-Handbuch :

($ -, ein Bindestrich.) Wird durch den Befehl set builtin oder durch die Shell selbst (z. B. die Option -i) auf die beim Aufruf angegebenen aktuellen Optionsflags erweitert.

Dawid Grabowski
quelle
7
[[ $SHELLOPTS =~ xtrace ]] && wasset=1
set -x
echo rest of your script
[[ $wasset -eq 0 ]] && set +x

In der Bash wird $ SHELLOPTS mit den Flags gesetzt, die aktiviert sind. Überprüfen Sie dies, bevor Sie xtrace einschalten, und setzen Sie xtrace nur zurück, wenn es zuvor ausgeschaltet war.

Jeff Schaller
quelle
5

Um nur das Offensichtliche anzugeben, wenn set -xes für die Dauer des Skripts gültig sein soll und dies nur eine vorübergehende Testmaßnahme ist (nicht permanent Teil der Ausgabe sein soll), rufen Sie entweder das Skript mit der -xOption auf, z.

$ bash -x path_to_script.sh

... oder ändern Sie vorübergehend das Skript (erste Zeile), um die Debug-Ausgabe zu aktivieren, indem Sie die folgende -xOption hinzufügen :

#!/bin/bash -x
...rest of script...

Mir ist klar, dass dies wahrscheinlich ein zu breiter Strich für das ist, was Sie wollen, aber es ist der einfachste und schnellste Weg, um das Skript zu aktivieren / deaktivieren, ohne das Skript mit temporären Dingen zu komplizieren, die Sie wahrscheinlich sowieso entfernen möchten (meiner Erfahrung nach).

michael
quelle
3

Dies bietet Funktionen zum Speichern und Wiederherstellen der Flags, die über den POSIX- $-Spezialparameter sichtbar sind . Wir verwenden die localErweiterung für lokale Variablen. In einem POSIX-konformen portablen Skript werden globale Variablen verwendet (kein localSchlüsselwort):

save_opts()
{
  echo $-
}

restore_opts()
{
  local saved=$1
  local on
  local off=$-

  while [ ${#saved} -gt 0 ] ; do
    local rest=${saved#?}
    local first=${saved%$rest}

    if echo $off | grep -q $first ; then
      off=$(echo $off | tr -d $first)
    fi

    on="$on$first"
    saved=$rest
  done

  set ${on+"-$on"} ${off+"+$off"}
}

Dies wird ähnlich verwendet, wie Interrupt-Flags im Linux-Kernel gespeichert und wiederhergestellt werden:

Shell:                                Kernel:

flags=$(save_opts)                    long flags;
                                      save_flags (flags);

set -x  # or any other                local_irq_disable(); /* disable irqs on this core */

# ... -x enabled ...                  /* ... interrupts disabled ... */

restore_opts $flags                   restore_flags(flags);

# ... x restored ...                  /* ... interrupts restored ... */

Dies funktioniert nicht für die erweiterten Optionen, die nicht in der $-Variablen enthalten sind.

Ich habe gerade bemerkt, dass POSIX das hat, wonach ich gesucht habe: Das +oArgument von setist keine Option, sondern ein Befehl, der eine Reihe von Befehlen evalausgibt, die, wenn -ed, die Optionen wiederherstellen. Damit:

flags=$(set +o)

set -x

# ...

eval "$flags"

Das ist es.

Ein kleines Problem ist, dass, wenn die -xOption vorher evalaktiviert wird, eine hässliche Flut von set -oBefehlen zu sehen ist. Um dies zu beseitigen, können wir Folgendes tun:

set +x             # turn off trace not to see the flurry of set -o.
eval "$flags"      # restore flags
Kaz
quelle
1

Sie können eine Unterschale verwenden.

(
   set 
   do stuff
)
Other stuff, that set does not apply to
Strg-Alt-Delor
quelle