2008-10-06 91 views
41

我经常在使用autotools(autoconf,automake)的项目的构建脚本中看到这种情况。当有人要检查shell变量的值,他们经常使用这个成语:

if test "x$SHELL_VAR" = "xyes"; then 
... 

有什么优势,这只是检查像这样的价值在:

if test $SHELL_VAR = "yes"; then 
... 

我算起来也必须是我经常看到这个的一些原因,但我无法弄清楚它是什么。

+1

请参阅参考[shell脚本在``x $ Variable'``]中的用途“(http://stackoverflow.com/questions/1805663/shell-script-purpose-of-x-in-xvariable/)。这个问题是这个问题的重复。 – 2013-12-29 16:37:16

+1

另请参阅[使用``X``测试空字符串``](http://stackoverflow.com/questions/6852612/bash-test-for-empty-string-with-x)。这个问题实际上也是这个问题的重复,或者涵盖了大部分相同的理由。这两个副本都有一些很好的答案。 – 2013-12-29 16:45:51

回答

67

如果您使用的是一个壳简单替代和SHELL_VAR变量不存在(或为空),那么您需要注意边缘情况。下面的转换会发生:因为拳头参数test不见了

if test $SHELL_VAR = yes; then  --> if test = yes; then 
if test x$SHELL_VAR = xyes; then  --> if test x = xyes; then 

其中第一项将产生一个错误。第二个没有这个问题。

你的情况下翻译如下:

if test "x$SHELL_VAR" = "xyes"; then --> if test "x" = "xyes"; then 

可能似乎有点多余,因为它具有引号和“×”,但它也将处理与空格的变量,不给作为两个参数到test命令。

另一个原因(空变量除外)与选项处理有关。如果你写:

if test "$1" = "abc" ; then ... 

$1具有价值-n-z或任何其他有效的选项为test命令,语法是不明确的。前方的x可防止将前导短跑选作test的选项。

请记住,这取决于外壳。有些shell(csh,我认为)如果环境变量不存在而不是仅仅返回一个空字符串,会很痛苦地抱怨)。

+0

是的,现在看起来很明显。 – jonner 2008-10-06 13:57:20

1

我曾经在DOS下做过SHELL_VAR可能未定义的情况。

2

我相信它是由于

SHELLVAR=$(true) 
if test $SHELLVAR = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected 

以及

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi 
# bash: test: =: unary operator expected 

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi 
# yep 

然而,这通常应该工作

SHELLVAR=" hello" 
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output> 

但是当它在输出抱怨别的地方,它很难说其抱怨我猜,所以

SHELLVAR=" hello" 
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

作品一样好,但会更容易调试。

1

如果你不做“x $ SHELL_VAR”的事情,那么如果$ SHELL_VAR未定义,你会得到一个关于“=”不是一个monadic操作符或类似的错误。

9

有两方面的原因,我知道这个约定:

http://tldp.org/LDP/abs/html/comparison-ops.html

在混合测试,甚至引用字符串变量可能是不够的。如果$ string为空, [-n“$ string”-o“$ a”=“$ b”]可能会导致某些版本的 Bash出错。安全的方法是向可能为空的变量追加一个额外的字符,[“x $ string”!= x -o“x $ a”=“x $ b”](“x's”抵消)。

其次,在其他壳比猛砸,尤其是老年人,喜欢的测试条件“-z”来测试一个空的变量是不存在的,因此,虽然这样的:

if [ -z "$SOME_VAR" ]; then 
    echo "this variable is not defined" 
fi 

将正常工作在BASH中,如果你的目标是跨越各种UNIX环境的可移植性,在这些环境中你不能确定默认shell是Bash,并且它是否支持-z测试条件,那么使用if [“x $ SOME_VAR “=”x“],因为这将始终具有预期的效果。从本质上讲,这是一个用于查找空变量的旧shell脚本技巧,尽管存在更简洁的方法,但它现在仍然用于向后兼容。

17

另一个没有人提到的原因是与选项处理有关。如果你写:

if [ "$1" = "abc" ]; then ... 

和$ 1的值为'-n',测试命令的语法不明确;目前还不清楚你正在测试什么。前面的'x'可防止前导破折号引起麻烦。

你必须看着真正古老的炮弹找到一个测试命令不支持-n-z;版本7(1978)test命令包含它们。这并不是无关紧要的 - 一些版本6 UNIX的东西被转移到了BSD中,但是现在你很难找到任何古老的东西。

正如许多其他人所指出的那样,在值周围不使用双引号是很危险的。事实上,如果文件名可能包含空格(MacOS X和Windows都在某种程度上鼓励,而且Unix一直支持它,尽管像xargs这样的工具使得它更难),那么你应该把文件名用双引号括起来你也可以使用它们。除非你负责该值(例如在选项处理过程中,并且在启动时将该变量设置为'no',而在命令行中包含标志时将该变量设置为'yes'),那么使用不加引号的变量形式是不安全的直到你证明他们是安全的 - 而且你也可以一直做很多事情。或者如果用户试图处理名称中空白的文件,那么你的脚本将会非常糟糕地失败。 (还有其他人物也会担心 - 例如,反引号也可能相当讨厌。)