Jak przywrócić wartość opcji powłoki, takich jak `set -x`?


47

Chcę set -xna początku skryptu i „cofnąć” go (wrócić do stanu, zanim go ustawię), zamiast ślepego ustawiania +x. czy to możliwe?

PS: Już tu sprawdziłem ; to nie wydawało się odpowiadać na moje pytanie, o ile mogłem powiedzieć.

Odpowiedzi:


59

Abstrakcyjny

Aby odwrócić po set -xprostu wykonaj a set +x. Większość czasu, odwrotnością ciąg set -strjest taki sam ciąg z +: set +str.

Ogólnie, aby przywrócić wszystkie errexitopcje powłoki (czytaj poniżej o bash ) (zmienione za pomocą setkomendy), możesz zrobić (przeczytaj również o shoptopcjach bash ):

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

Dłuższy opis

grzmotnąć

To polecenie:

shopt -po xtrace

wygeneruje łańcuch wykonywalny, który odzwierciedla stan opcji. Te pśrodki flag wydrukować, a ookreśla flag że pytamy o opcji (s) ustawianą przez setkomendy (w przeciwieństwie do opcji zestawu (ów) przez shoptkomendę). Możesz przypisać ten ciąg do zmiennej i wykonać zmienną na końcu skryptu, aby przywrócić stan początkowy.

# 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"

To rozwiązanie działa jednocześnie dla wielu opcji:

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"

Dodanie set +vxpozwala uniknąć drukowania długiej listy opcji.


A jeśli nie podasz żadnych nazw opcji,

oldstate="$(shopt -po)"

daje wartości wszystkich opcji. A jeśli pominiesz oflagę, możesz zrobić to samo z shoptopcjami:

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

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

Jeśli chcesz sprawdzić, czy setopcja jest ustawiona, najbardziej idiomatycznym (Bash) sposobem jest:

[[ -o xtrace ]]

co jest lepsze niż dwa pozostałe podobne testy:

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

W przypadku dowolnego testu działa to:

# 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"

Oto jak przetestować stan shoptopcji:

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

Proste, zgodne z POSIX rozwiązanie do przechowywania wszystkich setopcji to:

set +o

który jest opisany w standardzie POSIX jako:

+ o

    Zapisz bieżące ustawienia opcji na standardowe wyjście w formacie odpowiednim do ponownego włożenia do powłoki jako polecenia, które osiągają te same ustawienia opcji.

Po prostu:

oldstate=$(set +o)

zachowa wartości dla wszystkich opcji ustawionych za pomocą setpolecenia.

Ponownie przywrócenie opcji do ich oryginalnych wartości jest kwestią wykonania zmiennej:

set +vx; eval "$oldstate"

Jest to dokładnie równoważne z używaniem Basha shopt -po. Pamiętaj, że nie obejmie wszystkich możliwych opcji Bash , ponieważ niektóre z nich są ustawione przez shopt.

bash special case

Istnieje wiele innych opcji powłoki wymienionych shoptw bash:

$ 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

Można je dołączyć do powyższej zmiennej i przywrócić w ten sam sposób:

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

Można to zrobić (dodaje $-się, aby zapewnić errexitzachowanie):

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

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

Uwaga : każda powłoka ma nieco inny sposób na zbudowanie listy opcji, które są ustawione lub wyłączone (nie wspominając o różnych zdefiniowanych opcjach), więc łańcuchy nie są przenośne między powłokami, ale są ważne dla tej samej powłoki.

skrzynka specjalna zsh

zshdziała również poprawnie (zgodnie z POSIX) od wersji 5.3. W poprzednich wersjach tylko częściowo stosował się do POSIX-a set +o, drukując opcje w formacie odpowiednim do ponownego wprowadzenia się do powłoki jako polecenia, ale tylko dla opcji ustawionych (nie drukował opcji nieustawionych ).

skrzynka specjalna mksh

Program mksh (i w konsekwencji lksh) nie jest jeszcze (MIRBSD KSH R54 2016/11/11) w stanie to zrobić. Podręcznik mksh zawiera to:

W przyszłej wersji set + o zachowa zgodność z POSIX i wyda polecenia, aby zamiast tego przywrócić bieżące opcje.

ustaw -e - przypadek specjalny

W bash wartość set -e( errexit) jest resetowana wewnątrz podpowłoki, co utrudnia uchwycenie jej wartości za set +opomocą podpowłoki $ (…).

Aby obejść ten problem, użyj:

oldstate="$(set +o); set -$-"

21

