proper way to detect shell exit code when errexit

2019-03-08 13:50发布

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

The following code will stop at bad_command line

#!/bin/bash
set -o errexit ; set -o nounset
bad_command # stop here
good_command

I want to capture it, here is my method

#!/bin/bash
set -o errexit ; set -o nounset
rc=1
bad_command && rc=0 # stop here
[ $rc -ne 0 ] && do_err_handle
good_command

Is there any better or cleaner method

My Answer:

#!/bin/bash
set -o errexit ; set -o nounset
if ! bad_command ; then
  # error handle here
fi
good_command

标签: shell
11条回答
Bombasti
2楼-- · 2019-03-08 14:02

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.

查看更多
姐就是有狂的资本
3楼-- · 2019-03-08 14:10
set -o errexit

bad_command || {
  resp_code=$?
  echo Bad Thing $resp_code happened
}
查看更多
我想做一个坏孩纸
4楼-- · 2019-03-08 14:15

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
查看更多
ら.Afraid
5楼-- · 2019-03-08 14:15

A common way to avoid exiting a Bash program when errexit is set and a command that may fail is run is to precede the command with !.

After the command has run $? does not contain the exit status of the command. (It contains 0 if the command failed and 1 otherwise.) However, the PIPESTATUS array does contain the exit status of the command. A safe way to capture the exit status of a command that may fail, whether or not errexit is set, is:

! bad_command
rc=${PIPESTATUS[0]}

The second line can be simplified to rc=$PIPESTATUS, but Shellcheck will complain about it.

If (as is often the case) you don't need to know the exit status of the command, just if it succeeded or failed, then the solution by @george is good if the error handler is a one-liner. For multi-line error handlers a good option is:

if ! bad_command ; then
    # Handle errors here
fi

Note that (except in very unusual circumstances) it would not be correct to use `bad_command` (as suggested in the question) instead of plain bad_command. You can use ${PIPESTATUS[0]} to get the exit status of the command if it is needed in the error handling code, since $? doesn't contain it in this case either.

查看更多
beautiful°
6楼-- · 2019-03-08 14:16

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=$?
查看更多
倾城 Initia
7楼-- · 2019-03-08 14:17

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
查看更多
登录 后发表回答