为啥shell脚本比较经常使用x$VAR = xyes?
Posted
技术标签:
【中文标题】为啥shell脚本比较经常使用x$VAR = xyes?【英文标题】:Why do shell script comparisons often use x$VAR = xyes?为什么shell脚本比较经常使用x$VAR = xyes? 【发布时间】:2010-09-15 12:06:55 【问题描述】:我经常在使用自动工具(autoconf、automake)的项目的构建脚本中看到这一点。当有人想要检查 shell 变量的值时,他们经常使用这个成语:
if test "x$SHELL_VAR" = "xyes"; then
...
与像这样简单地检查值相比,这样做有什么好处:
if test $SHELL_VAR = "yes"; then
...
我想我经常看到这个肯定是有原因的,但我不知道它是什么。
【问题讨论】:
另见Shell script purpose of x in"x$Variable"
。该问题与此问题重复。
另见bash test for empty string with X""
。这个问题实际上也是这个问题的重复,或者涵盖了大部分相同的领域。两个副本都有一些很好的答案。
【参考方案1】:
当 SHELL_VAR 可能未定义时,我曾经在 DOS 中这样做。
【讨论】:
【参考方案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
同样有效,但更容易调试。
【讨论】:
【参考方案3】:如果您不执行“x$SHELL_VAR”的操作,那么如果 $SHELL_VAR 未定义,您会收到有关“=”不是一元运算符或类似的错误。
【讨论】:
只有在你没有正确引用的情况下才是正确的;如果你没有正确引用,你的 shell 脚本会有更大的问题,使用x$FOO
将无法解决。【参考方案4】:
如果您使用的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
,至少对于 POSIX 兼容的 shell,实际上是多余的,因为引号会导致空参数和包含空格的参数都被解释为单个对象。
【讨论】:
不会引用test "$SHELL_VAR" = yes
比xyes
更优雅地解决空var 边缘情况吗?对于期权处理,更改订单'abc' = "$1"
是另一种选择吗?
为什么test
的第一个参数会丢失而不是空字符串?不同的 shell 处理这个问题的方式不同吗?
@phk:试试export xxx=''
、if test $xxx = 1 ; then echo 2 ; fi
、export xxx='yyy'
、if test $xxx = 1 ; then echo 2 ; fi
。第一个if
给出错误,第二个没有。这是bash
。
我的错误,我正在使用'printf'进行测试,它显然总是假设有第二个参数。
@SnakE:与 POSIX 兼容的 shell 安全地做到了这一点,我在最后一段中说过。 Jonathan Leffler 的回答解释了为什么 x
可能已在较旧的 shell 中使用。【参考方案5】:
我知道这个约定有两个原因:
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”被取消)。
其次,在 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 脚本技巧,尽管有更简洁的方法可用,但它至今仍被用于向后兼容。
【讨论】:
对于第一种情况,我实际上会选择[ -n "$string" ] || [ "$a" = "$b" ]
【参考方案6】:
其他人尚未提及的另一个原因与期权处理有关。如果你写:
if [ "$1" = "abc" ]; then ...
$1 的值为'-n',测试命令的语法不明确;目前尚不清楚您在测试什么。前面的“x”可防止前导破折号造成麻烦。
你必须查看真正古老的 shell 才能找到一个 test 命令不支持 -n
或 -z
的 shell;版本 7 (1978) test
命令包括它们。这并不是完全无关紧要 - 一些 UNIX 版本 6 的东西逃到了 BSD 中,但是现在,你很难找到当前使用的任何古老的东西。
正如许多其他人指出的那样,不在值周围使用双引号是危险的。事实上,如果文件名有可能包含空格(MacOS X 和 Windows 都在某种程度上鼓励这种做法,而 Unix 一直支持它,尽管像 xargs
这样的工具使它更难),那么你应该将文件名用双引号括起来每次使用时都会引用它们。除非您负责该值(例如,在选项处理期间,并且您在启动时将变量设置为“否”,当命令行中包含标志时设置为“是”),否则使用不带引号的变量形式是不安全的直到你证明它们是安全的——你也可以为了许多目的一直这样做。或者记录如果用户尝试处理名称中包含空格的文件,您的脚本将严重失败。 (还有其他字符也需要担心——例如,反引号也可能相当讨厌。)
【讨论】:
我想说,当您的第一个示例中正确引用测试核心时,没有歧义。在这种情况下,即使某些值为空,也有 3 个参数,例如 [ "-n" = "" ]。所以test
实现不应该进入 [ -n "$var" ] 分支案例(只需要 2 个参数)。就个人而言,每次我遇到问题时,要么与 $var 周围缺少引号导致错误“=:预期的一元运算符”有关,要么 -z 选项不存在,或者在其核心 [... ] 而不是使用多个 [ test1 ] || [ test2 ] 在外壳级别(如@Jay 所述)。
如果您可以依赖现代实现,也许您可以侥幸逃脱,尽管有时很难阅读您的 cmets。从历史上看,test
的一些实现做了一些奇怪的事情,并且该技术可以防止这些奇怪的实现。您可能很幸运,这些天不必担心。【参考方案7】:
我建议改为:
if test "yes" = "$SHELL_VAR"; then
因为它消除了丑陋的x
,并且仍然解决了https://***.com/a/174288/895245 提到的问题,即$SHELL_VAR
可能以-
开头并被视为一个选项。
【讨论】:
以上是关于为啥shell脚本比较经常使用x$VAR = xyes?的主要内容,如果未能解决你的问题,请参考以下文章