为啥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" = yesxyes 更优雅地解决空var 边缘情况吗?对于期权处理,更改订单'abc' = "$1" 是另一种选择吗? 为什么test 的第一个参数会丢失而不是空字符串?不同的 shell 处理这个问题的方式不同吗? @phk:试试export xxx=''if test $xxx = 1 ; then echo 2 ; fiexport 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?的主要内容,如果未能解决你的问题,请参考以下文章

shell脚本学习比较两个数字大小

Shell脚本

shell脚本如何判断文件或文件夹是否存在

如何在shell脚本中使用'($ var)'作为参数[重复]

shell如何比较 两个字符串是不是相等?

Linux | shell脚本-比较判断和运算语句