为什么在[[]]测试中使用双引号?


23

假设我们在bash脚本中有2个整数:

value1=5
value2=3

那为什么要在测试中使用双引号呢?例如:

if [[ "$value1" -eq "$value2" ]]

为什么不只使用以下内容?

if [[ $value1 -eq $value2 ]]

对我来说,双引号没有任何意义。


5
Shell中的引用与字符串无关(就像在数据类型中一样)。这实际上是关于防止Shell执行单词拆分(以及其他类型的扩展。)
filbranden

13
正如terdon所指出的那样,虽然不必在此特定结构中也引用变量(当然,在任何地方都带有一个单词的值),但我还是想建议始终引用您的var。您真的知道在最不相关的上下文中可以不引用变量吗?即使无需使用53,也要引用所有的原因是可维护性。以后可能会更改这些值,并且导致的错误可能并不明显。
彼得-

2
@sudodus我认为并非如此[[ ]],仅适用于[ ]
本杰明·W。

1
你是对的,@ BenjaminW。单词拆分不是唯一的情况,为什么对变量使用双引号是个好主意。变量为空时也有这种情况。这将使在声明】失败(例如[2当量]有错误,但[[]]是不容易(如与分词的情况下)。
sudodus

2
@ marcelm,Bash,ksh和Zsh具有整数变量,并且在[[ ]]它们内部还将的操作数强制转换-eq为整数。
ilkkachu

Answers:


7

分词。

这个例子是不可能的,但是可能的,因此,如果您想进行防御性编码,请在引号上加上引号:

$ set -x
$ value1=5
+ value1=5
$ value2=3
+ value2=3
$ [ $value1 -eq $value2 ]
+ '[' 5 -eq 3 ']'

好的,到目前为止一切都很好。让我们把扳手扔进齿轮:

$ IFS=456
+ IFS=456
$ [ $value1 -eq $value2 ]
+ '[' '' -eq 3 ']'
bash: [: : integer expression expected

哎呀。

$ [ "$value1" -eq "$value2" ]
+ '[' 5 -eq 3 ']'


2
但是OP使用双括号,因此不会发生单词拆分。
user000001

5
是的,这与bash的[[ ]]构造无关。
terdon

1
[ $value1 -eq $value2 ]用空value1'[' -eq 3 ']'没有''在左侧。
查尔斯·达菲

我也很惊讶,但是有证据。
格伦·杰克曼

1
这个答案与问题不直接相关。我很惊讶它被接受了。设置为社区Wiki。
格琳·杰克曼

35

您实际上不需要此处的引号。这是很少使用无引号的变量是安全的情况之一。您可以通过以下方式确认set -x

$ var1=""
$ var2="3"
$ set -x
$ if [[ $var1 -eq $var2 ]]; then echo "match!"; else echo "no match!"; fi
+ [[ '' -eq 3 ]]
+ echo 'no match!'
no match!
$ if [[ "$var1" -eq "$var2" ]]; then echo "match!"; else echo "no match!"; fi
+ [[ '' -eq 3 ]]
+ echo 'no match!'
no match!

如上所示,bash将测试的带引号和未引号版本解析为完全相同的内容。对于zsh我认为支持该[[ ]]运算符的任何其他外壳来说,也应该如此。

请注意,便携性不是这种情况[ ]

$ if [ $var1 -eq $var2 ]; then echo "match!"; else echo "no match!"; fi
+ '[' -eq 3 ']'
sh: [: -eq: unary operator expected
+ echo 'no match!'
no match!

[ ]构造不同,该构造[[ ]]确实需要引用。


一些有用的链接,以了解有关何时以及为何需要报价的更多信息:


在这种情况下,使用整数变量时,最好这样声明它们。declare -i var1=0在评估时。
弗雷迪

4
请注意,这-eq算术比较,因此您甚至可以$省略out(in [[ .. ]])并编写[[ a -eq b ]]。通过字符串比较,您$当然需要,所以[[ $a = $b ]]
ilkkachu

2
@ user000001好点。虽然,当然,您很有可能需要扩展它们。例如,我希望这是一个匹配项:var1="afoob"; var2="a*b"; [[ $var1 = $var2 ]] && echo match。如果您想使用glob字符而不在glob上下文中充当glob字符(例如[[ ]]),则需要引用,是的。
terdon

3
@ilkkachu,这让我感到惊讶。我认为“裸”变量只能在数组下标或((...))或中考虑$((...)。它似乎有效,但是(脾气暴躁的老兄,启用)我不喜欢它。
格琳·杰克曼

1
@glennjackman,很好,很抱歉告诉您,但是,是的,-eq里面的操作数和朋友[[ ]]也是算术上下文。:)(相关:bash [[]]命令中的自动变量扩展
ilkkachu

17

即使不需要双引号,使用它们的原因还有:

  • 良好习惯/习惯:在这种情况下,它们不是必需的,但总的来说,双引号为了避免意外的单词拆分。
  • 因为value1value2是变量,所以您可能不知道它们包含什么。否则,您可能会问:“为什么要烦恼变量而不是进行检查if [[ 5 -eq 3 ]]?或者更进一步,if当您已经知道5不等于3时,为什么还要烦恼at?通常最好进行防御。(这是事实。这个词拆分不会发生在[[,但如果字不分裂的情况下发生是罕见的。同样,见第一点。)

1

与您的问题没有直接关系,但我使用

如果(($ value1 == $ value2)); 然后

当我比较数字时,您也不必使用引号。


2
if (( value1 == value2 )); then。在算术上下文中,您甚至不需要$。但是,这个问题似乎集中在[[ ... ]]测试中使用的变量上。
库萨兰达

1
我知道。但坦率地说,我不使用此功能,因为我希望变量名保持一致,因此始终将$用作前缀。
抽筋

1

你是绝对正确的!

至少在这种情况下,双方括号内的引用完全没有意义。

但是,由于我每天都使用双引号-尤其是对于单方括号表达式,将参数传递给函数和脚本,有时还要进行变量赋值(对于简单的代数操作完全没有用)- 我想至少有些人会这样做,本能地围绕变量展开写双引号

双引号可以带给您节省的感觉。就像回家时双引号一样。-D.库默

因此,可理解的双引号的好处-但只有在有意义的情况下-才是bash的新手可以学习如何编写更稳定的脚本。它还强调了一个事实,即使用bash进行数据处理的技巧更多是关于通过字段分隔符分离数据流(包括变量)并将其通过过滤器进行管道传递。一旦将数据块与流分开,就将它们用双引号括起来!

另一个好处可能是在代码突出显示编辑器中带有双引号字符串的bash脚本的可读性更好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.