proper way to detect shell exit code when errexit option set

后端 未结 14 1508
甜味超标
甜味超标 2021-01-30 12:52

I prefer to write solid shell code, so the errexit & nounset is alway set.

The following code will stop at bad_command line

#!/bin/b         


        
相关标签:
14条回答
  • 2021-01-30 13:35

    The accepted answer is good, but I think it could be refactored to be even better; more generic, easier to refactor and read:

    some_command_status=$(some_command && echo $? || echo $?)
    

    vs.

    some_command && some_command_status=$? || some_command_status=$?
    
    0 讨论(0)
  • 2021-01-30 13:37

    Continue to write solid shell code.

    You do it right if bad_command is really a command. But be careful with function calls in if, while, ||, && or !, because errexit will not work there. It may be dangerous.

    If your bad_command is actually bad_function you should write this:

    set -eu 
    
    get_exit_code() {
        set +e
        ( set -e;
          "$@"
        )
        exit_code=$?
        set -e
    }
    
    ...
    
    get_exit_code bad_function
    
    if [ "$exit_code" != 0 ]; then
        do_err_handle
    fi
    

    This works well in bash 4.0. In bash 4.2 you only get exit codes 0 or 1.

    0 讨论(0)
  • 2021-01-30 13:37

    A slight variation of the answer given by @rrauenza. Since the && rc=$? part is their answer will always be equal to && rc=0 one can as well set rc to 0 before running the command. The result ends up more readable in my opinion because the variable is defined upfront in its own line of code and is only changed if the command exits with a non-zero exit status. If nounset is also given, then it's now clear that rc was indeed never undefined. This also avoids mixing && and || in the same line which might be confusing because one might not always know the operator precedence by heart.

    #!/bin/sh                                                                       
    set -eu
    
    rc=0
    cat /tmp/doesnotexist || rc=$?                                         
    echo exitcode: $rc        
    
    rc=0
    cat /dev/null || rc=$?                                                 
    echo exitcode: $rc   
    

    Output:

    cat: /tmp/doesnotexist: No such file or directory
    exitcode: 1
    exitcode: 0
    
    0 讨论(0)
  • 2021-01-30 13:39

    I cobbled together a (hopefully) textbook example from all the answers:

    #!/usr/bin/env bash
    
    # exit immediately on error
    set -o errexit
    
    file='dfkjlfdlkj'
    # Turn off 'exit immediately on error' during the command substitution
    blah=$(set +o errexit && ls $file) && rc=$? || rc=$?
    echo $blah
    # Do something special if $rc
    (( $rc )) && echo failure && exit 1
    echo success
    
    0 讨论(0)
  • 2021-01-30 13:44

    In case you want to detect the exit code of a compound list (or a function), and have errexit and nounset applied there, you can use this kind of code:

    #!/bin/sh
    set -eu
    unset -v unbound_variable
    
    f() {
        echo "before false"
        false
        echo "after false"
    }
    
    g() {
        echo "before unbound"
        var=$unbound_variable
        echo "after unbound"
    }
    
    set +e
    (set -e; f)
    echo error code of f = $?
    set -e
    
    echo still in main program
    
    set +e
    (set -e; g)
    echo error code of g = $?
    set -e
    
    echo still in main program
    

    The above should print non-zero error codes for both functions f and g. I suppose this works by any POSIX shell. You can also detect the error code in EXIT trap, but the shell exits thereafter. The problem with other proposed methods is that the errexit setting is ignored when an exit status of such a compound list is tested. Here is a quote from the POSIX standard:

    The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last.

    Note that if you have defined your function like

    f() (
        set -e
        ...
    )
    

    it is enough to do

    set +e
    f
    echo exit code of f = $?
    set -e
    

    to get the exit code.

    0 讨论(0)
  • 2021-01-30 13:46

    How about this? If you want the actual exit code ...

    #!/bin/sh                                                                       
    set -e
    
    cat /tmp/doesnotexist && rc=$? || rc=$?                                         
    echo exitcode: $rc        
    
    cat /dev/null && rc=$? || rc=$?                                                 
    echo exitcode: $rc   
    

    Output:

    cat: /tmp/doesnotexist: No such file or directory
    exitcode: 1
    exitcode: 0
    
    0 讨论(0)
提交回复
热议问题