Wil*_*ard 14 bash command-substitution variable exit-status
我在脚本中看到过这样的结构:
if somevar="$(somecommand 2>/dev/null)"; then
...
fi
Run Code Online (Sandbox Code Playgroud)
这是在某处记录的吗?变量的返回状态是如何确定的,它与命令替换有什么关系?(例如,我会得到相同的结果if echo "$(somecommand 2>/dev/null)"; then吗?)
G-M*_*ca' 18
它在 Open Group Base Specifications 的第 2.9.1 节简单命令中有记录(针对 POSIX)。那里有一堵文字墙;我将你的注意力引向最后一段:
如果有命令名称,则应按照命令搜索和执行中的描述继续执行。如果没有命令名称,但命令包含命令替换,则该命令应以上次执行的命令替换的退出状态完成。否则,该命令将以零退出状态完成。
所以,例如,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar)$(quux) Exit status from "quux"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
Run Code Online (Sandbox Code Playgroud)
这也是 bash 的工作方式。但另请参阅最后的“不那么简单”部分。
phk,在他的问题中,分配就像具有退出状态的命令,除非有命令替换?, 建议
……似乎赋值本身算作一个命令……退出值为零,但它适用于赋值的右侧(例如,命令替换调用……)
这不是一种可怕的看待方式。?用于确定一个简单的命令的返回状态的粗方案(一个不含有;,&,|,&&或||α)为:
从左到右扫描该行,直到到达末尾或命令字(通常是程序名称)。
如果您看到变量赋值,则该行的返回状态可能只是 0。
如果您看到命令替换——即$(…)——从该命令中获取退出状态。
如果到达实际命令(不在命令替换中),请从该命令获取退出状态。
该行的返回状态是您遇到的最后一个数字。
命令替换作为命令的参数,例如,foo?$(bar)不计算在内;你从foo. 用phk 的符号来解释,这里的行为是
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Run Code Online (Sandbox Code Playgroud)
但这有点过于简单化了。总体退货状态来自
A=$( cmd 1 ) B=$( cmd 2 ) C=$( cmd 3 ) D=$( cmd 4 ) E=mc 2是从 的退出状态。在之后出现的分配分配不会整体退出状态设置为0。
cmd4E=D=
icarus在回答 phk 的问题时提出了一个重要观点:变量可以设置为只读。POSIX 标准第 2.9.1 节的倒数第三段说,
如果任何变量赋值尝试为在当前 shell 环境中设置了readonly属性的变量赋值(无论是否在该环境中进行赋值),都会发生变量赋值错误。有关这些错误的后果,请参阅Shell 错误的后果。
所以如果你说
A=$(cmd1) B=$(cmd2) C=$(cmd3) D=$(cmd4) E=mc2
返回状态为 1。字符串Garfield、Felix和/ 或Tigger
是否替换为命令替换并不重要- 但请参阅下面的注释。
第 2.8.1 节 Shell 错误的后果有另一串文本和一个表格,并以
在表中显示的所有交互式 shell 要求不退出的情况下,shell 不应对发生错误的命令执行任何进一步处理。
一些细节是有道理的;有些没有:
A=分配有时中止命令行,因为这最后一句,似乎说明。在上面的例子中,C设置为Garfield,但T没有设置(当然,也没有设置 A)。C=$(cmd1) A=$(cmd2) T=$(cmd3)cmd1cmd3cmd2在我的 bash 版本中,
只读 A C=某物A=某物T=某物 cmd 0
确实执行。特别是,cmd0
C=$( cmd 1 ) A=$( cmd 2 ) T=$( cmd 3 ) cmd 0执行 and ,但不执行。(请注意,这与它在没有命令时的行为相反。)并且它在 的环境中设置(以及)。我想知道这是否是 bash 中的错误。
cmd1cmd3cmd2TCcmd0
这个答案的第一段是指“简单命令”。 规范说,
“简单命令”是一系列可选的变量赋值和重定向,以任何顺序,可选地后跟单词和重定向,由控制运算符终止。
这些语句类似于我的第一个示例块中的语句:
readonly A
C=Garfield A=Felix T=Tigger
Run Code Online (Sandbox Code Playgroud)
前三个包括变量赋值,后三个包括命令替换。
但是一些变量赋值并不是那么简单。 bash(1)说,
赋值语句也可能出现作为参数传递给
alias,declare,typeset,export,readonly,和local内置命令(声明命令)。
对于export,POSIX 规范说,
退出状态
0
所有名称操作数均已成功导出。
>0至少无法导出一个名称,或者
-p指定了选项并发生错误。
POSIX 不支持local,但bash(1)说,
local不在函数内时使用是错误的。返回状态为 0,除非local在函数外使用、提供了无效名称或name是只读变量。
在两行之间阅读,我们可以看到声明命令如下
readonly A C=something A=something T=something cmd0
和
C=$(cmd1) A=$(cmd2) T=$(cmd3) cmd0
更像是
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
Run Code Online (Sandbox Code Playgroud)
只要他们忽略退出状态bar
,并给您基于主命令的退出状态(export,local,或foo)。所以我们有像
export FOO=$(bar)
Run Code Online (Sandbox Code Playgroud)
我们可以用
local FOO=$(bar)
Run Code Online (Sandbox Code Playgroud)
和
foo $(bar)
Run Code Online (Sandbox Code Playgroud)
幸运的是,ShellCheck捕获到错误并引发SC2155,它建议
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
Run Code Online (Sandbox Code Playgroud)
应该改为
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
Run Code Online (Sandbox Code Playgroud)
和
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Run Code Online (Sandbox Code Playgroud)
应该改为
export foo="$(mycmd)"
Run Code Online (Sandbox Code Playgroud)
信用和参考
我想到了连接命令替换的想法 —
$(bar)$(quux)— 从 Gilles 的回答 如何以与 pipefail 类似的方式让 bash 在反引号失败时退出?,其中包含许多与此问题相关的信息。
| 归档时间: |
|
| 查看次数: |
3257 次 |
| 最近记录: |