Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

share|improve this question
add comment

3 Answers

up vote 9 down vote accepted

One common way is:

die() {
    echo "$*" 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() {
    echo "FATAL ERROR: $* (status $?)" 1>&2
    exit 1
}

and then using it is a bit easier:

mkdir -p some/path || die "mkdir failed"

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"
run_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"
run_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.

dir=some/path
mkdir -p "$dir"            || die "Cannot make $dir"
cd "$dir"                  || die "Cannot cd to $dir"
run_some_command           || die "Cannot run some_command"

And if you plan to fix the problem and re-run the script, maybe you want the script to work if the directory already exists, so you don't have to remove it first, in which case, you would want

dir=some/path
if [ ! -d "$dir" ]; then
    mkdir -p "$dir"        || die "Cannot make $dir"
fi
cd "$dir"                  || die "Cannot cd to $dir"
run_some_command           || die "Cannot run some_command"
share|improve this answer
    
+1 for the || idea –  Johan Mar 8 '11 at 7:23
add comment

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
}
share|improve this answer
    
You can also use the code in this format. <pre>function try { –  BillThor Mar 7 '11 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 Mar 7 '11 at 17:50
    
Why give a version that doesn't work with spaces? Change $* to "$@". –  Mikel Mar 7 '11 at 19:49
    
I've fixed it for you. –  Mikel Mar 7 '11 at 20:05
    
Thanks, and for the -ne tweak, too. –  Warren Young Mar 7 '11 at 20:13
add comment

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.

share|improve this answer
    
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 do command || true. –  Shawn J. Goff Mar 7 '11 at 18:49
1  
Furthermore, even if you use set -e, you can set an “exception handler” to catch all errors with trap did_it_work EXIT. –  Gilles Mar 8 '11 at 18:41
    
This is part of POSIX, not a bash specific feature. Use it but be aware of some pitfalls mywiki.wooledge.org/BashFAQ/105 –  kmkaplan Feb 5 at 7:46
    
@ShawnJ.Goff I prefer to do command && true. Like that the return value is not changed. –  kmkaplan Feb 5 at 7:49
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.