我想set -x
在脚本开始时“撤消”它(返回到设置之前的状态),而不是盲目设置+x
.这可能吗?
PS:我已经查过了这里;据我所知,这似乎没有回答我的问题。
答案1
抽象的
要反转 aset -x
只需执行 a set +x
。大多数时候,字符串的反转set -str
是带有+
:的同一个字符串set +str
。
一般来说,要恢复所有(请阅读下面有关 bash 的内容errexit
)shell 选项(使用命令更改set
),您可以执行以下操作(另请阅读下面有关 bashshopt
选项的内容):
oldstate="$(set +o)" # POSIXly store all set options.
.
.
set -vx; eval "$oldstate" # restore all options stored.
应该足够了,但是 bash 有两组通过set
(或shopt -po
) 访问的选项,还有一些通过shopt -p
.此外,bash 在进入子 shell 时不会保留set -e
(errexit)。请注意,扩展产生的选项列表$-
可能无法在 shell 中重新输入。
要捕获整个当前状态(在 bash 中),请使用:
oldstate="$(shopt -po; shopt -p)"; [[ -o errexit ]] && oldstate="$oldstate; set -e"
或者,如果您不介意设置inherit_errexit
标志(并且您的 bash ≥4.4):
shopt -s inherit_errexit; oldstate="$(shopt -po; shopt -p)"
更长的描述
巴什
这个命令:
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 ]]
这比其他两个类似的测试更好:
[[ $- =~ 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
用于存储所有选项的简单且符合 POSIX 标准的解决方案set
是:
set +o
这是POSIX 标准中描述作为:
+o
将当前选项设置写入标准输出,其格式适合作为实现相同选项设置的命令重新输入到 shell。
所以,简单地说:
oldstate=$(set +o)
将保留使用该命令设置的所有选项的值set
(在某些 shell 中)。
同样,将选项恢复为其原始值只需执行变量即可:
set +vx; eval "$oldstate"
这与使用 Bash 的shopt -po
.请注意,它会不是覆盖全部可能的重击选项,因为其中一些(仅)由shopt
.
bash 特殊情况
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"
bash 的set -e
特殊情况
set -e
在 bash 中, ( )的值errexit
在子 shell 内重置,这使得在set +o
$(...) 子 shell 内捕获其值变得困难。
作为解决方法,请使用:
oldstate="$(set +o)"; [[ -o errexit ]] && oldstate="$oldstate; set -e"
或者(如果它与您的目标不矛盾并且您的 bash 支持它)您可以使用该inherit_errexit
选项。
笔记:每个 shell 构建已设置或未设置的选项列表的方式略有不同(更不用说定义的不同选项),因此字符串不可在 shell 之间移植,但对同一 shell 有效。
zsh 特殊情况
zsh
从 5.3 版开始也可以正常工作(遵循 POSIX)。在以前的版本中,它仅部分遵循 POSIX,set +o
因为它以适合作为命令重新输入到 shell 的格式打印选项,但仅适用于放选项(它没有打印未设置选项)。
mksh 特殊情况
mksh(以及由此产生的 lksh)还不能做到这一点(MIRBSD KSH R54 2016/11/11)。 mksh 手册包含以下内容:
在未来的版本中,set +o 将表现出 POSIX 兼容并打印命令来恢复当前选项。
答案2
使用 Almquist shell 和衍生产品(至少是 dash
NetBSD/FreeBSD )和4.4 或更高版本,您可以使用以下命令将选项设置为函数的本地选项(如果您愿意,可以将变量设置为本地变量):sh
bash
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 在内的其他 shell 中使用的动态作用域相比,作用域是静态的):
$ 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
然而,有些 shell 会+ 2> /dev/null
在恢复时输出 a (你会看到痕迹case
当然,如果set -x
已经启用了该构造)。该方法也是不可重入的(就像您在调用自身的函数或使用相同技巧的另一个函数中执行此操作一样)。
看https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh(POSIX shell 的变量和选项的本地范围)了解如何实现围绕该问题的堆栈。
对于任何 shell,您都可以使用子 shell 来限制选项范围
$ sh -c 'f() (set -x; echo test); f; echo no trace'
+ echo test
test
no trace
但是,这限制了所有内容(变量、函数、别名、重定向、当前工作目录...)的范围,而不仅仅是选项。
答案3
您可以$-
在开头读取变量以查看是否-x
已设置,然后将其保存到变量中,例如
if [[ $- == *x* ]]; then
was_x_set=1
else
was_x_set=0
fi
来自bash手册:
($-,连字符。)扩展为调用时指定的当前选项标志、set 内置命令或 shell 本身设置的选项标志(例如 -i 选项)。
答案4
只是为了说明明显,如果set -x
要在脚本的持续时间内有效,这只是一个临时测试措施(不是输出的永久部分),那么要么调用带有选项的脚本-x
,例如,
$ bash -x path_to_script.sh
...或者,通过添加选项临时更改脚本(第一行)以启用调试输出-x
:
#!/bin/bash -x
...rest of script...
我意识到这对于您想要的东西来说可能太宽泛了,但这是启用/禁用的最简单和最快的方法,而不会使用您可能想要删除的临时内容使脚本过于复杂(根据我的经验)。