[[ ! (-z "") ]]
如果我以交互方式或非交互方式执行测试bash -c '[[ ! (-z "") ]]'
,我会获得相同的结果,即echo $?
给我1
。
但如果我忘记了上述表达式中的一个空格,得到[[ !(-z "") ]]
,我就不再得到相同的结果了。更准确地说,
set -x; [[ !(-z "") ]]; echo $?
给我
+ [[ -n !(-z ) ]]
+ echo 0
0
尽管
bash -c 'set -x; [[ !(-z "") ]]; echo $?'
给我
+ [[ -z '' ]]
+ echo 1
1
所以我的问题是:为什么交互式和非交互式 shell 不会给出相同的扩展,并且在这种情况下(或其他类似情况下)不会给出相同的结果?
答案1
这相当复杂。
首先请注意,通配符在 内部不起作用[[ ]]
。如果你运行类似
set -x; [[ / == /* ]]; echo $?
然后你会看到/*
那里并没有像 那样展开echo /*
。
现在有extglob
shell 选项。看起来在你的情况下它在交互式 shell 中启用,在非交互式 shell 中禁用。(比较在我的交互式 shell 中,bash 的“shopt extglob”在哪里打开?)
如果
extglob
使用内置命令启用了 shell 选项shopt
,则可以识别多个扩展模式匹配运算符。[…]
!(pattern-list)
匹配除给定模式之一之外的任意内容。
(来源)
运算符内部[[ ]]
被识别,但是没有展开。我怎么知道的?我可以让 shell 不识别它:
shopt -u extglob
set -x; [[ !(-z "") ]]; echo $?
现在我得到了-bash: !: event not found
。这表明!
有不同的含义:
!
开始历史替换,除非后面跟着空格、制表符、行尾=
或(
(当extglob
使用内置命令启用 shell 选项时shopt
)。
(来源)
交互式和非交互式 shell 之间存在以下差异:
当 shell 以交互方式运行时,它会通过多种方式改变其行为。
[…]
7. 命令历史记录[…]和历史记录扩展[…]默认启用。
(来源)
下一步是禁用历史记录:
shopt -u extglob
set +o history
set -x; [[ !(-z "") ]]; echo $?
在这里,结果就像在非交互式 shell 中一样。
我们可以反过来做吗?我们能让非交互式 shell 表现得像交互式 shell 吗?
首先让我们检查一下当强制新 shell 处于交互状态时会发生什么:
bash -ic 'set -x; [[ !(-z "") ]]; echo $?'
它的行为与预期一致,就像您的交互式 shell 一样。现在让我们启用extglob
非交互式 shell:
bash -c 'shopt -s extglob; set -x; [[ !(-z "") ]]; echo $?'
但不起作用!它仍然表现得像extglob
禁用了的非交互式 shell。原因很简单,但并不明显:在shopt
执行其工作之前,对整行执行了通配。当选项发生变化时已经太晚了。让我们将命令拆分为两行:
bash -c 'shopt -s extglob
set -x; [[ !(-z "") ]]; echo $?'
现在它的行为就像你的交互式 shell 一样。
最后一个谜团是:
+ [[ -n !(-z ) ]]
交互式 shell 中的是-n
从哪里来的?我的假设是基于[[ "random-string" ]]
被视为 的观察结果[[ -n "random-string" ]]
(用 进行检查set -x
)。我猜测当你运行[[ !(-z "") ]]
并extglob
启用时,操作符被识别但未被扩展的事实!( )
使得整个!(-z "")
字符串作为一个单词保留在那里,即你的命令变成了
set -x; [[ "!(-z )" ]]; echo $?
此命令(在交互式或非交互式 shell 中)的行为与您在交互式 shell 中运行的命令类似。