What would be the best way to check the exit status in an if
statement in order to echo a specific output?
I'm thinking of it being:
if [ $? -eq 1 ]
then
echo "blah blah blah"
fi
The issue I am also having is that the exit
statement is before the if
statement simply because it has to have that exit code. Also, I know I'm doing something wrong since the exit would obviously exit the program.
some_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
Every command that runs has an exit status.
That check is looking at the exit status of the command that finished most recently before that line runs.
If you want your script to exit when that test returns true (the previous command failed) then you put exit 1
(or whatever) inside that if
block after the echo
.
That being said, if you are running the command and are wanting to test its output, using the following is often more straightforward.
if some_command; then
echo command returned true
else
echo command returned some error
fi
Or to turn that around use !
for negation
if ! some_command; then
echo command returned some error
else
echo command returned true
fi
Note though that neither of those cares what the error code is. If you know you only care about a specific error code then you need to check $?
manually.
Note that exit codes != 0 are used to report errors. So, it's better to do:
retVal=$?
if [ $retVal -ne 0 ]; then
echo "Error"
fi
exit $retVal
instead of
# will fail for error codes == 1
retVal=$?
if [ $retVal -eq 1 ]; then
echo "Error"
fi
exit $retVal
dnf
developers have chosen this way, it's their choice. But still, their choice doesn't make the specification to be broken :)
# will fail for error codes > 1
but $retVal -eq 1
checks for error code equal 1?
An alternative to an explicit if
statement
Minimally:
test $? -eq 0 || echo "something bad happened"
Complete:
EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened";
exit $EXITCODE
$?
is a parameter like any other. You can save its value to use before ultimately calling exit
.
exit_status=$?
if [ $exit_status -eq 1 ]; then
echo "blah blah blah"
fi
exit $exit_status
For the record, if the script is run with set -e
(or #!/bin/bash -e
) and you therefore cannot check $?
directly (since the script would terminate on any return code other than zero), but want to handle a specific code, @gboffis comment is great:
/some/command || error_code=$?
if [ "${error_code}" -eq 2 ]; then
...
/some/command
is in the PATH? error_code
is not set.
/bin/mkdir
on an existing directory, that should return 1. Are you certain the command you tried did return an exit code other than 0? /usr/bin/file
on an non-existant file for example prints an error but still returns 0 🤷
|| is_fail=true; if [ "$is_fail" = true ]; then . . .
Just to add to the helpful and detailed answer:
If you have to check the exit code explicitly, it is better to use the arithmetic operator, (( ... ))
, this way:
run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }
Or, use a case
statement:
run_some_command; ec=$? # grab the exit code into a variable so that it can
# be reused later, without the fear of being overwritten
case $ec in
0) ;;
1) printf '%s\n' "Command exited with non-zero"; exit 1;;
*) do_something_else;;
esac
Related answer about error handling in Bash:
Raise error in a Bash script
$?
in a boolean check like that, regardless of whether you use (( $? != 0 ))
or [[ $? -ne 0 ]]
because it doesn't get parsed like normal vars do (related description).
(( $retVal )) && echo 'ERROR'
instead of (( $retVal != 0 ))
or [[ $retVal -ne 0 ]]
but that doesn't necessarily mean it's better. In fact, the shortcut I like to use would be confusing to anyone who doesn't know Bash all that well.
If you are writing a function – which is always preferred – you can propagate the error like this:
function()
{
if <command>; then
echo worked
else
return
fi
}
Now, the caller can do things like function && next
as expected! This is useful if you have a lot of things to do in the if
block, etc. (otherwise there are one-liners for this). It can easily be tested using the false
command.
Using Z shell (zsh
) you can simply use:
if [[ $(false)? -eq 1 ]]; then echo "yes" ;fi
When using Bash and set -e
is on, you can use:
false || exit_code=$?
if [[ ${exit_code} -ne 0 ]]; then echo ${exit_code}; fi
This might only be useful in a limited set of use-cases, I use this specifically when I need to capture the output from a command and write it to a log file if the exit code reports that something went wrong.
RESULT=$(my_command_that_might_fail)
if (exit $?)
then
echo "everything went fine."
else
echo "ERROR: $RESULT" >> my_logfile.txt
fi
Success story sharing
a_command || return 1
return
only works in a function and a sourced script. You needexit
for the other case (which does too much in a function and a sourced script). But yes, that's certainly a reasonable pattern if you don't need any specific cleanup or extra output.dash
, the default non-interactive shell in many modern linux distributions, don't care of the distinction betweenreturn
andexit
inside of executed shell scripts.dash
exits the script even if I usereturn
in it.if <command>
passes if the exit code is 0. In any other language it would be the other way aroundif ! some_command | some_other_command
will ignore the status of some_command. The two most command workarounds are toset -o pipefail
(may change functionality in other parts of your program) or to move theif
statement toif [[ ${PIPESTATUS[0]} -ne 0 ]]
as a separate follow-up command (ugly, but functional). If you're usingset -e
then you'll also want to add|| true
to the end of the pipe when using the second solution since removing the pipe from the control flow offered byif
would otherwise cause it to immediately exit.