我经常在使用autotools(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
其中第一个将产生错误,因为第一个参数test
已经丢失.第二个没有那个问题.
您的案例翻译如下:
if test "x$SHELL_VAR" = "xyes"; then --> if test "x" = "xyes"; then
它可能看起来有点多余,因为它既有引号又有"x",但它也会处理一个带空格的变量,而不会将其作为命令的两个参数x
.
另一个原因(除了空变量)与选项处理有关.如果你写:
if test $SHELL_VAR = yes; then --> if test = yes; then if test x$SHELL_VAR = xyes; then --> if test x = xyes; then
并且SHELL_VAR
具有该命令的值test
或x
任何其他有效选项SHELL_VAR
,语法不明确.在test
在前面防止前导破折号从被拾起作为一个选项x
.
请记住,这取决于shell.SHELL_VAR
如果环境变量不存在而不是仅仅返回一个空字符串,那么一些shell(我认为)会抱怨.
没有人提到的另一个原因是与期权处理有关.如果你写:
if [ "$1" = "abc" ]; then ...
并且$ 1的值为'-n',测试命令的语法不明确; 目前尚不清楚你在测试什么.前面的'x'可以防止前导破折号造成麻烦.
你必须要寻找真正的古贝壳找到一个地方测试命令没有支持-n
或-z
; 版本7(1978)test
命令包括它们.这并不是无关紧要 - 一些版本6 UNIX的东西逃到了BSD,但是现在,你很难找到任何古老的东西.
正如许多其他人所指出的那样,不使用值附近的双引号是危险的.实际上,如果文件名可能包含空格(MacOS X和Windows都在某种程度上鼓励它,并且Unix一直支持它,尽管像xargs
使它变得更难),然后你每次使用时都应该用双引号括起文件名.除非您负责该值(例如在选项处理期间,并且在启动时将变量设置为"no",并且在命令行中包含标志时为"是"),否则使用不带引号的变量形式是不安全的直到你证明他们是安全的 - 你也可以为了许多目的而一直这样做.或者记录如果用户尝试处理名称中带有空格的文件,脚本将会崩溃.(并且还有其他角色需要担心 - 例如,反叛也可能相当令人讨厌.)
我知道这个约定的原因有两个:
http://tldp.org/LDP/abs/html/comparison-ops.html
在复合测试中,甚至引用字符串变量可能还不够.如果$ string为空,[-n"$ string"-o"$ a"="$ b"]可能会导致某些版本的Bash出错.安全的方法是在可能为空的变量附加一个额外的字符,["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测试条件,那么使用表格会更安全[] x $ SOME_VAR"="x"]因为那将始终具有预期的效果.从本质上讲,这是一个旧的shell脚本技巧,用于查找空变量,尽管有更清晰的方法,但它现在仍然用于向后兼容.
我建议改为:
if test "yes" = "$SHELL_VAR"; then
因为它摒弃了丑陋的x
,而且还解决了提到的问题/sf/ask/17360801/即$SHELL_VAR
可以与启动-
和读取作为一个选项。