Dzięki powłoce Almquist i jej pochodnym ( przynajmniej dashNetBSD / FreeBSD sh) i bash4.4 lub nowszym możesz ustawić opcje lokalne dla funkcji za pomocą local -(ustaw $-zmienną lokalną, jeśli chcesz):

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

Nie dotyczy to plików źródłowych , ale możesz przedefiniować sourcesposób source() { . "$@"; }obejścia tego.

Za ksh88pomocą domyślnie zmiany opcji są lokalne dla funkcji. Z ksh93, dotyczy to tylko funkcji zdefiniowanych przy pomocy function f { ...; }składni (a zakres jest statyczny w porównaniu do zakresu dynamicznego używanego w innych powłokach, w tym ksh88):

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

W zshto jest zrobione z localoptionsopcją:

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

POSIXly możesz:

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

Jednak niektóre powłoki wygenerują a + 2> /dev/nullpo przywróceniu (i oczywiście zobaczysz ślad tej casekonstrukcji, jeśli set -xbył już włączony). Podejście to również nie jest ponownie uruchamiane (tak jak w przypadku funkcji, która wywołuje samą siebie lub innej funkcji używającej tej samej sztuczki).

Zobacz https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh (lokalny zakres dla zmiennych i opcji dla powłok POSIX), aby dowiedzieć się, jak zaimplementować stos, który działa wokół tego.

W dowolnej powłoce można użyć podpowłoki, aby ograniczyć zakres opcji

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

Ogranicza to jednak zakres wszystkiego (zmiennych, funkcji, aliasów, przekierowań, bieżącego katalogu roboczego ...), nie tylko opcji.


bash 4.4 wyszedł 4 dni temu (16 września 2016 r.), więc prawdopodobnie będziesz musiał go ponownie skompilować, ale dlaczego nie
edi9999

Wydaje mi się, że oldstate=$(set +o)jest to prostszy (i POSIX) sposób przechowywania wszystkich opcji.
sorontar

@sorontar, dobra uwaga, ale to nie działa w przypadku implementacji powłoki takich jak pdksh/ mksh(i inne pochodne pdksh) lub zshgdzie set +owypisuje jedynie odchylenia od ustawień domyślnych. To działałoby dla bash / dash / yash, ale nie byłoby przenośne.
Stéphane Chazelas

13

Możesz odczytać $-zmienną na początku, aby sprawdzić, czy -xjest ustawiona, czy nie, a następnie zapisać ją w zmiennej np

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

Z podręcznika Bash :

($ -, łącznik.) Rozwija się do bieżących flag opcji określonych podczas wywołania za pomocą wbudowanego polecenia set lub tych ustawionych przez samą powłokę (na przykład opcję -i).


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

W bash $ SHELLOPTS jest ustawiony przy włączonych flagach. Sprawdź to przed włączeniem xtrace i zresetuj xtrace tylko, jeśli wcześniej był wyłączony.


5

Aby powiedzieć oczywiste, jeśli set -xma obowiązywać przez czas trwania skryptu, a jest to tylko tymczasowy środek testowy (nie powinien być trwale częścią wyniku), wówczas albo wywołaj skrypt bez -xopcji, np.

$ bash -x path_to_script.sh

... lub tymczasowo zmień skrypt (pierwszy wiersz), aby włączyć wyjście debugowania, dodając -xopcję:

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

Zdaję sobie sprawę, że prawdopodobnie jest to zbyt szeroki skok do tego, czego chcesz, ale jest to najprostszy i najszybszy sposób włączania / wyłączania, bez nadmiernego komplikowania skryptu tymczasowymi rzeczami, które prawdopodobnie i tak chcesz usunąć (z mojego doświadczenia).


3

Zapewnia to funkcje do zapisywania i przywracania flag widocznych poprzez $-specjalny parametr POSIX . Używamy localrozszerzenia dla zmiennych lokalnych. W przenośnym skrypcie zgodnym z POSIX używane byłyby zmienne globalne (bez localsłowa kluczowego):

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"}
}

Jest to używane podobnie do sposobu zapisywania i odtwarzania flag przerwań w jądrze Linux:

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 ... */

To nie zadziała dla żadnej z rozszerzonych opcji, które nie są uwzględnione w $-zmiennej.

Właśnie zauważyłem, że POSIX ma to, czego szukałem: +oargumentem setnie jest opcja, ale polecenie, które zrzuca wiązkę poleceń, które jeśli eval-ed przywróci opcje. Więc:

flags=$(set +o)

set -x

# ...

eval "$flags"

Otóż ​​to.

Jednym małym problemem jest to, że jeśli -xopcja zostanie wcześniej zmieniona eval, pojawi się brzydka seria set -opoleceń. Aby to wyeliminować, możemy wykonać następujące czynności:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.