Answers:
要反转,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
设置了选项,最惯用的方法是:
[[ -o xtrace ]]
比其他两个类似的测试更好:
[[ $- =~ x ]]
[[ $- == *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
选项:
set +o
这是在POSIX标准描述如下:
+ o
将当前选项设置以适合于作为实现相同选项设置的命令重新输入到外壳的格式写入标准输出。
因此,只需:
oldstate=$(set +o)
将保留使用set
命令设置的所有选项的值。
同样,将选项恢复为其原始值只是执行变量的问题:
set +vx; eval "$oldstate"
这完全等同于使用Bash的shopt -po
。请注意,它不会涵盖所有可能的Bash选项,因为其中一些是由设置的shopt
。
shopt
bash中列出了许多其他shell选项:
$ 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
从5.3版开始,它也可以正常工作(遵循POSIX)。在以前的版本中,它仅部分遵循POSIX set +o
,它以适合于作为命令重新输入到Shell的格式打印选项,但仅用于设置选项(它不打印未设置的选项)。
mksh(以及相应的lksh)尚不能执行此操作(MIRBSD KSH R54 2016/11/11)。mksh手册包含以下内容:
在将来的版本中,set + o将符合POSIX的行为,并显示命令以恢复当前选项。
在bash中,set -e
(errexit
)的值在子shell内重置,这使得很难set +o
在$(…)子shell内捕获其值。
解决方法是,使用:
oldstate="$(set +o); set -$-"
使用Almquist Shell及其衍生版本(至少为dash
NetBSD / FreeBSD sh
)和 bash
4.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 { ...; }
语法定义的函数的情况(与用于其他shell(包括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 shell的选项)。
对于任何外壳,您都可以使用子外壳来限制选项的范围
$ sh -c 'f() (set -x; echo test); f; echo no trace'
+ echo test
test
no trace
但是,这不仅限制了选项,还限制了所有内容的范围(变量,函数,别名,重定向,当前工作目录...)。
oldstate=$(set +o)
是一种存储所有选项的更简单(和POSIX)方式。
pdksh
/ mksh
(和其他的pdksh的衍生物)或zsh
其中set +o
仅输出从默认设置的偏差。这将适用于bash / dash / yash,但不能移植。
您可以$-
在开始时读取变量以查看是否-x
已设置,然后将其保存到变量,例如
if [[ $- == *x* ]]; then
was_x_set=1
else
was_x_set=0
fi
从Bash手册:
($-,连字符。)扩展为调用时,由set Builtin命令或由shell本身设置的那些选项标志(如-i选项)。
[[ $SHELLOPTS =~ xtrace ]] && wasset=1
set -x
echo rest of your script
[[ $wasset -eq 0 ]] && set +x
在bash中,$ SHELLOPTS设置为带有已打开的标志。在打开xtrace之前先进行检查,并且仅在xtrace之前关闭时才将其重置。
这提供了保存和恢复通过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
您可以使用子外壳。
(
set …
do stuff
)
Other stuff, that set does not apply to