Как восстановить значение параметров оболочки, таких как `set -x`?

47

Я хочу set -xв начале своего сценария и «отменить» его (вернуться в состояние до того, как я его установлю) после установки вслепую +x. Это возможно?

PS: я уже проверил здесь ; это, кажется, не отвечало на мой вопрос, насколько я мог сказать.

Рони Майкл
источник

Ответы:

59

Абстрактные

Чтобы повернуть set -xпросто выполнить set +x. Большая часть времени, обратные по отношению к строке set -strодно и то же строка с +: set +str.

В общем, чтобы восстановить все (читайте ниже о bash errexit) параметры оболочки (измененные с помощью setкоманды), которые вы можете сделать (также читайте ниже о bash shopt):

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

Более длинное описание

удар

Эта команда:

shopt -po xtrace

сгенерирует исполняемую строку, которая отражает состояние опции. На pсредства флаг печати, а oфлаг указывает , что мы спрашиваем о вариант (ы) , задаваемой setкомандой (в отличие от опции (ы) набора по shoptкоманде). Вы можете присвоить эту строку переменной и выполнить переменную в конце вашего скрипта, чтобы восстановить начальное состояние.

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

Это решение работает для нескольких вариантов одновременно:

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"

Добавление set +vxпозволяет избежать печати длинного списка опций.


И, если вы не перечислите имена опций,

oldstate="$(shopt -po)"

это дает вам значения всех вариантов. И, если вы оставите oфлаг, вы можете сделать то же самое с shoptопциями:

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

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

Если вам нужно проверить, установлена ​​ли setопция, самый идиоматический (Bash) способ сделать это:

[[ -o xtrace ]]

что лучше, чем два других похожих теста:

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

С любым из тестов это работает:

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

Вот как можно проверить состояние shoptопции:

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

Простое POSIX-совместимое решение для хранения всех setопций:

set +o

который описан в стандарте POSIX как:

+ о

    Запишите текущие настройки параметров в стандартный вывод в формате, подходящем для повторного ввода в оболочку, в качестве команд, которые достигают тех же настроек параметров.

Итак, просто:

oldstate=$(set +o)

сохранит значения для всех параметров, установленных с помощью setкоманды.

Опять же, восстановление параметров до их исходных значений - это выполнение переменной:

set +vx; eval "$oldstate"

Это в точности эквивалентно использованию Bash shopt -po. Обратите внимание, что он не охватывает все возможные параметры Bash , так как некоторые из них установлены shopt.

особый случай

В shoptbash есть много других параметров оболочки :

$ 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

Они могут быть добавлены к указанной выше переменной и восстановлены таким же образом:

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

Это можно сделать ( $-добавляется для обеспечения errexitсохранения):

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

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

Примечание : каждая оболочка имеет немного другой способ построения списка параметров, которые установлены или не установлены (не говоря уже о различных параметрах, которые определены), поэтому строки не переносимы между оболочками, но действительны для одной и той же оболочки.

зш особый случай

zshтакже работает правильно (в соответствии с POSIX) начиная с версии 5.3. В предыдущих версиях он следовал POSIX только частично, set +oв том смысле, что он печатал параметры в формате, который был пригоден для повторного ввода в оболочку в качестве команд, но только для заданных параметров (он не печатал не заданные параметры).

мкш особый случай

Мкш (и, как следствие, лкш) пока не может (MIRBSD KSH R54 2016/11/11) сделать это. Инструкция МКШ содержит:

В будущей версии set + o будет вести себя как POSIX-совместимый и печатать команды для восстановления текущих параметров.

установить -e особый случай

В bash значение set -e( errexit) сбрасывается внутри вложенных оболочек, что затрудняет захват его значения set +oвнутри вложенной оболочки $ (…).

В качестве обходного пути используйте:

oldstate="$(set +o); set -$-"
sorontar
источник
21

С помощью оболочки и производных Almquist ( по крайней мере dash, NetBSD / FreeBSD sh) и bash4.4 или выше вы можете сделать опции локальными для функции с помощью local -(сделайте $-переменную локальной, если хотите):

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

Это не относится к , источником файлов, но вы можете переопределить , sourceкак source() { . "$@"; }работать вокруг этого.

При этом ksh88изменения параметров являются локальными для функции по умолчанию. С ksh93, это только в случае функций, определенных с function f { ...; }синтаксисом (и область видимости является статической по сравнению с динамической областью видимости, используемой в других оболочках, включая ksh88):

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

