我对使用单括号或双括号感到困惑。请看以下代码:但是当我将其更改为单括号时:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi


它说:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi


当我更改它时到:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected


工作正常。有人可以解释发生了什么吗?何时应在诸如"${var}"之类的变量周围分配双引号以防止空格引起的问题?

评论

Bash常见问题解答31

一个更通用的vs问题:unix.stackexchange.com/questions/306111/…

#1 楼

单括号[实际上是test命令的别名,它不是语法。
单括号(很多)的缺点之一是,如果它试图评估的一个或多个操作数返回一个空字符串,它将抱怨它期望两个操作数(二进制)。这就是为什么人们看到[ x$foo = x$blah ]的原因,而x则保证了操作数绝不会为空字符串。
另一方面,双括号[[ ]]是语法,并且比[ ]更强大。如您所见,它不存在“缺少操作数”的问题,并且还允许>, <, >=, <=, !=, ==, &&, ||运算符使用更多类似C的语法。
我的建议如下:如果您的解释器是#!/bin/bash,则始终使用[[ ]]
请注意,并非所有POSIX外壳程序都支持[[ ]],但是除zsh之外,许多外壳程序都支持kshbash

评论


空字符串(以及许多其他问题)通过使用引号解决。 “ x”是另一种问题:其中操作数可以用作运算符。就像$ foo是!或(或-n ...这个问题并不意味着POSIX shell的问题([和]旁边)不大于4。

–StéphaneChazelas
2013年1月25日20:36



为了澄清,[x $ foo = x $ blah]与[$ foo = $ bar]一样错误。 [“ $ foo” =“ $ bar”]在任何符合POSIX的shell中都是正确的,[“ x $ foo” =“ x $ bar”]在任何类似Bourne的shell中都可以使用,但是x不适用于您有空字符串,但是$ foo可能在哪里!或-n ...

–StéphaneChazelas
2013年3月7日16:29



这个答案中有趣的语义。我不确定您的意思是* not *语法。当然,[并不是测试的确切别名。如果是这样,则测试将接受一个封闭的方括号。测试-n foo]。但是测试没有,并且[需要一个。 [在所有其他方面都与test相同,但这不是别名的工作原理。 Bash将[和测试描述为shell内置函数,但是[[描述为shell关键字。

– kojiro
17-10-9在13:02

好吧,在bash中[是内置的shell,但/ usr / bin / [也是可执行文件。传统上,它是/ usr / bin / test的链接,但在现代gnu中,coreutils是单独的二进制文件。传统版本的测试会检查argv [0],以查看是否将其作为[调用,然后寻找匹配的]。

–埃文
19年1月9日在20:03

我的bash不适用于> =和<=

–学生
19年7月22日在13:15

#2 楼

[命令是普通命令。尽管大多数外壳程序将其作为效率的内置组件提供,但它遵循外壳程序的常规语法规则。 [test完全等效,不同之处在于[要求]作为最后一个参数,而test不需要。

双括号[[ … ]]是特殊语法。它们是在ksh中([之后的数年)引入的,因为[可能难以正确使用,并且[[允许添加一些使用shell特殊字符的新功能。例如,您可以编写

[[ $x = foo && $y = bar ]]


,因为整个条件表达式都是由Shell解析的,而[ $x = foo && $y = bar ]首先会分成两个命令[ $x = foo$y = bar ],并由&&分隔操作员。类似地,双括号使诸如模式匹配语法之类的东西成为可能。 [[ $x == a* ]]测试x的值是否以a开头;在单括号中,这会将a*扩展到当前目录中名称以a开头的文件列表。双括号最早是在ksh中引入的,仅在ksh,bash和zsh中可用。

在单括号内,您需要像在其他大多数地方一样在变量替换周围使用双引号,因为它们是只是命令的参数(恰好是[命令)。在双括号内,您不需要双引号,因为外壳程序不进行单词拆分或加修饰:它解析的是条件表达式,而不是命令。

[[ $var1 = "$var2" ]]是一个例外如果要进行字节对字符串的比较,则用引号引起来,否则,$var2将成为匹配$var1的模式。

[[ … ]]不能做的一件事是使用变量作为操作员。例如,这是完全合法的(但很少使用):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi
…
if [ "$x" "$op" "$y" ]; then …


在您的示例中

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then …


if内部的命令是[,带有4个参数-d/home/mazimi/VirtualBoxVMs]。 Shell解析-d /home/mazimi/VirtualBox,然后不知道如何处理VMs。为了获得格式正确的命令,您需要防止在${dir}上进行单词拆分。

通常来说,除非您知道要在菜单上执行单词拆分和globing,否则始终在变量和命令替换周围使用双引号。结果。安全使用双引号的主要地方是:


在赋值中:foo=$bar(但请注意,在export "foo=$bar"或数组赋值(例如array=("$a" "$b")中)确实需要双引号) ;
case语句中:case $foo in …;
在双括号内,但===运算符的右侧除外(除非您想要模式匹配):[[ $x = "$y" ]]

在所有这些中,使用双引号是正确的,因此您最好跳过高级规则并始终使用引号。

评论


@StephaneChazelas我的一个错误。事实证明,[确实来自1981年的System III,我认为它更旧。

–吉尔斯'所以-不再是邪恶的'
13年7月7日在15:13

#3 楼


我什么时候应该在"${var}"之类的变量周围加上双引号,以防止空格引起的问题?

这个问题的隐含意义是

为什么${variable_name}不够好?


${variable_name}并不意味着您认为它会做什么...
...如果您认为它与空格引起的问题有关(以可变值表示) 。
${variable_name}对此非常有用:
 $ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food
 

其他什么都没有!1
${variable_name}不做好任何事情
,除非您立即在它后面加上一个可能是变量名称一部分的字符

字母(A-Za-z),下划线(_ )或一个数字(0-9)。
即使如此,您也可以解决它:
 $ echo "$bar"d
food
 

我并不想阻止其使用-
echo "${bar}d"可能是这里最好的解决方案—
但是要劝阻人们不要使用大括号而不是引号,或者本能地使用大括号然后问,
“现在,我需要还有引号吗?”
除非有充分的理由不要使用引号,否则
并且您确定自己知道自己在做什么。
_________________1当然,
例如,
${parameter:-[word]}
${parameter%[word]}的更高级形式的参数扩展基于${parameter}语法。
此外,您还需要使用等,
引用第10、11等位置参数-
引用将无济于事。 >

#4 楼

为了处理变量中的空格和空格|特殊字符,您应始终在其周围加上双引号。
设置适当的IFS也是一种好习惯。

推荐:
http://www.dwheeler.com/essays/filenames-in-shell.html