Linux Bash 中命令替换变量赋值的退出代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20157938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:25:48  来源:igfitidea点击:

Exit code of variable assignment to command substitution in Bash

linuxbashshellexit-code

提问by Reorx

I am confused about what error code the command will return when executing a variable assignment plainly and with command substitution:

我对在执行变量赋值和命令替换时命令将返回什么错误代码感到困惑:

a=$(false); echo $?

It outputs 1, which let me think that variable assignment doesn't sweep or produce new error code upon the last one. But when I tried this:

它输出1,这让我认为变量赋值不会在最后一个时清除或产生新的错误代码。但是当我尝试这样做时:

false; a=""; echo $?

It outputs 0, obviously this is what a=""returns and it override 1returned by false.

它输出0,显然这是a=""返回,并能战胜1由返回false

I want to know why this happens, is there any particularity in variable assignment that differs from other normal commands? Or just be cause a=$(false)is considered to be a single command and only command substitution part make sense?

我想知道为什么会发生这种情况,变量赋值与其他普通命令有什么特殊性吗?或者只是因为a=$(false)被认为是单个命令并且只有命令替换部分才有意义?

-- UPDATE --

- 更新 -

Thanks everyone, from the answers and comments I got the point "When you assign a variable using command substitution, the exit status is the status of the command." (by @Barmar), this explanation is excellently clear and easy to understand, but speak doesn't precise enough for programmers, I want to see the reference of this point from authorities such as TLDP or GNU man page, please help me find it out, thanks again!

谢谢大家,从答案和评论中我明白了“当您使用命令替换分配变量时,退出状态是命令的状态。” (by @Barmar),这个解释非常清晰易懂,但对程序员来说还不够准确,我想从 TLDP 或 GNU 手册页等权威机构那里看到这一点的参考,请帮我找到它出来,再次感谢!

采纳答案by devnull

Upon executing a command as $(command)allows the output of the command to replace itself.

在执行命令时$(command)允许命令的输出替换自身

When you say:

当你说:

a=$(false)             # false fails; the output of false is stored in the variable a

the output produced by the command falseis stored in the variable a. Moreover, the exit code is the same as produced by the command. help falsewould tell:

命令产生的输出false存储在变量中a。此外,退出代码与命令生成的代码相同。 help false会告诉:

false: false
    Return an unsuccessful result.

    Exit Status:
    Always fails.

On the other hand, saying:

另一方面说:

$ false                # Exit code: 1
$ a=""                 # Exit code: 0
$ echo $?              # Prints 0

causes the exit code for the assignment to ato be returned which is 0.

导致a返回分配的退出代码,即0.



EDIT:

编辑:

Quoting from the manual:

引用手册

If one of the expansions contained a command substitution, the exit status of the command is the exit status of the last command substitution performed.

如果其中一个扩展包含命令替换,则命令的退出状态是上次执行的命令替换的退出状态。

Quoting from BASHFAQ/002:

引自BASHFAQ/002

How can I store the return value and/or output of a command in a variable?

...

output=$(command)

status=$?

The assignment to outputhas no effect on command's exit status, which is still in $?.

如何将命令的返回值和/或输出存储在变量中?

...

output=$(command)

status=$?

对 的分配对的退出状态output没有影响command,该状态仍处于$?.

回答by Beni Cherniavsky-Paskin

(not an answer to original question but too long for comment)

(不是原始问题的答案,但评论太长了)

Note that export A=$(false); echo $?outputs 0! Apparently the rules quoted in devnull's answerno longer apply. To add a bit of context to that quote (emphasis mine):

注意export A=$(false); echo $?输出 0! 显然,devnull 的答案中引用的规则不再适用。为该引用添加一些上下文(强调我的):

3.7.1 Simple Command Expansion

...

If there is a command name left after expansion, execution proceeds as described below. Otherwise, the command exits. If one of the expansions contained a command substitution, the exit status of the command is the exit status of the last command substitution performed. If there were no command substitutions, the command exits with a status of zero.

3.7.2 Command Search and Execution [ — this is the "below" case]

3.7.1 简单命令扩展

...

如果有膨胀后留下了一个命令的名称,则继续执行描述如下否则,命令退出。如果其中一个扩展包含命令替换,则命令的退出状态是上次执行的命令替换的退出状态。如果没有命令替换,则命令以零状态退出。

3.7.2 命令搜索和执行 [——这是“下面”的情况]

IIUC the manual describes var=fooas special case of var=foo command...syntax (pretty confusing!). The "exit status of the last command substitution" rule only applies to the no-command case.

IIUC 手册将其描述var=foovar=foo command...语法的特殊情况(非常令人困惑!)。“最后一个命令替换的退出状态”规则仅适用于无命令的情况。

While it's tempting to think of export var=fooas a "modified assignment syntax", it isn't — exportis a builtin command (that just happens to take assignment-like args).

虽然很容易将其export var=foo视为“修改后的赋值语法”,但它不是——export是一个内置命令(恰好采用类似赋值的 args)。

=> If you want to export a var AND capture command substitution status, do it in 2 stages:

=> 如果要导出 var AND 捕获命令替换状态,请分 2 个阶段进行:

A=$(false)
# ... check $?
export A

This way also works in set -emode — exits immediately if the command substitution return non-0.

这种方式也适用于set -e模式 - 如果命令替换返回非 0,则立即退出。

回答by Nick P.

Note that this isn't the case when combined with local, as in local variable="$(command)". That form will exit successfully even if commandfailed.

请注意,与local,组合时情况并非如此,如。即使失败,该表单也会成功退出。local variable="$(command)"command

Take this Bash script for example:

以这个 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

Here is the output of this:

这是输出:

nick.parry@nparry-laptop1:~$ ./tmp.sh 
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1

This is because localis actually a builtin command, and a command like local variable="$(command)"calls localaftersubstituting the output of command. So you get the exit status from local.

这是因为local实际上是一个内置命令,并且是一个类似调用的命令替换的输出。所以你从.local variable="$(command)"localcommandlocal

回答by Zhi Zhu

I came across the same problem yesterday (Aug 29 2018).

我昨天(2018 年 8 月 29 日)遇到了同样的问题。

In addition to localmentioned in Nick P.'s answerand @sevko's comment in the accepted answer, declarein global scope also has the same behavior.

除了localNick P. 的回答和@sevko 在接受的回答中的评论中提到之外,declare在全球范围内也有相同的行为。

Here's my Bash code:

这是我的 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"

The output:

输出:

$ ./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

Note the values of local_ret2and global_ret2in the output above. The exit codes are overwritten by localand declare.

注意上面输出中local_ret2和的值global_ret2。退出代码被local和覆盖declare

My Bash version:

我的 Bash 版本:

$ echo $BASH_VERSION 
4.4.19(1)-release