我经常在使用自动工具(autoconf、automake)的项目的构建脚本中看到这一点。当有人想要检查 shell 变量的值时,他们经常使用这个成语:
if test "x$SHELL_VAR" = "xyes"; then
...
与简单地检查这样的值相比,这样做有什么好处:
if test $SHELL_VAR = "yes"; then
...
我想我经常看到这个肯定是有原因的,但我不知道它是什么。
如果您使用的 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,实际上是多余的,因为引号会导致空参数和包含空格的参数都被解释为单个对象。
其他人尚未提及的另一个原因与期权处理有关。如果你写:
if [ "$1" = "abc" ]; then ...
并且 $1 的值为 '-n',测试命令的语法不明确;目前尚不清楚您在测试什么。前面的“x”可防止前导破折号造成麻烦。
您必须查看真正古老的 shell 才能找到 test 命令不支持-n
or的 shell -z
;版本 7 (1978)test
命令包括了它们。这并不是完全无关紧要的——一些版本 6 的 UNIX 东西逃到了 BSD 中,但是现在,你很难找到当前使用的任何古老的东西。
正如许多其他人指出的那样,不在值周围使用双引号是危险的。确实,如果文件名有可能包含空格(MacOS X 和 Windows 在某种程度上都鼓励这样做,而 Unix 一直支持它,尽管像这样的工具xargs
让它更难),那么每次使用文件名时也应该用双引号括起来。除非您负责该值(例如,在选项处理期间,并且您在启动时将变量设置为“否”,当命令行中包含标志时设置为“是”),否则使用不带引号的变量形式是不安全的直到你证明它们是安全的——你也可以为了许多目的一直这样做。或者记录如果用户尝试处理名称中包含空格的文件,您的脚本将严重失败。(还有其他字符也需要担心——例如,反引号也可能相当讨厌。)
我知道这个约定有两个原因:
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 脚本技巧,尽管有更简洁的方法可用,但它今天仍然用于向后兼容。
我建议改为:
if test "yes" = "$SHELL_VAR"; then
因为它消除了丑陋的x
,并且仍然解决了https://stackoverflow.com/a/174288/895245提到的问题,该问题$SHELL_VAR
可能以选项开头-
并被阅读。
我相信这是由于
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
效果一样好,但更容易调试。
当 SHELL_VAR 可能未定义时,我曾经在 DOS 中这样做。
如果你不做 "x$SHELL_VAR" 的事情,那么如果 $SHELL_VAR 是未定义的,你会得到一个关于 "=" 不是一元运算符或类似的错误。