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

时间:2013-11-23 02:52:25

标签: linux bash shell exit-code

我很担心命令在执行变量赋值时会返回什么错误代码,并且命令替换:

a=$(false); echo $?

它输出1,这让我认为变量赋值不会扫描或产生最后一个错误代码。但是当我尝试这个时:

false; a=""; echo $?

它会输出0,显然这是a=""返回的内容,它会覆盖1返回的false

我想知道为什么会发生这种情况,变量赋值中是否有任何与其他正常命令不同的特殊性?或者只是因为a=$(false)被认为是单个命令而只有命令替换部分才有意义?

- 更新 -

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

4 个答案:

答案 0 :(得分:54)

执行$(command)命令时,允许output of the command to replace itself

当你说:

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


编辑:

引用manual

  

如果其中一个扩展包含命令替换,则退出   命令的状态是最后一个命令的退出状态   替换。

引自BASHFAQ/002

  

如何在a中存储命令的返回值和/或输出   可变吗

     

...

     

output=$(command)

     

status=$?

     

output的作业对command的退出状态没有影响   仍在$?

答案 1 :(得分:9)

请注意,在函数中使用local时不是这种情况。这与在接受的答案中描述的行为略有不同,以及此处发布的链接:http://mywiki.wooledge.org/BashFAQ/002

以这个bash脚本为例:

#!/bin/bash
function funWithLocalVar() {
    local output="$(echo "Doing some stuff.";exit 1)"
    local exitCode=$?
    echo "output: $output"
    echo "exitCode: $exitCode"
}

function funWithoutLocalVar() {
    output="$(echo "Doing some stuff.";exit 1)"
    local exitCode=$?
    echo "output: $output"
    echo "exitCode: $exitCode"
}

funWithLocalVar
funWithoutLocalVar

以下是此输出:

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

也许没有人关心,但我做到了。我花了一分钟时间弄清楚为什么我的状态代码总是为0时显然不是。不是100%明白为什么。但只是知道这有帮助。

答案 2 :(得分:1)

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

除了Nick P.'s answer中提到的localaccepted answer中@sevko的评论外,全局范围内的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_ret2global_ret2的值。退出代码被localdeclare覆盖。

我的Bash版本:

$ echo $BASH_VERSION 
4.4.19(1)-release

答案 3 :(得分:0)

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

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

  

3.7.1简单命令扩展

     

...

     

如果扩展后还有一个命令名称,则执行按中的所述进行。 否则,该命令退出。如果其中一个扩展包含命令替换,则该命令的退出状态是执行的最后一个命令替换的退出状态。如果没有命令替换,则命令退出状态为零。

     

3.7.2命令搜索和执行[ - 这是“下面”案例]

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

虽然将export var=foo视为“已修改的赋值语法”很诱人,但它不是 - export是内置命令(恰好采用类似赋值的args)。

=>如果要导出var AND capture命令替换状态,请分两个阶段执行:

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

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