7

In Bash, I would like an if statement which is based of the exit code of running a command. For example:

#!/bin/bash

if [ ./success.sh ]; then
    echo "First: success!"
else
    echo "First: failure!"
fi

if [ ./failure.sh ]; then
    echo "Second: success!"
else
    echo "Second: failure!"
fi

success.sh

#!/bin/bash

exit 0

failure.sh

#!/bin/bash

exit 1

This should print out:

First: success!
Second: failure!

How would I achieve this? Thanks!

Luke
  • 1,660
  • 1
  • 18
  • 43
  • You should remove the *signals* tag from your post. There is no such thing like an *exit **signal***, and your post is about *exit **codes***, not signals. Asking with which signal a program was terminated, would also be a meaningful question, but this is not what you have asked. – user1934428 Apr 16 '18 at 11:38
  • See this: [How to check the exit status using an if statement](https://stackoverflow.com/questions/26675681). – mivk Nov 24 '20 at 15:06
  • Relevant: https://shapeshed.com/unix-exit-codes/ – Ioannis Filippidis May 13 '21 at 05:46

2 Answers2

18

Just remove the brackets:

#!/bin/bash

if ./success.sh; then
    echo "First: success!"
else
    echo "First: failure!"
fi

if ./failure.sh; then
    echo "Second: success!"
else
    echo "Second: failure!"
fi

Explanation: the thing that goes between if and then is a command (or series of commands), the exit status of which is used to determine whether to run the then clause, or the else clause. This is exactly what you want.

So why do people use brackets in if statements? It's because normally you want to decide which branch of the if to run based on some conditional expression (is "$a" equal to "$b", does a certain file exist, etc). [ is actually a command which parses its arguments as a conditional expression (ignoring the final ]), and then exits with either success or failure depending on whether the conditional is true or false. Essentially, [ ] functions as an adapter that lets you use conditional expressions instead of command success/failure in your if statements. In your case, you want success/failure not a conditional expression, so don't use the adapter.

BTW, you'll also sometimes see if [[ some expression ]]; then and if (( some expression )); then. [[ ]] and (( )) are conditional expressions built into bash syntax (unlike [, which is a command). [[ ]] is essentially a better version of [ ] (with some syntax oddities cleaned up and some features added), and (( )) is a somewhat similar construct that does arithmetic expressions.

BTW2 another thing you'll see in scripts is the exit status being tested by checking the special parameter $?, which gives the exit status of the last command. It looks like this:

somecommand
if [ $? -eq 0 ]; then
    echo "Somecommand: success!"
else
    echo "Somecommand: failure!"
fi

I really consider this cargo cult programming. People are used to seeing [ ] conditional expressions in if statements, and this idiom puts the success test in the form of a conditional expression. But let me run through how it works: it takes the exit status of the command, puts it in a conditional expression, has [ ] evaluate that and turn it right back into an exit status so if can use it. That whole rigamarole is unnecessary; just put the command directly in the if statement.

Gordon Davisson
  • 95,980
  • 14
  • 99
  • 125
  • 2
    Note that there is something a bit weird in the way bash `if` works. An exit code of zero causes bash to take the `if` branch, and a non-zero exit code causes bash to take the `else` branch. That's backwards from other languages, where zero is 'falsy' and non-zero is 'truthy'. http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html – Christian Long Oct 16 '19 at 01:25
  • 4
    @ChristianLong Yep, it's confusing all right. The shell convention works this way because an exit code of 0 indicates that the command succeeded, and success is 'truthy', so (in exit code context) 0 has to be 'truthy' (and nonzero indicates failure, so has to be 'falsy'). IMO it's easiest to think in terms of command success vs failure instead of zero vs nonzero; that's another advantage of putting the command directly in the `if` statement. – Gordon Davisson Oct 16 '19 at 01:52
0

In addition to the accepted answer, note that you sometimes may wish to preserve the exit status for later use. In such cases, you can save it into a variable and then test that variable.

Here is an example using your commands and the Bash arithmetic (( )) command for tests :

#!/bin/bash

./success.sh
es1=$?

echo "exit status = $es1"
if (( es1 == 0 )); then
    echo "First: success! (es1 = $es1)"
else
    echo "First: failure! (es1 = $es1)"
fi

./failure.sh
es2=$?

echo "exit status = $es2"
if (( es2 > 0 )); then
    echo "Second: failure! ($es2 is greater than 0)"
else
    echo "Second: success!"
fi
mivk
  • 9,883
  • 3
  • 55
  • 54