Skip to content
Lawrence Velázquez edited this page Mar 9, 2024 · 14 revisions

Check exit code directly with e.g. if mycmd;, not indirectly with $?.

Problematic code:

make mytarget

if [ $? -ne 0 ]
then
  echo "Build failed"
fi

Correct code:

if ! make mytarget
then
  echo "Build failed"
fi

For the Solaris 10 Bourne shell:

if make mytarget
then
  :
else
  echo "Build failed"
fi

Rationale:

Running a command and then checking its exit status $? against 0 is redundant.

Instead of just checking the exit code of a command, it checks the exit code of a command (e.g. [) that checks the exit code of a command.

Apart from the redundancy, there are other reasons to avoid this pattern:

  • Since the command and its status test are decoupled, inserting an innocent command like echo "make finished" after make will cause the if statement to silently start comparing echo's status instead.
  • Scripts that run or are called with set -e aka errexit will exit immediately if the command fails, even though they're followed by a clause that handles failure.
  • The value of $? is overwritten by [/[[, so you can't get the original value in the relevant then/else block (e.g. if mycmd; then echo "Success"; else echo "Failed with $?"; fi).

To check that a command returns success, use if mycommand; then ....

To check that a command returns failure, use if ! mycommand; then .... Notice that ! will overwrite $? value.

To additionally capture output with command substitution: if ! output=$(mycommand); then ...

This also applies to while/until loops.

The default Solaris 10 Bourne shell does not support negating exit statuses with !, so ! mycommand tries to invoke a utility named "!" instead. To test for failure, use if mycommand; then :; else ...; fi and until mycommand; do ...; done.

Exceptions:

None.

ShellCheck

Each individual ShellCheck warning has its own wiki page like SC1000. Use GitHub Wiki's "Pages" feature above to find a specific one, or see Checks.

Clone this wiki locally