我很困惑,当简单地执行变量赋值并使用命令替换时,命令将返回什么错误代码:
a=$(false); echo $?
它输出1
,这让我认为变量赋值不会在最后一个错误代码后清除或产生新的错误代码。但当我试着这样做时:
false; a=""; echo $?
它输出0
,显然这是a=""
返回的,它覆盖了false
返回的1
。
我想知道为什么会发生这种情况,变量赋值是否有任何特殊性,与其他正常命令不同?或者仅仅是因为a=$(false)
被认为是一个单一的命令,只有命令替换部分有意义?
--更新--
谢谢大家,从回答和评论中我得到了一个观点“当你使用命令替换分配一个变量时,退出状态是命令的状态。”(作者@Barmar),这个解释非常清晰易懂,但对程序员来说不够精确,我想从TLDP或GNU手册页等权威机构看到这一点的参考,请帮助我找到它,再次感谢!
5条答案
按热度按时间o2gm4chl1#
正如其他人所说,命令替换的退出代码就是被替换命令的退出代码,所以
然而,出乎意料的是,将
export
添加到的开头会产生不同的结果:这是因为,当替代命令
false
失败时,export
命令成功,这是语句返回的退出代码。v09wglhw2#
在执行命令时,
$(command)
允许命令的输出替换自身。当你说:
由命令
false
产生的输出被存储在变量a
中。此外,退出代码与命令生成的退出代码相同。help false
表示:另一方面,说:
导致返回
a
赋值的退出代码,即0
。编辑:
引自手册:
如果其中一个扩展包含命令替换,则命令的退出状态是执行的最后一个命令替换的退出状态。
引用自BASHFAQ/002:
如何在变量中存储命令的返回值和/或输出?
...
output=$(command)
status=$?
分配给
output
对command
的退出状态没有影响,它仍然在$?
中。这不是bash特有的。引用The Open Group Base Specifications Issue 7,POSIX.1-2017的“Shell & Utilities”卷中第2.9.1节“简单命令”的结尾:
如果没有命令名称,但命令包含命令替换,则该命令应完成,并具有上次执行的命令替换的退出状态
0s0u357o3#
请注意,当与
local
结合使用时(如local *variable*="$(*command*)"
),情况并非如此。即使*command*
失败,该表单也将成功退出。以Bash脚本为例:
下面是它的输出:
这是因为
local
实际上是一个内置命令,像local *variable*="$(*command*)"
这样的命令在 * 替换*command*
的输出后调用local
*。因此,您可以从local
获取退出状态。anauzrmj4#
我昨天(2018年8月29日)遇到了同样的问题。
除了在Nick P.'s answer中提到的
local
和@sevko在accepted answer中的评论外,全局范围内的declare
也有相同的行为。下面是我的Bash代码:
输出:
注意上面输出中的
local_ret2
和global_ret2
的值。退出代码被local
和declare
覆盖。我的Bash版本:
6jygbczu5#
(not对原始问题的答复,但太长,无法评论)
注意,
export A=$(false); echo $?
输出0!显然,devnull's answer中引用的规则不再适用。为这句话添加一点上下文(强调我的):3.7.1简单的命令扩展
...
如果在展开后还有命令名,则执行将按 * 如下所述 * 进行。否则,命令退出。如果其中一个扩展包含命令替换,则命令的退出状态是执行的最后一个命令替换的退出状态。如果没有命令替换,则命令以零状态退出。
3.7.2命令搜索和执行[ - this is the“below”case]
IIUC手册将
var=foo
描述为var=foo command...
语法的特殊情况(非常混乱!“最后命令替换的退出状态”规则仅适用于无命令的情况。虽然很容易将
export var=foo
视为“修改的赋值语法”,但事实并非如此-export
是一个内置命令(只是碰巧接受类似赋值的参数)。=>如果要导出var AND capture命令替换状态,请分两个阶段执行:
这种方式也适用于
set -e
模式-如果命令替换返回非0,则立即退出。