81

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

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

与简单地检查这样的值相比,这样做有什么好处:

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

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

4

7 回答 7

98

如果您使用的 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

其中第一个将产生一个错误,因为第一个参数 totest已经丢失。第二个没有这个问题。

您的案例翻译如下:

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

x至少对于 POSIX 兼容的 shell,实际上是多余的,因为引号会导致空参数和包含空格的参数都被解释为单个对象。

于 2008-10-06T13:29:01.000 回答
24

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

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

并且 $1 的值为 '-n',测试命令的语法不明确;目前尚不清楚您在测试什么。前面的“x”可防止前导破折号造成麻烦。

您必须查看真正古老的 shell 才能找到 test 命令不支持-nor的 shell -z;版本 7 (1978)test命令包括了它们。这并不是完全无关紧要的——一些版本 6 的 UNIX 东西逃到了 BSD 中,但是现在,你很难找到当前使用的任何古老的东西。

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

于 2008-10-07T22:00:01.173 回答
12

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

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 测试条件,使用 if [ " 格式会更安全x$SOME_VAR" = "x" ] 因为这将始终具有预期的效果。本质上,这是一个用于查找空变量的旧 shell 脚本技巧,尽管有更简洁的方法可用,但它今天仍然用于向后兼容。

于 2008-10-06T13:41:06.670 回答
5

我建议改为:

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

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

于 2016-06-30T05:04:47.263 回答
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 

效果一样好,但更容易调试。

于 2008-10-06T13:00:42.633 回答
0

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

于 2008-10-06T12:51:46.770 回答
0

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

于 2008-10-06T13:01:05.130 回答