代码之家  ›  专栏  ›  技术社区  ›  jonner

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

  •  52
  • jonner  · 技术社区  · 16 年前

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

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

    与简单地检查这样的值相比,它的优势是什么:

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

    我想我经常看到这个肯定是有原因的,但我不知道它是什么。

    7 回复  |  直到 8 年前
        1
  •  77
  •   paxdiablo    12 年前

    如果你用的是外壳 简单的 替代和 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”,但它也会处理一个变量,其中包含空格,而不会给出 的参数 测试 命令。

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

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

    $1 具有价值 -n -z 或任何其他有效选项 测试 命令,语法不明确。这个 x 在前面,防止前面的冲刺被选作 测试 .

    记住这取决于外壳。一些贝壳( csh 首先,我认为)如果环境变量不存在,而不仅仅是返回一个空字符串,那么它将发出痛苦的抱怨。

        2
  •  19
  •   Jonathan Leffler Toon Krijthe    16 年前

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

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

    $1的值为“-n”,test命令的语法不明确;不清楚您在测试什么。前面的“X”防止前面的破折号引起故障。

    你必须看看真正古老的贝壳才能找到一个测试命令不支持的地方 -n -z ;第7版(1978) test 命令包括了它们。这并不完全无关紧要——有些版本6的Unix东西逃到了BSD中,但是现在,要找到任何在当前使用中很古老的东西都是非常困难的。

    正如其他一些人指出的那样,不要在值周围使用双引号是危险的。事实上,如果文件名可能包含空格(MacOS X和Windows在某种程度上都鼓励这样做,而且Unix一直支持它,尽管类似于 xargs 更难),然后每次使用文件名时,都应该用双引号将其括起来。除非您负责该值(例如,在选项处理期间,并且在启动时将变量设置为“否”,在命令行中包含标志时将变量设置为“是”),否则,在证明变量的安全性之前,使用未加引号的变量形式是不安全的,而且出于许多目的,您也可以一直这样做。或者记录下,如果用户试图处理名称中有空格的文件,脚本将严重失败。(还有其他的角色也需要担心——例如,背景符号也可能非常讨厌。)

        3
  •  10
  •   Jay    16 年前

    我知道本公约有两个原因:

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

    在复合测试中,即使引用字符串变量也可能不够。 [-n“$string”-o“$a”=“$b”]可能导致某些版本的 如果$string为空,则进行bash。安全的方法是在 可能是空变量,[“x$string”!=x-o“x$a”=“x$b”](x's“取消)。

    其次,在bash之外的其他shell中,尤其是较旧的shell中,不存在用于测试空变量的“-z”这样的测试条件,因此,尽管这样做:

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

    在bash中可以很好地工作,如果您的目标是跨各种Unix环境的可移植性,在这些环境中,您不能确定默认shell是否为bash,以及它是否支持-z测试条件,那么如果[“x$some_var”=“x”]始终具有预期的效果,那么使用表单更安全。从本质上讲,这是一个寻找空变量的旧shell脚本技巧,尽管有更干净的方法可用,但它仍然用于向后兼容性。

        4
  •  4
  •   Community Mr_and_Mrs_D    7 年前

    我建议改为:

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

    因为它消除了丑陋 x 仍然解决了 https://stackoverflow.com/a/174288/895245 那个 $SHELL_VAR 可以开始 - 作为一种选择。

        5
  •  2
  •   Kent Fredric    16 年前

    我相信是因为

    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 
    

    也可以,但更容易调试。

        6
  •  1
  •   Ken    16 年前

    在DOS中,当shell变量可能未定义时,我会这样做。

        7
  •  1
  •   Paul Tomblin    16 年前

    如果不执行“x$shell_var”操作,那么如果未定义$shell_var,则会得到一个关于“=”的错误,它不是一元运算符或类似的运算符。