Exit code of variable assignment to command substitution in Bash

前端 未结 4 1126
不知归路
不知归路 2020-12-01 04:57

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

a=$(false); echo $?
<         


        
相关标签:
4条回答
  • 2020-12-01 05:24

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

    In addition to local mentioned in Nick P.'s answer and @sevko's comment in the accepted answer, declare in global scope also has the same behavior.

    Here's my Bash code:

    #!/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_ret2 and global_ret2 in the output above. The exit codes are overwritten by local and declare.

    My Bash version:

    $ echo $BASH_VERSION 
    4.4.19(1)-release
    
    0 讨论(0)
  • 2020-12-01 05:31

    (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 answer no longer apply. To add a bit of context to that quote (emphasis mine):

    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]

    IIUC the manual describes var=foo as 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.

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

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

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

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

    0 讨论(0)
  • 2020-12-01 05:32

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

    Take this Bash script for example:

    #!/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 local is actually a builtin command, and a command like local variable="$(command)" calls local after substituting the output of command. So you get the exit status from local.

    0 讨论(0)
  • 2020-12-01 05:38

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

    When you say:

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

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

    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 a to be returned which is 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:

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

    ...

    output=$(command)

    status=$?

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

    0 讨论(0)
提交回复
热议问题