When I read this answer about $? another question comes to mind.
Is there any best practice for how to use $? in bash?
Let's have a example:
We have a linear script and I we would like to know that all the command was executed ok. Do you think it is ok to call a small function (let's call it "did_it_work"), to check the error code and break if it's not.
#!/bin/bash
function did_it_work {
code=1ドル
if [ "$code" -ne "0" ]
then
echo "Error failure: code $code "
exit 1
fi
}
dir=some/path
mkdir -p $dir
did_it_work $?
cd $dir
did_it_work $?
run_some_command
did_it_work $?
This approach of course means that I have to manually solve the problem if there is any and rerun the script.
Do you think this is a good idea or is there some other best practice to do this?
/Thanks
3 Answers 3
One common way is:
die() {
IFS=' ' # make sure "$*" is joined with spaces
# output the arguments if any on stderr:
[ "$#" -eq 0 ] || printf '%s\n' "$*" 1>&2
exit 1
}
then you use it like this:
mkdir -p some/path || die "mkdir failed with status $?"
Or if you want it to include the exit status, you could change it to:
die() {
last_exit_status=$?
IFS=' '
printf '%s\n' "FATAL ERROR: $* (status $last_exit_status)" 1>&2
exit 1
}
and then using it is a bit easier:
mkdir -p some/path || die "mkdir failed"
When it fails, mkdir
will likely already have issued an error message, so that second one may be seen as redundant, and you could just do:
mkdir -p some/path || exit # with the same (failing) exit status as mkdir's
mkdir -p some/path || exit 1 # with exit status 1 always
(or use the first variant of die
above without argument)
Just in case you haven't seen command1 || command2
before, it runs command1
, and if command1
fails, it runs command2
.
So you can read it like "make the directory or die".
Your example would look like:
mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
some_command || die "some_command failed"
Or you can align the dies
further on the right so that the main code is more obvious.
mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
some_command || die "some_command failed"
Or on the following line when the command lines are long:
mkdir -p some/path ||
die "mkdir failed"
cd some/path ||
die "cd failed"
some_command ||
die "some_command failed"
Also, if you are going to use the name some/path
multiple times, store it in a variable so you don't have to keep typing it, and can easily change it if you need to. And when passing variable arguments to commands, make sure to use the --
option delimiter so that the argument is not taken as an option if it starts with -
.
dir=some/path
mkdir -p -- "$dir" || die "Cannot make $dir"
cd -P -- "$dir" || die "Cannot cd to $dir"
some_command || die "Cannot run some_command"
You could rewrite your code like this:
#!/bin/bash
function try {
"$@"
code=$?
if [ $code -ne 0 ]
then
echo "1ドル did not work: exit status $code"
exit 1
fi
}
try mkdir -p some/path
try cd some/path
try run_some_command
If you don't actually need to log the error code, but just whether the command succeeded or not, you can shorten try()
further like so:
function try {
if ! "$@"
then
echo "1ドル did not work"
exit 1
fi
}
-
You can also use the code in this format. <pre>function try {BillThor– BillThor2011年03月07日 17:40:26 +00:00Commented Mar 7, 2011 at 17:40
-
If you use this functionality inline and don't want to return from the true side you can replace
return $?
with the:
builtin.BillThor– BillThor2011年03月07日 17:50:36 +00:00Commented Mar 7, 2011 at 17:50
If you really want to exit
on an error and are using Bash, then you should also consider set -e
. From help set
:
-e Exit immediately if a command exits with a non-zero status.
This of course doesn't give you the flexibility of a did_it_work() function, but it is an easy way to make sure your bash script stops on an error without adding lots of calls to your new function.
-
set -e
is useful. There are some commands that return non-zero under normal circumstances (for instance,diff
). When I'm using set -e in a script where I'm expecting a nonzero return, I docommand || true
.Shawn J. Goff– Shawn J. Goff2011年03月07日 18:49:47 +00:00Commented Mar 7, 2011 at 18:49 -
2Furthermore, even if you use
set -e
, you can set an "exception handler" to catch all errors withtrap did_it_work EXIT
.Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2011年03月08日 18:41:22 +00:00Commented Mar 8, 2011 at 18:41 -
1This is part of POSIX, not a bash specific feature. Use it but be aware of some pitfalls mywiki.wooledge.org/BashFAQ/105kmkaplan– kmkaplan2014年02月05日 07:46:06 +00:00Commented Feb 5, 2014 at 7:46
-
@ShawnJ.Goff I prefer to do
command && true
. Like that the return value is not changed.kmkaplan– kmkaplan2014年02月05日 07:49:29 +00:00Commented Feb 5, 2014 at 7:49 -
@kmkaplan Your last comment doesn't make any sense to me. The purpose of
command || true
is to preventset -e
from exiting the script ifcommand
returns a non-zero exit code. It changes the exit code because we need to. The only thingcommand && true
does is runtrue
(return a zero exit code) if `command succeeded (returned a zero exit code) - it's a complete no-op.tripleee– tripleee2019年06月18日 04:32:37 +00:00Commented Jun 18, 2019 at 4:32