为什么shell脚本比较经常使用x $ VAR = xyes?
我经常在使用自动工具(autoconf,automake)的项目的构build脚本中看到这一点。 当有人想检查一个shellvariables的值时,经常使用这个习惯用法:
if test "x$SHELL_VAR" = "xyes"; then ...
简单地检查像这样的值有什么好处:
if test $SHELL_VAR = "yes"; then ...
我认为我必须有一些原因,我经常看到这一点,但我无法弄清楚它是什么。
如果你正在使用一个简单的replaceshell,并且SHELL_VAR
variables不存在(或者是空的),那么你需要注意边缘情况。 以下翻译将会发生:
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”,但是它也会处理一个带有空格的variables,而不会把它作为test
命令的两个参数。
另一个原因(空variables除外)与选项处理有关。 如果你写:
if test "$1" = "abc" ; then ...
而$1
的值为-n
或-z
或其他任何有效的test
命令选项,语法是不明确的。 在前面的x
防止领先的破折号作为一个选项进行test
。
请记住,这取决于壳。 如果环境variables不存在,而不是仅仅返回一个空string,一些shell( csh
for one,我认为)会痛苦地抱怨。
另一个没有人提到的原因是与期权处理有关。 如果你写:
if [ "$1" = "abc" ]; then ...
$ 1的值为“-n”,testing命令的语法不明确; 目前还不清楚你正在testing什么。 前面的“x”可防止前导破折号造成麻烦。
你必须看看真正的古代炮弹,find一个testing命令不支持-n
或-z
; 版本7(1978) test
命令包括他们。 这并不是什么无关紧要的东西 – 有些UNIX版本的东西已经被转移到了BSD中,但是这些日子里,你很难find任何古老的东西。
正如一些其他人所指出的那样,不要使用双引号来表示危险。 事实上,如果文件名可能包含空格(MacOS X和Windows都鼓励在某种程度上,Unix一直支持它,虽然像xargs
这样的工具使它更难),那么你应该把文件名用双引号括起来你也可以使用它们。 除非你负责这个值(例如,在选项处理过程中,并且在启动时将variables设置为“no”,而在命令行中包含标志时将该值设置为“yes”),那么使用不加引号的variablesforms是不安全的直到你certificate他们是安全的 – 而且你也可以一直做很多事情。 或者如果用户试图用名称中的空白处理文件,那么你的脚本将会非常糟糕地失败。 (还有其他angular色也要担心 – 例如,反引号也可能是相当讨厌的)。
我知道这个约定有两个原因:
http://tldp.org/LDP/abs/html/comparison-ops.html
在复合testing中,即使引用stringvariables也可能不够。 如果$ string为空,[-n“$ string”-o“$ a”=“$ b”]可能会导致某些版本的Bash出错。 安全的方法是追加一个额外的字符可能是空的variables,[“x $ string”!= x -o“x $ a”=“x $ b”](“x的”取消)。
其次,在除Bash之外的其他shell中,尤其是较老的shell,像'-z'这样的testing空variables的testing条件并不存在,所以当这个:
if [ -z "$SOME_VAR" ]; then echo "this variable is not defined" fi
在BASH中可以正常工作,如果你打算在各种UNIX环境中移植,你不能确定默认的shell是否是Bash,并且它是否支持-ztesting条件,那么使用if [ x $ SOME_VAR“=”x“],因为这将始终具有预期的效果。 从本质上讲,这是一个用于查找空variables的旧shell脚本技巧,尽pipe存在更简洁的方法,但它仍然用于向后兼容。
我相信这是因为
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
工作也一样,但会更容易debugging。
我build议,而不是:
if test "yes" = "$SHELL_VAR"; then
因为它消除了丑陋的x
,并且仍然解决了$SHELL_VAR
可能以$SHELL_VAR
的问题,并且可以作为一个选项来阅读。
我曾经在DOS下做SHELL_VAR可能是未定义的。
如果你不做“x $ SHELL_VAR”的话,那么如果$ SHELL_VAR是未定义的,你会得到一个关于“=”不是一个monadic运算符或类似的错误。