抽象的

抽象的

我想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 ]]

这比其他两个类似的测试更好:

  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 标准中描述作为:

+o

    将当前选项设置写入标准输出,其格式适合作为实现相同选项设置的命令重新输入到 shell。

所以,简单地说:

oldstate=$(set +o)

将保留使用该命令设置的所有选项的值set(在某些 shell 中)。

同样,将选项恢复为其原始值只需执行变量即可:

set +vx; eval "$oldstate"

这与使用 Bash 的shopt -po.请注意,它会不是覆盖全部可能的重击选项,因为其中一些(仅)由shopt.

bash 特殊情况

shoptbash 中列出了许多其他 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 和衍生产品(至少是 dashNetBSD/FreeBSD )和4.4 或更高版本,您可以使用以下命令将选项设置为函数的本地选项(如果您愿意,可以将变量设置为本地变量):shbashlocal -$-

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

这不适用于来源文件,但您可以重新定义sourcesource() { . "$@"; }解决这个问题。

使用 时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...

我意识到这对于您想要的东西来说可能太宽泛了,但这是启用/禁用的最简单和最快的方法,而不会使用您可能想要删除的临时内容使脚本过于复杂(根据我的经验)。

相关内容