为什么shell脚本比较经常使用x $ VAR = xyes?

我经常在使用自动工具(autoconf,automake)的项目的构build脚本中看到这一点。 当有人想检查一个shellvariables的值时,经常使用这个习惯用法:

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

简单地检查像这样的值有什么好处:

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

我认为我必须有一些原因,我经常看到这一点,但我无法弄清楚它是什么。

如果你正在使用一个简单的replaceshell,并且SHELL_VARvariables不存在(或者是空的),那么你需要注意边缘情况。 以下翻译将会发生:

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

第一个会产生一个错误,因为第一个test参数已经丢失。 第二个没有这个问题。

你的情况翻译如下:

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

看起来有点多余,因为它既有引号也有“x”,但是它也会处理一个带有空格的variables,而不会把它作为test命令的两个参数。

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

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

$1的值为-n-z或其他任何有效的test命令选项,语法是不明确的。 在前面的x防止领先的破折号作为一个选项进行test

请记住,这取决于壳。 如果环境variables不存在,而不是仅仅返回一个空string,一些shell( csh for one,我认为)会痛苦地抱怨。

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

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

$ 1的值为“-n”,testing命令的语法不明确; 目前还不清楚你正在testing什么。 前面的“x”可防止前导破折号造成麻烦。

你必须看看真正的古代炮弹,find一个testing命令不支持-n-z ; 版本7(1978) test命令包括他们。 这并不是什么无关紧要的东西 – 有些UNIX版本的东西已经被转移到了BSD中,但是这些日子里,你很难find任何古老的东西。

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

我知道这个约定有两个原因:

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

在复合testing中,即使引用stringvariables也可能不够。 如果$ string为空,[-n“$ string”-o“$ a”=“$ b”]可能会导致某些版本的Bash出错。 安全的方法是追加一个额外的字符可能是空的variables,[“x $ string”!= x -o“x $ a”=“x $ b”](“x的”取消)。

其次,在除Bash之外的其他shell中,尤其是较老的shell,像'-z'这样的testing空variables的testing条件并不存在,所以当这个:

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

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

我相信这是因为

 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 

工作也一样,但会更容易debugging。

我build议,而不是:

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

因为它消除了丑陋的x ,并且仍然解决了$SHELL_VAR可能以$SHELL_VAR的问题,并且可以作为一个选项来阅读。

我曾经在DOS下做SHELL_VAR可能是未定义的。

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