Bash 为什么狂欢节会这样;测试-n“;命令为$@(dollar at)位置参数给出错误的结果,而“!”;!测试-z";作品

Bash 为什么狂欢节会这样;测试-n“;命令为$@(dollar at)位置参数给出错误的结果,而“!”;!测试-z";作品,bash,shell,Bash,Shell,在bashtest表达式中使用$@会产生奇怪的结果 这里有一个最小的测试脚本来重现这个问题(在相同测试的替代配方中,这些配方没有错误地通过了测试): 前两个部分(“空”和“空格”)用于验证test、test-n和test-z工作正常;这就是混乱的程度 当$@表示“无参数”时,即当$#为零(0)时,会出现问题 三次测试运行显示了错误:请注意第三次运行中的“3.A3.NE-非空”报告: $ ./bash_weirdness.sh x -> args: 'x' --- empty --- 1.A

在bash
test
表达式中使用
$@
会产生奇怪的结果

这里有一个最小的测试脚本来重现这个问题(在相同测试的替代配方中,这些配方没有错误地通过了测试):

前两个部分(“空”和“空格”)用于验证
test
test-n
test-z
工作正常;这就是混乱的程度

$@
表示“无参数”时,即当
$#
为零(0)时,会出现问题

三次测试运行显示了错误:请注意第三次运行中的“3.A3.NE-非空”报告:

$ ./bash_weirdness.sh x
->
args: 'x'
--- empty ---
1.A2.EMPTY     - empty?
1.B1.NOT.TEST  - empty?
1.B3.NOT.NE    - empty?
--- space ---
2.A1.TEST      - not empty?
2.A3.NE        - not empty?
2.B2.NOT.EMPTY - not empty?
--- $@ ---
3.A1.TEST      - not empty?
3.A3.NE        - not empty?
3.B2.NOT.EMPTY - not empty?
--- $* ---
4.A1.TEST      - not empty?
4.A3.NE        - not empty?
4.B2.NOT.EMPTY - not empty?
这很好

$ ./bash_weirdness.sh ""
->
args: ''
--- empty ---
1.A2.EMPTY     - empty?
1.B1.NOT.TEST  - empty?
1.B3.NOT.NE    - empty?
--- space ---
2.A1.TEST      - not empty?
2.A3.NE        - not empty?
2.B2.NOT.EMPTY - not empty?
--- $@ ---
3.A2.EMPTY     - empty?
3.B1.NOT.TEST  - empty?
3.B3.NOT.NE    - empty?
--- $* ---
4.A2.EMPTY     - empty?
4.B1.NOT.TEST  - empty?
4.B3.NOT.NE    - empty?
这也很好

$ ./bash_weirdness.sh 
->
args: ''
--- empty ---
1.A2.EMPTY     - empty?
1.B1.NOT.TEST  - empty?
1.B3.NOT.NE    - empty?
--- space ---
2.A1.TEST      - not empty?
2.A3.NE        - not empty?
2.B2.NOT.EMPTY - not empty?
--- $@ ---
3.A2.EMPTY     - empty?
3.A3.NE        - not empty?
3.B1.NOT.TEST  - empty?
--- $* ---
4.A2.EMPTY     - empty?
4.B1.NOT.TEST  - empty?
4.B3.NOT.NE    - empty?

这一点都不好:请注意错误的“3.A3.NE”结果。

当将这一令人困惑的观察行为转化为可能适用于StackOVerflow的测试脚本和问题时,发生了一些事情,导致了这个“为什么”问题的答案

TL;答案见下文第5节;我描述了发现的整个过程,因为我(重新)学到了很多东西,我认为其他人也会遵循同样的,如果不是非常相似的,发现的道路。谷歌并不总是你的朋友

1.谷歌总失败率 首先,当你搜索
bash test$@奇怪的结果
或类似的查询时,“谷歌”没有提供任何东西,因为它去掉了最重要的
$@
。将
$@
改为
美元在
也没有吐出任何有用的东西,这让我相当担心。我甚至开始怀疑我正在运行的bash(Windows上的msysgitbash)

多亏了这次失败,我去查阅了这个
$@
的官方名称(行话)(我上一次阅读bash手册可能是在20年前;从那以后我一直在涉猎它,现在我因为没有学习我编写(少量)代码的语言而受到惩罚。)

2.名称为:“位置参数”<代码>$@(和
$*
) 另一轮搜索结果告诉我,
$@
(和
$*
-哦,对了,我已经忘记了你的一切!)是“位置参数”

