`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我々はオプションで(S)セットについて尋ねていることをフラグが指定する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 ]]

これは、他の2つの同様のテストよりも優れています。

  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

すべてのsetオプションを保存する単純なPOSIX準拠のソリューションは次のとおりです。

set +o

POSIX標準では次のように記述されています。

+ o

    現在のオプション設定を、同じオプション設定を実現するコマンドとしてシェルに再入力するのに適した形式で標準出力に書き込みます。

だから、単純に:

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特殊ケース

zshバージョン5.3以降も(POSIXに準拠して)正しく動作します。以前のバージョンではset +o、コマンドとしてシェルに再入力するのに適した形式でオプションを印刷しましたが、設定オプションのみでオプションを印刷するという点で、POSIXに部分的にのみ従いました(未設定オプションは印刷しませんでした)。

mksh特殊ケース

mksh(および結果としてlksh)はまだこれを行うことができません(MIRBSD KSH R54 2016/11/11)。mkshマニュアルにはこれが含まれています。

将来のバージョンでは、set + oはPOSIX準拠で動作し、代わりに現在のオプションを復元するコマンドを出力します。

set -e特殊なケース

bashでは、set -eerrexit)の値はサブシェル内でリセットされるためset +o、$(…)サブシェル内で値をキャプチャすることは困難です。

回避策として、次を使用します。

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

21

Almquistシェルと派生物(少なくともdashNetBSD / 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

ではzshlocaloptionsオプションを使用してこれを行います。

$ 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復元時にaを出力します(既に有効になっている場合は、もちろんその構成のトレースが表示されます)。また、このアプローチはリエントラントではありません(自分自身を呼び出す関数または同じトリックを使用する別の関数で行う場合など)。caseset -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ので、おそらくあなたはそれを自分で再コンパイルする必要がありますが、なぜでしょう、(16-9月- 2016年)4日前に出てきた
edi9999

私にoldstate=$(set +o)は、すべてのオプションを保存するより簡単な(そしてPOSIX)方法であるように思えます。
sorontar

@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 builtinコマンドによる指定、またはシェル自体(-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"

それでおしまい。

1つの小さな問題は、-xこれより前にオプションをオンにするとevalset -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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.