How can I write a shell script that exits, if one part of it fails? For example, if the following code snippet fails, then the script should exit.
n=0
until [ $n -ge 5 ]
do
gksu *command* && break
n=$[$n+1]
sleep 3
One approach would be to add set -e
to the beginning of your script. That means (from help set
):
-e Exit immediately if a command exits with a non-zero status.
So if any of your commands fail, the script will exit.
Alternatively, you can add explicit exit
statements at the possible failure points:
command || exit 1
set -e
feature. It doesn't really apply here though. The OP wants to exit the script after 5 failing attempts to run the command.
Commented
Sep 12, 2016 at 9:50
set -e
is the only way I know to do that.
set -e
is sleep
(break
being a special builtin would cause the script to exit on failure in most shells, commands in if
or to the left of &&
are not affected by set -e
, n=...
could fail if n
is read-only, but then that would exit the script without set -e
as well), so that interpretation sounds quite unlikely. I agree the question is poorly worded.
Commented
Sep 12, 2016 at 10:46
You can exit a script at any place using the keyword exit
. You can also specify an exit code in order to indicate to other programs that or how your script failed, e.g. exit 1
or exit 2
etc. (By convention, exit code 0 is for success and anything greater than 0 signifies failure; however, also by convention, exit codes above 127 are reserved for abnormal termination (e.g. by a signal)).
The generic construction to exit on failure is
if [ failure condition ]; then
exit n
fi
with suitable failure condition
and n
. But in specific scenarios you may proceed differently. Now for your case I interpret your question that if any of the five invocations of gksu
fail, then you mean to exit. One way is to use a function like this
function try_command {
for i in 1 2 3 4 5 ; do
if gksu command ; then
return 0
fi
fi
exit 1
}
and then, invoke the loop by try_command
.
There are (more) advanced or sophisticated ways of how to address your question. However, the solution above is more accessible to beginners than, say, Stephane's solution.
attempt=0
until gksu command; do
attempt=$((attempt + 1))
if [ "$attempt" -gt 5 ]; then
exit 1
fi
done
exit
exits the script unless it's called in a subshell. If that part of the script is in a subshell, for instance because it's within (...)
or $(...)
or part of a pipe-line, then it will only exit that subshell.
In that case, if you want the script to exit in addition to the subshell, then you'll need to call exit
upon that subshell exiting.
For instance, here with 2 nested levels of subshells:
(
life=hard
output=$(
echo blah
[ "$life" = easy ] || exit 1 # exit subshell
echo blih not run
) || exit # if the subshell exits with a non-zero exit status,
# exit as well with the same exit status
echo not run either
) || exit # if the subshell exits with a non-zero exit status,
# exit as well with the same exit status
It can become trickier if the subshell is part of a pipeline. bash
has a special $PIPESTATUS
array, similar to zsh
's $pipestatus
one that can help you here:
{
echo foo
exit 1
echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
exit "$subshell_ret"
fi
Trap will perform an action upon receiving a signal.
trap "echo EXIT; exit" 0
trap "echo HUP; exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT; exit" 3
trap "echo ERR; exit" ERR
n=0
until [ $n -ge 5 ]
do
n=$[$n+1]
echo $n
sleep 3
done
Run this and let it exit normally. It traps on signal 0.
EXIT
Run it again and interrupt with ^C. It traps on both signal 2 and signal 0.
CTL-C
EXIT
A non-zero exit status will trap on ERR
ERR
EXIT
pass_to_functio()
{
echo "command exited with status $1"
}
(
exec <Any Command > &
child_pid=$!
wait $child_pid
pass_to_function $?
)&
I prefer doing it this way:
function exit_failed() {
echo ""
echo " ... batch failed !!!"
echo ""
exit 1
}
7z a $FN . || exit_failed
<call-something-else> || exit_failed