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

时间:2008-10-06 12:50:21

标签: unix shell autotools

我经常在使用autotools(autoconf,automake)的项目的构建脚本中看到这一点。当有人想检查shell变量的值时,他们经常使用这个习语:

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

这比仅仅检查这样的值有什么好处:

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

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

7 个答案:

答案 0 :(得分:80)

如果您正在使用一个执行简单替换的shell并且SHELL_VAR变量不存在(或为空),那么您需要注意边缘情况。将进行以下翻译:

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”但它也会处理一个带有空格的变量,而不是将它作为两个 test命令的参数。

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

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

$1具有值-n-ztest命令的任何其他有效选项,语法不明确。前面的x可以防止前导短划线被选为test的选项。

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

答案 1 :(得分:19)

其他人尚未提及的另一个原因是与期权处理有关。如果你写:

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

和$ 1的值为'-n',测试命令的语法不明确;目前尚不清楚你在测试什么。前面的'x'可防止前导冲刺造成麻烦。

您必须查看非常古老的shell才能找到测试命令不支持-n-z的shell;版本7(1978)test命令包含它们。这并不是无关紧要 - 一些版本6 UNIX的东西逃到了BSD,但是现在,你很难找到任何古老的东西。

正如许多其他人所指出的那样,不使用值附近的双引号是危险的。实际上,如果文件名可能包含空格(MacOS X和Windows都在某种程度上鼓励它,并且Unix一直支持它,虽然像xargs这样的工具使其更难),那么你应该附上文件名每次使用时都用双引号括起来。除非您负责该值(例如在选项处理期间,并且在启动时将变量设置为“no”,并且在命令行中包含标志时为“是”),否则使用不带引号的变量形式是不安全的直到你证明他们是安全的 - 你也可以为了许多目的而一直这样做。或者记录如果用户尝试处理名称中带有空格的文件,脚本将会崩溃。 (还有其他角色需要担心 - 例如,反叛也可能相当令人讨厌。)

答案 2 :(得分:10)

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

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

  

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

其次,在除了Bash之外的其他shell中,尤其是旧版本的shell中,测试空变量的测试条件如'-z'不存在,所以尽管如此:

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

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

答案 3 :(得分:4)

我建议改为:

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

因为它消除了丑陋的x,并且仍然解决了https://stackoverflow.com/a/174288/895245提到的$SHELL_VAR可能以-开头并作为选项阅读的问题。

答案 4 :(得分: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 

同样适用,但更容易调试。

答案 5 :(得分:1)

当SHELL_VAR可能未定义时,我曾经在DOS中这样做。

答案 6 :(得分:1)

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