为什么 bash 测试对空格如此挑剔?

Kon*_*ner 10 shell bash whitespace test

作为一名主要的 Java 程序员,我发现 bash if-then构造非常令人困惑,尤其是在空格方面。谁能解释为什么第一个有效,而第二个或第三个无效?

#works
if [ -n $1 ]; then echo "parameterized"; fi

#output: ./links: Zeile 1: [-n: Kommando nicht gefunden.
if [-n $1 ]; then echo "parameterized"; fi

#output: ./links: Zeile 1: [: Fehlende `]'
if [ -n $1]; then echo "parameterized"; fi
Run Code Online (Sandbox Code Playgroud)

Gil*_*il' 20

不一致主要是由于历史原因。

括号作为条件命令的使用是在通配符模式中使用括号之后出现的。所以在当时[ -n foo ]出现的时候,[foo]就已经意味着“一个名为f或的文件o”。虽然在实践中很少使用方括号运算符会与通配符中方括号的实际使用发生冲突,但作者选择不冒险通过更改语法来破坏现有脚本。这种设计选择也使实现更容易:最初[是作为外部命令实现的,如果后面的空格[是可选的,则无法实现。(现代系统仍然有[一个外部命令,但几乎所有现代 shell 都内置了它。)

出于类似的原因,您的“工作”代码在大多数情况下实际上是不正确的。$1并不意味着“参数 1 的值”:它的意思是“取参数 1 的值,在空格处将其拆分为单独的单词(或任何值IFS),并将每个单词解释为一个全局模式”。“参数1的值”的写法需要双引号:"$1". 请参阅何时需要双引号?对于细节。你不需要理解这一点;您需要记住的是:始终在变量替换"$foo"和命令替换周围加上双引号"$(foo)"。因此:

if [ -n "$1" ]; then …
Run Code Online (Sandbox Code Playgroud)

在 bash、ksh 和 zsh 中,但不是在普通 sh 中,您也可以使用双括号。单括号[ … ]像普通命令一样被解析(确实[是一个普通命令,尽管通常是内置的)。双括号是一个单独的语法结构,大多数情况下您可以省略双引号。

if [[ -n $1 ]]; then …
Run Code Online (Sandbox Code Playgroud)

但是,有一个例外:[[ "$foo" = "$bar" ]]要测试两个变量是否具有相同的值需要在 周围用双引号引起来$bar,否则$bar被解释为通配符模式。同样,与其记住细节,还不如一直使用双引号。


Kiw*_*iwy 11

事实上,这个字符[是一个内置的 shell 命令,它会评估参数,直到参数]
bash 使用由空格分隔的参数,所以如果你想调用[你需要放一个空格,否则它会尝试执行[-n不是函数或二进制文件的命令 bash 知道(至少在你自己创建一个之前)。

当你写[-n $1 ]这意味着
[-n与参数$1]它没有任何意义
,如果你把[ -n $1]它意味着
[用争论-n$1],但缺少了]你的函数的参数[,因此无法计算表达式