我对 Bash 中反引号的求值感到困惑。我以前见过这样的代码,它很有意义:
RESULT=`whoami`
这会将命令的输出存储whoami
在RESULT
变量中,并确认反引号的计算结果为输出其中的命令。但我还看到过类似下面的代码:
if `wget google.com -T 2 -t 1 -o /dev/null`; then
echo "Internet ok"
fi
这是通过尝试获取 google 主页来测试互联网连接(使用 进行-t 1
一次尝试,使用-T 2
进行 2 秒超时,并重定向到,/dev/null
因为它实际上并不想要输出,而只是想看看它是否成功)。代码读起来好像作者期望反引号会评估命令的退出代码wget
而不是输出。
但它确实有效。它打印Internet ok
是否可以连接到 google.com,如果我将 URL 更改为不存在的无意义 URL,它就不满足 if 语句并且不打印任何内容。我不明白为什么这会起作用。
独立运行wget
具有有效和无效 URL 的命令在两种情况下都不会打印任何内容,只是退出代码有所不同。
我的结论是,有一个特殊的构造用于if
后跟反引号,它返回退出代码而不是输出。我错了吗?
答案1
不,反引号没有特殊含义,您甚至可以wget
在语句中不使用反引号来运行命令if
。
if
始终评估所跟随命令的退出代码。
可以找到更全面的概述这里。
编辑
反引号启动在子 shell 中执行的命令替换并返回退出代码。if
它只检查返回代码,而不使用命令的输出。
要比较命令的输出,您可以使用[
,它必须以 结尾]
,这基本上是一个测试。测试可以是任何东西
- 从
man test
字符串比较
[ "hello" == "test" ]
整数测试
[ 2 -eq 3 ]
如果测试成功,您将获得退出代码 0(true),否则不为 0(false),再次由 进行评估if
。
因此,您心中应该有如下想法。
if [ "`wget google.com -T 2 -t 1 -o /dev/null`" == "" ]
then
echo "emptY"
fi
但这没有多大意义,因为将输出重定向到/dev/null
您将始终从[ "
wget google.com -T 2 -t 1 -o /dev/null获得 true " == "" ]
。
另一方面,检查wget
命令的输出是否包含输出(忽略)也没有用-o /dev/null
,因为即使在错误情况下您也会得到输出,但返回代码不同。
答案2
在 bash if shell 脚本中,现在避免使用反引号,而使用命令替换$(...)
语法。
它比非常模糊的反引号更清晰,并且支持嵌套,这是反引号无法实现的。例如:
command $(command two $(command three))
答案3
我终于通过讨论主题在bash邮件列表中。结论如下:
- 这里的语句没有什么特别之处
if
,它只是执行后面的命令if
,然后根据该命令的退出代码进行分支。这里的意外行为归因于命令替换本身。 - 反引号命令(命令替换)始终计算为输出其中的命令,而不是退出代码(这不是什么新东西,只是确认是否使用反引号
if
是无关紧要的)。 - 基于以上两点,基于以下因素
if `cmd` ...
运行身体的不明显原因if
退出代码的cmd
cmd
不向 stdout 写入任何内容关于 shell 命令执行,有一个 POSIX 规范规则,其内容如下:
如果没有命令名称,但命令包含命令替换,则命令应以最后执行的命令替换的退出状态完成。否则,命令应以零退出状态完成。
參考文獻:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
- 所以,这只是因为在没有向标准输出写入任何内容的情况下
cmd
,shell - 而不是执行一个空命令 - 采用命令替换的退出代码(cmd
其自身的退出代码)并将其作为父命令的退出代码返回 - 然后冒泡到if
。
为了更好地说明这一点,请参见以下示例:
$ `true`
$ echo $?
0
$ `false`
$ echo $?
1
shell 首先运行true
,它不会向 stdout 发出任何内容,因此生成的命令为空。根据上述规则,我原本预计空命令要么确定地成功,要么确定地失败,但它以退出代码true
而不是“空命令的退出代码”完成。