我经常在使用autotools(autoconf,automake)的项目的构建脚本中看到这一点。当有人想检查shell变量的值时,他们经常使用这个习语:
if test "x$SHELL_VAR" = "xyes"; then
...
这比仅仅检查这样的值有什么好处:
if test $SHELL_VAR = "yes"; then
...
我认为必须有一些原因让我经常看到这一点,但我无法弄清楚它是什么。
答案 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
或-z
或test
命令的任何其他有效选项,语法不明确。前面的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运算符或类似的错误。