Як відновити значення параметрів оболонки типу `set -x`?


47

Я хочу set -xна початку свого сценарію і "скасувати" його (повернутись до стану, перш ніж я його встановити), а не сліпо налаштувати +x. Чи можливо це?

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

Відповіді:


59

Анотація

Для повернення set -xпросто виконувати a set +x. Велика частина часу, зворотні по відношенню до рядка set -strодне і те ж рядок з +: set +str.

Загалом, для відновлення всіх (читайте нижче про bash errexit) параметрів оболонки (змінених setкомандою) ви могли б зробити (також читайте нижче про shoptпараметри bash ):

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"

Це рівнозначно використанню Баша shopt -po. Зауважте, що він не охоплюватиме всі можливі параметри Bash , оскільки деякі з них встановлені shopt.

баш особливий випадок

Є багато інших параметрів оболонки, перелічених shoptу 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

Їх можна додати до встановленої вище змінної та відновити таким же чином:

$ 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 особливий випадок

zshтакож працює правильно (дотримуючись POSIX) з версії 5.3. У попередніх версіях він дотримувався POSIX лише частково, set +oоскільки він друкував параметри у форматі, який був придатний для повторного введення в оболонку як команди, але тільки для встановлених параметрів (він не друкував невстановлені параметри).

mksh спеціальний випадок

Mksh (і, як наслідок, lksh) ще не може (MIRBSD KSH R54 2016/11/11) це зробити. Посібник з mksh містить це:

У майбутній версії set + o буде поводитись сумісно з POSIX та друкувати команди для відновлення поточних параметрів.

набір -е спеціальний кейс

В bash, значення set -e( errexit) скидається всередину під оболонок, що ускладнює захоплення його значення set +oвсередині $ (...) під оболонки.

В якості вирішення використовуйте:

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

21

За допомогою оболонки Almquist та похідних ( dash, shпринаймні , NetBSD / FreeBSD ) та 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

Однак деякі оболонки видадуть a + 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 :

($ -, дефіс.) Розширюється на поточні прапори опцій, як зазначено при виклику, набором вбудованої команди або на ті, які встановлює сама оболонка (наприклад, опція -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воно має діяти протягом тривалості сценарію, і це лише тимчасовий захід тестування (не бути постійною частиною виводу), а потім або викликати сценарій w / -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

1

Ви можете використовувати підкожу.

(
   set 
   do stuff
)
Other stuff, that set does not apply to
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.