Bash 中变量赋值到命令替换的退出代码
- 2024-10-22 08:28:00
- admin 原创
- 84
问题描述:
我对在执行变量赋值和使用命令替换时命令将返回什么错误代码感到困惑:
a=$(false); echo $?
它输出1
,这让我认为变量赋值不会清除或在最后一个变量赋值时产生新的错误代码。但是当我尝试这样做时:
false; a=""; echo $?
它输出0
,显然这就是a=""
返回的内容,并且它会覆盖1
返回的内容false
。
我想知道为什么会发生这种情况,变量赋值有什么特殊性吗?与其他普通命令不同吗?或者只是因为它a=$(false)
被视为单个命令,只有命令替换部分有意义?
- 更新 -
谢谢大家,从答案和评论中我得到了这个观点:“当您使用命令替换分配变量时,退出状态就是命令的状态。”(来自@Barmar),这个解释非常清楚易懂,但是对于程序员来说说得不够精确,我想从TLDP或GNU手册页等权威机构那里看到对这一点的参考,请帮我找出来,再次感谢!
解决方案 1:
执行命令时,$(command)
允许命令的输出替换其自身。
当你说:
a=$(false) # false fails; the output of false is stored in the variable a
该命令产生的输出false
存储在变量中a
。此外,退出代码与命令产生的代码相同。 help false
会告诉:
false: false
Return an unsuccessful result.
Exit Status:
Always fails.
另一方面,说:
$ false # Exit code: 1
$ a="" # Exit code: 0
$ echo $? # Prints 0
导致返回分配的退出代码,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 节“简单命令”的结尾:
如果没有命令名称,但命令包含命令替换,则命令将以最后执行的命令替换的退出状态完成
解决方案 2:
请注意,与 结合使用时情况并非如此local
,如。即使失败,该表单也会成功退出。local variable="$(command)"
`command`
以这个 Bash 脚本为例:
#!/bin/bash
function funWithLocalAndAssignmentTogether() {
local output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
function funWithLocalAndAssignmentSeparate() {
local output
output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
funWithLocalAndAssignmentTogether
funWithLocalAndAssignmentSeparate
输出如下:
nick.parry@nparry-laptop1:~$ ./tmp.sh
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1
这是因为local
实际上是一个内置命令,而像 这样的命令在替换 的输出后会调用。因此您可以从 获得退出状态。local variable="$(command)"
`local
command`local
解决方案 3:
我昨天(2018 年 8 月 29 日)遇到了同样的问题。
除了在Nick P. 的回答和 @sevko 在接受的答案local
中的评论中提到的之外,在全局范围内也有同样的行为。declare
这是我的 Bash 代码:
#!/bin/bash
func1()
{
ls file_not_existed
local local_ret1=$?
echo "local_ret1=$local_ret1"
local local_var2=$(ls file_not_existed)
local local_ret2=$?
echo "local_ret2=$local_ret2"
local local_var3
local_var3=$(ls file_not_existed)
local local_ret3=$?
echo "local_ret3=$local_ret3"
}
func1
ls file_not_existed
global_ret1=$?
echo "global_ret1=$global_ret1"
declare global_var2=$(ls file_not_existed)
global_ret2=$?
echo "global_ret2=$global_ret2"
declare global_var3
global_var3=$(ls file_not_existed)
global_ret3=$?
echo "global_ret3=$global_ret3"
输出:
$ ./declare_local_command_substitution.sh 2>/dev/null
local_ret1=2
local_ret2=0
local_ret3=2
global_ret1=2
global_ret2=0
global_ret3=2
local_ret2
请注意上面输出中的和 的值global_ret2
。退出代码被local
和覆盖declare
。
我的 Bash 版本:
$ echo $BASH_VERSION
4.4.19(1)-release
解决方案 4:
(这不是对原始问题的回答,但太长了,无法评论)
请注意输出 0!显然, devnull 的答案export A=$(false); echo $?
中引用的规则不再适用。为该引文添加一些背景信息(重点是我的):
3.7.1 简单命令扩展
...
如果扩展后还有命令名,则执行按如下所述进行。否则,命令退出。如果其中一个扩展包含命令替换,则命令的退出状态是执行的最后一个命令替换的退出状态。如果没有命令替换,则命令以零状态退出。
3.7.2 命令搜索和执行[ —这是“以下”的情况]
IIUC 手册将其描述var=foo
为语法特例var=foo command...
(相当令人困惑,从语义上讲它们非常不同!)。“最后一个命令替换的退出状态”规则仅适用于无命令的情况。
尽管很容易将其视为export var=foo
“修改后的赋值语法”,但它并不是——它export
是一个内置命令(只是恰好采用了类似赋值的参数)。
=> 如果您想要导出 var 并捕获命令替换状态,请分两个阶段进行:
A=$(false)
# ... check $?
export A
这种方式在set -e
模式下也有效——如果命令替换返回非 0 则立即退出。
解决方案 5:
正如其他人所说,命令替换的退出代码是被替换命令的退出代码,因此
FOO=$(false)
echo $?
---
1
然而,出乎意料的是,export
在开头添加却产生了不同的结果:
export FOO=$(false)
echo $?
---
0
这是因为,虽然替代的命令false
失败,但export
命令成功,并且这是语句返回的退出代码。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件