${variable_name}并不意味着你认为它的作用......

${variable_name}并不意味着你认为它的作用......

我对使用单括号或双括号感到困惑。看这段代码:

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例如kshbash

答案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-Za- 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

相关内容