为什么 bash 和 zsh 将 [-1] 计算为 1?

为什么 bash 和 zsh 将 [-1] 计算为 1?

我注意到了这种令人惊讶的行为并且不太明白发生了什么:

$ bash -c 'echo [-1]'
1

$ zsh -c 'echo [-1]'
1

命令解析器似乎试图评估一个表达式:

$ echo [1,2,3]
1
$ echo [123-33]
1

因为如果表达式被引用,或者表明括号只是一个括号,那么奇怪的评估就会被省略。

$ echo \[123-33]
[123-33]

$ echo [123-33\]
[123-33]

$ echo '[123-33]'
[123-33]

在寻找线索时,我发现了这句话:

https://github.com/mvdan/sh

注意事项

索引 Bash 关联数组时,请始终使用引号。否则,静态解析器将不得不假定索引是算术表达式。

$ echo '${array[spaced string]}' | shfmt
1:16: not a valid arithmetic operator: string
$ echo '${array[dash-string]}' | shfmt
${array[dash - string]}

不确定该怎么做,因为在上下文中没有数组可言echo [1-1]

这当然可能是由我自己的环境中的一些原因造成的,但当我检查时,似乎不是:

$ env -i HOME=$(mktemp -d) bash --noprofile --norc
bash-5.1$ echo [1-1]
1

$ env -i HOME=$(mktemp -d) /bin/bash-4.4  --noprofile --norc
bash-4.4-4.4$ echo [1-1]
1

但是另一方面,在 Ubuntu 上我得到了不同的结果:

$ bash --version | sed 1q; echo [1-1]
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
[1-1]

禁用所有选项不会改变此行为。

for i in `shopt | awk '/on$/ { print $1}'`; do shopt -u $i;done

您知道是什么原因导致这个括号表达式被求值以及为什么结果为 1 吗?

答案1

1仅当当前目录中有一个名为的文件时才会发生这种情况。

$ bash -c 'echo [-1]'
[-1]
$ touch 1
$ bash -c 'echo [-1]'
1
$ rm 1
$ bash -c 'echo [-1]'
[-1]

为什么?shell 会对[-1]匹配名为-或 的文件进行通配符1。因此,如果存在具有此类单字符名称的文件,则通配符将匹配并获取文件名。如果不存在此类文件,则通配符保持不变。

PS:它还适用于-

$ touch ./-
$ bash -c 'echo [-1]'
-

相关内容