В zsh, это сделано с localoptionsопцией:

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

POSIXly, вы можете сделать:

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

Однако некоторые оболочки будут выводить + 2> /dev/nullпри восстановлении (и вы , конечно, увидите след этой caseконструкции, если set -xона уже была включена). Этот подход также не является повторным (например, если вы делаете это в функции, которая вызывает себя или другую функцию, которая использует тот же трюк).

См. Https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh (локальная область видимости для переменных и параметров для оболочек POSIX), чтобы узнать, как реализовать стек, который работает вокруг этого.

С любой оболочкой вы можете использовать подоболочки, чтобы ограничить область действия опций

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

Однако это ограничивает область действия всего (переменные, функции, псевдонимы, перенаправления, текущий рабочий каталог ...), а не только параметры.

Стефан Шазелас
источник
bash 4.4 вышел 4 дня назад (16 сентября 2016 года), так что, вероятно, вам придется перекомпилировать его самостоятельно, но почему бы и нет
edi9999
Мне кажется, что oldstate=$(set +o)это более простой (и POSIX) способ хранения всех опций.
Соронтар
@sorontar, хороший момент, хотя это не работает для реализаций оболочки, таких как pdksh/ mksh(и другие производные pdksh) или zshгде выводятся set +oтолько отклонения от настроек по умолчанию. Это будет работать для bash / dash / yash, но не будет переносимым.
Стефан Шазелас
13

Вы можете прочитать $-переменную в начале, чтобы увидеть, -xустановлена ​​она или нет, а затем сохранить ее в переменной, например.

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

Из руководства Bash :

($ -, дефис.) Расширяется до флагов текущей опции, как указано при вызове, с помощью встроенной команды set или тех, которые установлены самой оболочкой (например, опция -i).

Давид Грабовски
источник
7
[[ $SHELLOPTS =~ xtrace ]] && wasset=1
set -x
echo rest of your script
[[ $wasset -eq 0 ]] && set +x

В bash $ SHELLOPTS устанавливается с включенными флагами. Проверьте это, прежде чем включать xtrace, и сбросьте xtrace, только если он был выключен ранее.

Джефф Шаллер
источник
5

Просто чтобы заявить очевидное, если set -xоно должно действовать в течение всего сценария, а это всего лишь временная мера тестирования (не быть постоянно частью вывода), то либо вызовите сценарий с -xпараметром, например,

$ bash -x path_to_script.sh

... или временно измените сценарий (первая строка), чтобы включить отладочный вывод, добавив -xпараметр:

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

Я понимаю, что это, вероятно, слишком широкий ход для того, что вы хотите, но это самый простой и быстрый способ включить / отключить, не слишком усложняя сценарий временными вещами, которые вы, вероятно, захотите удалить в любом случае (по моему опыту).

Майкл
источник
3

Это предоставляет функции для сохранения и восстановления флагов, которые видны через $-специальный параметр POSIX . Мы используем localрасширение для локальных переменных. В переносимом скрипте, соответствующем POSIX, будут использоваться глобальные переменные (без localключевого слова):

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

Это используется аналогично тому, как флаги прерываний сохраняются и восстанавливаются в ядре 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 ... */

Это не будет работать ни для одной из расширенных опций, которые не включены в $-переменную.

Просто заметил, что у POSIX есть то, что я искал: +oаргумент setне является опцией, а командой, которая выводит кучу команд, которые, если eval-ed, восстановят опции. Так:

flags=$(set +o)

set -x

# ...

eval "$flags"

Вот и все.

Одна небольшая проблема заключается в том, что если -xопция была включена до этого eval, появляется ужасный поток set -oкоманд. Чтобы устранить это, мы можем сделать следующее:

set +x             # turn off trace not to see the flurry of set -o.
eval "$flags"      # restore flags
Kaz
источник
1

Вы можете использовать вложенную оболочку.

(
   set 
   do stuff
)
Other stuff, that set does not apply to
Ctrl-Alt-Делор
источник
0

По аналогии с тем, что сказал @Jeff Shaller, но установил НЕ повторять эту часть посередине (это то, что мне было нужно):

OPTS=$SHELLOPTS ; set +x
echo 'message' # or whatever
[[ $OPTS =~ xtrace ]] && set -x
Ли Меадор
источник