In a Bash script, how can I exit the entire script if a certain condition occurs?
Solution 1
Try this statement:
exit 1
Replace 1
with appropriate error codes. See also Exit Codes With Special Meanings.
Solution 2
Use set -e
#!/bin/bash
set -e
/bin/command-that-fails
/bin/command-that-fails2
The script will terminate after the first line that fails (returns nonzero exit code). In this case, command-that-fails2 will not run.
If you were to check the return status of every single command, your script would look like this:
#!/bin/bash
# I'm assuming you're using make
cd /project-dir
make
if [[ $? -ne 0 ]] ; then
exit 1
fi
cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
exit 1
fi
With set -e it would look like:
#!/bin/bash
set -e
cd /project-dir
make
cd /project-dir2
make
Any command that fails will cause the entire script to fail and return an exit status you can check with $?. If your script is very long or you're building a lot of stuff it's going to get pretty ugly if you add return status checks everywhere.
Solution 3
A SysOps guy once taught me the Three-Fingered Claw technique:
yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }
These functions are *NIX OS and shell flavor-robust. Put them at the beginning of your script (bash or otherwise), try()
your statement and code on.
Explanation
(based on flying sheep comment).
-
yell
: print the script name and all arguments tostderr
:-
$0
is the path to the script ; -
$*
are all arguments. -
>&2
means>
redirect stdout to & pipe2
. pipe1
would bestdout
itself.
-
-
die
does the same asyell
, but exits with a non-0 exit status, which means “fail”. -
try
uses the||
(booleanOR
), which only evaluates the right side if the left one failed.-
$@
is all arguments again, but different.
-
Solution 4
If you will invoke the script with source
, you can use return <x>
where <x>
will be the script exit status (use a non-zero value for error or false). But if you invoke an executable script (i.e., directly with its filename), the return statement will result in a complain (error message "return: can only `return' from a function or sourced script").
If exit <x>
is used instead, when the script is invoked with source
, it will result in exiting the shell that started the script, but an executable script will just terminate, as expected.
To handle either case in the same script, you can use
return <x> 2> /dev/null || exit <x>
This will handle whichever invocation may be suitable. That is assuming you will use this statement at the script's top level. I would advise against directly exiting the script from within a function.
Note: <x>
is supposed to be just a number.
Solution 5
I often include a function called run() to handle errors. Every call I want to make is passed to this function so the entire script exits when a failure is hit. The advantage of this over the set -e solution is that the script doesn't exit silently when a line fails, and can tell you what the problem is. In the following example, the 3rd line is not executed because the script exits at the call to false.
function run() {
cmd_output=$(eval $1)
return_value=$?
if [ $return_value != 0 ]; then
echo "Command $1 failed"
exit -1
else
echo "output: $cmd_output"
echo "Command succeeded."
fi
return $return_value
}
run "date"
run "false"
run "date"
samoz
I am a cryptographer, working in the West Lafayete, IN area. My interests include reverse engineering, information security, ellipic curves, trusted computing environments, operating systems, and embedded systems. Most of my reversing is on Windows, but I like to practice my make-fu and Unix-fu on Arch Linux.
Updated on July 10, 2022Comments
-
samoz almost 2 years
I'm writing a script in Bash to test some code. However, it seems silly to run the tests if compiling the code fails in the first place, in which case I'll just abort the tests.
Is there a way I can do this without wrapping the entire script inside of a while loop and using breaks? Something like a dun dun dun goto?
-
Adobe over 11 yearsWith
set -e
You still can make some commands exit with errors without stopping the script:command 2>&1 || echo $?
. -
Tor Klingberg over 11 yearsI think this will not catch errors if you have commands like
(cd tmp && make)
-
Mikko Rantalainen almost 11 years
set -e
will abort the script if a pipeline or command structure returns non-zero value. For examplefoo || bar
will fail only if bothfoo
andbar
return non-zero value. Usually a well written bash script will work if you addset -e
at the start and the addition works as an automated sanity check: abort the script if anything goes wrong. -
Jake Biesinger over 10 yearsIf you're piping commands together, you can also fail if any of them fails by setting the
set -o pipefail
option. -
tripleee over 10 yearsActually the idiomatic code without
set -e
would be justmake || exit $?
. -
Michał Górny about 10 years@CMCDragonkai, usually any non-zero code will work. If you don't need anything special, you can just use
1
consistently. If the script is meant to be run by another script, you may want to define your own set of status code with particular meaning. For example,1
== tests failed,2
== compilation failed. If the script is part of something else, you may need to adjust the codes to match the practices used there. For example, when part of test suite run by automake, the code77
is used to mark a test skipped. -
Admin over 9 yearsMan, for some reason, I really like this answer. I recognize it's a bit more complicated, but it seems so useful. And given that I'm no bash expert, it leads me to believe that my logic is faulty, and there's something wrong with this methodology, otherwise, I feel others would have given it more praise. So, what's the problem with this function? Is there anything I should be looking out for here?
-
Joseph Sheedy over 9 yearsI don't recall my reason for using eval, the function works fine with cmd_output=$($1)
-
kaizenCoder almost 9 yearsI'm fairly new to unix scripting. Can you please explain how the above functions are executed? I'm seeing some new syntax that I'm not familiar with. Thanks.
-
flying sheep almost 9 yearsyell:
$0
is the path to the script.$*
are all arguments.>&2
means “>
redirect stdout to&
pipe2
”. pipe 1 would be stdout itself. so yell prefixes all arguments with the script name and prints to stderr. die does the same as yell, but exits with a non-0 exit status, which means “fail”. try uses the boolean or||
, which only evaluates the right side if the left one didn’t fail.$@
is all arguments again, but different. hope that explains everything -
Anentropic over 8 yearsif I do
command -that --fails || exit $?
it works without parentheses, what is it aboutecho $[4/0]
that causes us to need them? -
imiric almost 8 yearsJust curious, but why exit code
111
instead of1
? Does it have some special meaning I'm not aware of? Searching yielded nothing. -
Mark Lakata over 7 years@imiric -I think exit code 111 for no specific reason, but the conventions are that success is 0, and small integers are normal failures, like 1,2,3. The exit code is limited to 8 bits, but there is ambiguity as to whether it is signed or not, so most people stick with 0-127 to be safe. So 111 is a big number, far from the other end, that means "exceptional" failure.
-
Mark Lakata over 7 yearsI modified this to be
die() { yell "$1"; exit $2; }
so that you can pass a message and exit code withdie "divide by zero" 115
. -
Pablo Bianchi about 7 years
-
fuzzygroup almost 7 yearsI just implemented this as part of a complex deploy process and it worked fantastic. Thank you and here's a comment and an upvote.
-
eventhorizon over 6 yearsKeep in mind that subscripts (scripts in their own bash "instance") like (command) need some help to return a failure like (set -e; command) or (command || exit $?)
-
Toni Leigh about 6 yearsno this closes the window too, not just exit the script
-
Michael Foukarakis about 6 years@ToniLeigh bash does not have the concept of a "window", you are probably confused with regard to what your particular set-up - e.g. a terminal emulator - does.
-
Tony-Caffe almost 6 yearsTruly amazing work! This is the simplest and cleanest solution that works well. For me I added this before a command in a FOR loop since FOR loops will not pick up the
set -e option
. Then the command, since it is with arguments, I used single quotes to avoid bash issues like so:runTry 'mysqldump $DB_PASS --user="$DB_USER" --host="$BV_DB_HOST" --triggers --routines --events --single-transaction --verbose $DB_SCHEMA $tables -r $BACKUP_DIR/$tables$BACKUP_FILE_NAME'
. Note I changed the name of the function to runTry. -
dragon788 almost 6 years
eval
is potentially dangerous if you accept arbitrary input, but otherwise this looks pretty nice. -
kshenoy over 5 yearsI can see how I'd use
yell
anddie
. However,try
not so much. Can you provide an example of you use it? -
jan over 5 yearsDoesn't work for me in a script with an return/exit inside a function, i.e. is it possible to exit inside the function without existing the shell, but without making the function caller care for the correct check of the return code of the function?
-
kavadias over 5 years@jan What the caller does (or does not do) with return values, is completely orthogonal (i.e., independent of) how you return from the function (... w/o exiting the shell, no matter what the invocation). It mainly depends on the caller code, which is not part of this Q&A. You could even tailor the return value of the function to the needs of the caller, but this answer does not limit what this return value can be...
-
bobbogo about 5 years@TorKlingberg The exit code of
(echo hello; make)
is the exit code of the make. A non-zero exit will therefore be caught under-e
-
bobbogo about 5 years@Anentropic @skalee The parentheses have nothing to do with precedence, but exception handling. A divide-by-zero will cause an immediate exit from the shell with code 1. Without the parentheses (i.e., a simple
echo $[4/0] || exit $?
) bash will never execute theecho
, let alone obey the||
. -
Engineer about 5 yearsIf I run a bash script with an exit in it, it goes to a login prompt.
-
Sergey Kotyushkin over 4 years@ToniLeigh If it's closing the "window" likely you're putting the
exit #
command inside a function, not a script. (In which case usereturn #
instead.) -
TheJavaGuy-Ivan Milosavljević about 4 yearsHmm, but how do you use them in a script? I don't understand what you mean by "try() your statement and code on".
-
Ryan about 3 years@TheJavaGuy-IvanMilosavljević once you've added those three lines to the top of your script, you'd start using the
try
function with your code. Ex:try cp ./fake.txt ./copy.txt
-
Starman about 3 yearsThis should be the accepted answer. The Q did not specify if it was using "source script.sh" or "./script.sh". People use both, frequently. The currently accepted answer does not work if you use source. This answer works for both cases.
-
Gagik about 3 yearsFor short scripts
read -r
command can also be an alternative toexit
if only script is always being executed in front of the terminal user, in order to give user choice to terminate the execution or continue with error. -
Soren Bjornstad over 2 yearsYou have 9 and 15 mixed up. Signal 9 is SIGKILL, which is a “harder” exit that forces immediate termination. Signal 15 is SIGTERM, which politely asks the program to terminate and lets it clean up if it wants.
-
rebane2001 over 2 years@ToniLeigh The
exit
command only exits the bash process the script is running in. If you run your script with./script.sh
orbash script.sh
, your "window" or shell will stay open, because a new bash process is created for the script. On the other hand, if you run your script with. script.sh
orsource script.sh
, it executes in your current bash instance and exits it instead.