3.
测试的确切行为是什么?
在这次搜索狂潮中,我还发现了这一点,它解释了
test
在参数数量可能不是您期望的数量时的行为

但是我确实在
$@
中引用了引号,不是吗? 所以它必须是
test
的单个参数,即使参数列表为空!?(
$#=0

(错!不是!)

测试
表达式求值规则集 复制上面链接中非常重要的部分(通过最小的编辑使之与这个问题相匹配)——这就是为什么我的大脑会点击一个获得预感,然后完全理解:

:

参数数规则
测试
内置,尤其是隐藏在其
[
名称下,可能看起来很简单,但实际上有时会造成很多麻烦。困难之一是
测试的行为不仅取决于其参数,而且取决于其参数的数量

以下是从手册中获取的规则(注意:这是针对命令
test
,对于
[
参数的数量是在没有最终
]
的情况下计算的,例如
[]
遵循“零参数”规则):

  • 0个参数

    表达式为
    false

  • 1个参数

    当且仅当参数不为null时,表达式为
    true

  • 2个参数

    如果第一个参数是
    (感叹号),则当且仅当第二个参数为null时,表达式为
    true

    如果第一个参数是上述语法规则(例如
    -n
    -z
    )下列出的一元条件运算符之一,则如果一元测试为
    true
    ,则表达式为
    true

    如果第一个参数不是有效的一元条件运算符,则表达式为
    false

  • 3个参数

    如果第二个参数是上述语法规则下列出的二进制条件运算符之一,则表达式的结果是使用第一个和第三个参数作为操作数的二进制测试的结果

    如果第一个参数是
    ,则该值是使用第二个和第三个参数对两个参数测试的求反

    如果第一个参数正好是
    ,第三个参数正好是
    ,则结果是第二个参数的一个参数测试。否则,表达式是
    false
    -a
    -o
    运算符在这种情况下被视为二进制运算符(注意:这意味着操作员
    -a
    在本例中不是文件操作员!)

  • 4个参数

    如果第一个参数是
    ,则结果是由其余参数组成的三个参数表达式的求反。否则,将使用上面列出的规则根据优先级分析和计算表达式

  • 5个或更多参数

    使用上面列出的规则,根据优先级解析和计算表达式

这些规则看似复杂,但在实践中并不是那么糟糕。了解它们可能有助于解释您可能遇到的一些“无法解释的”行为:

(注:下一节为parap)
$ ./bash_weirdness.sh 
->
args: ''
--- empty ---
1.A2.EMPTY     - empty?
1.B1.NOT.TEST  - empty?
1.B3.NOT.NE    - empty?
--- space ---
2.A1.TEST      - not empty?
2.A3.NE        - not empty?
2.B2.NOT.EMPTY - not empty?
--- $@ ---
3.A2.EMPTY     - empty?
3.A3.NE        - not empty?
3.B1.NOT.TEST  - empty?
--- $* ---
4.A2.EMPTY     - empty?
4.B1.NOT.TEST  - empty?
4.B3.NOT.NE    - empty?
 function test {
   if [ -n "$@" ] ; then echo "argument list is not empty"; fi
 }

 test
$ ./bash_weirdness.sh x y z
->
args: 'x y z'
--- empty ---
1.A2.EMPTY     - empty?
1.B1.NOT.TEST  - empty?
1.B3.NOT.NE    - empty?
--- space ---
2.A1.TEST      - not empty?
2.A3.NE        - not empty?
2.B2.NOT.EMPTY - not empty?
--- $@ ---
util/bash_weirdness.sh: line 25: test: y: binary operator expected
util/bash_weirdness.sh: line 26: test: too many arguments
util/bash_weirdness.sh: line 27: test: too many arguments
util/bash_weirdness.sh: line 28: test: y: binary operator expected
3.B1.NOT.TEST  - empty?
util/bash_weirdness.sh: line 29: test: too many arguments
3.B2.NOT.EMPTY - not empty?
util/bash_weirdness.sh: line 30: test: too many arguments
3.B3.NOT.NE    - empty?
--- $* ---
4.A1.TEST      - not empty?
4.A3.NE        - not empty?
4.B2.NOT.EMPTY - not empty?
test -n "$@"
test $# -gt 0
[[ $# -gt 0 ]]
(($# > 0))
(($#))
test -n "$*"
[[ -n $* ]]
$ test -n; echo $?
0
test STRING
test -n '-n'
args=("$@")

for arg in "${args[@]}"; do
    echo "$arg"
done