我对使用单括号或双括号感到困惑。看这段代码:
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
当我将其更改为:
dir="/home/mazimi/VirtualBox VMs"
if [ -d "${dir}" ]; then
echo "yep"
fi
效果很好。有人可以解释发生了什么事吗?我什么时候应该在变量周围分配双引号,例如"${var}"
防止空格引起的问题?
答案1
单括号[
实际上是命令的别名test
,它是不是句法。
单括号的缺点之一是,如果它尝试计算的一个或多个操作数返回空字符串,它会抱怨它需要两个操作数(二进制)。这就是为什么你看到人们这样做[ x$foo = x$blah ]
,x
保证操作数永远不会计算为空字符串。
[[ ]]
另一方面,双括号是语法并且比 更有能力[ ]
。正如您所发现的,它不存在“缺少操作数”的问题,并且还允许使用更多类似 C 的运算符语法>, <, >=, <=, !=, ==, &&, ||
。
我的建议如下:如果您的口译员是#!/bin/bash
,那么总是使用[[ ]]
值得注意的是,并非[[ ]]
所有 POSIX shell 都支持它,但是许多 shell 确实支持它,zsh
例如ksh
bash
答案2
该[
命令是普通命令。尽管大多数 shell 将其作为内置函数提供以提高效率,但它遵循 shell 的正常语法规则。[
与 完全相同test
,只是[
需要 a]
作为其最后一个参数,但test
不需要。
双括号[[ … ]]
是特殊语法。它们是在 ksh 中引入的(几年后[
),因为[
使用起来很麻烦,并且[[
允许使用 shell 特殊字符的一些新添加。例如,您可以编写
[[ $x = foo && $y = bar ]]
因为整个条件表达式是由 shell 解析的,而 while[ $x = foo && $y = bar ]
首先会被分成两个命令[ $x = foo
并由运算符$y = bar ]
分隔&&
。类似地,双括号可以实现诸如模式匹配语法之类的功能,例如[[ $x == a* ]]
测试 的值是否以;x
开头。a
在单括号中,这将扩展到当前目录中a*
名称开头的文件列表。a
双括号首先在 ksh 中引入,并且仅在 ksh、bash 和 zsh 中可用。
在单括号内,您需要在变量替换周围使用双引号,就像在大多数其他地方一样,因为它们只是命令的参数(恰好是命令[
)。在双括号内,您不需要双引号,因为 shell 不执行分词或通配符:它正在解析条件表达式,而不是命令。
但一个例外是,[[ $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/VirtualBox
,VMs
和]
。 shell 解析-d /home/mazimi/VirtualBox
后不知道如何处理VMs
.您需要防止分词${dir}
才能获得格式正确的命令。
一般来说,始终在变量和命令替换周围使用双引号,除非您知道要对结果执行分词和通配符。不使用双引号是安全的主要地方是:
- 在赋值中:(但请注意,在或在数组赋值中
foo=$bar
需要双引号,例如);export "foo=$bar"
array=("$a" "$b")
- 在
case
声明中:case $foo in …
; =
在双括号内,除了or运算符的右侧==
(除非您确实需要模式匹配):[[ $x = "$y" ]]
.
在所有这些中,使用双引号是正确的,因此您不妨跳过高级规则并始终使用引号。
答案3
我什么时候应该在变量周围分配双引号,例如
"${var}"
防止空格引起的问题?
这个问题隐含的是
为什么还不够好?
${variable_name}
${variable_name}
并不意味着你认为它的作用......
......如果你认为它有任何事物处理由空格或“glob”(文件名模式)字符(在变量值中)引起的问题。 对此有好处:${variable_name}
$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food
没有别的! 1 不做${variable_name}
任何很好,除非您紧随其后的是可能是变量名称一部分的字符:字母(A
-Z
或a
- z
)、下划线(_
)或数字(0
- 9
)。即使这样,你也可以解决这个问题:
$ echo "$bar"d
food
或者
$ fourth_letter=d
$ echo $bar$fourth_letter
food
我并不是想阻止它的使用——
echo "${bar}d"
可能是最好的解决方案——而是阻止人们依赖牙套代替引号,或者本能地使用大括号,然后问:“现在,我需要引号吗?还?”
您应该始终使用引号,除非您有充分的理由不这样做,并且您确定自己知道自己在做什么。
_________________
1 当然,除了以下事实:参数扩展,例如 、 、 和 ,建立在语法之上。另外,您需要使用、等来引用第 10 个、第 11 个等位置参数 - 引号对此无济于事。${parameter:-[word]}
${parameter%[word]}
${#parameter}
${parameter^^}
${parameter}
${10}
${11}
答案4
为了处理变量中的空格和空白|特殊字符,您应该始终用双引号将它们引起来。设置适当的 IFS 也是一个很好的做法。
受到推崇的: http://www.dwheeler.com/essays/filenames-in-shell.html