rahuL
rahuL

Reputation: 3420

capture compile error and terminate bash script

I've written a bash script which install 3 packages from source. The script is fairly simple with the ./configure, make, make install statements written thrice (after cding into the source folder). To make it look a little cleaner, I have redirected the output to another file like so:./configure >> /usr/local/the_packages/install.log 2>&1.

The question is, if any one package fails to compile due to some reason (I'm not even sure of what reason, because it has always run successfully till now - this is just something I want to add), I'd like to terminated the script and rollback.

I figure rolling back would simply be deleting the destination folders specified in prefix=/install/path but how do I terminate the script itself?

Upvotes: 0

Views: 2765

Answers (3)

rkyser
rkyser

Reputation: 3357

Option 1

You can check the return code of something run from a script with the $? bash variable.

moo@cow:~$ false
moo@cow:~$ echo $?
1
moo@cow:~$ true
moo@cow:~$ echo $?
0

Option 2

You can also check the return code by directly putting the command into a if statement like so.

moo@cow:~$ if echo a < bad_command; then echo "success"; else echo "fail"; fi
fail

Invert the return code

The return code of a command can be inverted with the ! character.

moo@cow:~$ if ! echo a < bad_command; then echo "success"; else echo "fail"; fi
success

Example Script

Just for fun, I decided to write this script based on your question.

#!/bin/bash

_installed=()

do_rollback() {
    echo "rolling back..."
    for i in "${_installed[@]}"; do
        echo "removing $i"
        rm -rf "$i"
    done
}

install_pkg() {
    local _src_dir="$1"
    local _install_dir="$2"
    local _prev_dir="$PWD"
    local _res=0

    # Switch to source directory
    cd "$_src_dir"

    # Try configuring
    if ! ./configure --prefix "$_install_dir"; then
        echo "error: could not configure pkg in $_src_dir"
        do_rollback
        exit 1
    fi

    # Try making
    if ! make; then
        echo "error: could not make pkg in $_src_dir"
        do_rollback
        exit 1
    fi

    # Try installing
    if ! make install; then
        echo "error: could not install pkg from $_src_dir"
        do_rollback
        exit 1
    fi

    # Update installed array
    echo "installed pkg from $_src_dir"
    _installed=("${_installed[@]}" "$_install_dir")

    # Restore previous directory
    cd "$_prev_dir"
}

install_pkg /my/source/directory1 /opt/install/dir1
install_pkg /my/source/directory2 /opt/install/dir2
install_pkg /my/source/directory3 /opt/install/dir3

Upvotes: 2

James Polley
James Polley

Reputation: 8181

In two parts:

To make the script abort as soon as any command returns an error, you want to use set -e. From the man page (BUILTINS section; description of the set builtin):

-e

Exit immediately if a pipeline (which may consist of a single simple command),
a subshell command enclosed in parentheses, or one of the commands executed as
part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with
a non-zero status. The shell does not exit if the command that fails is part of
the command list immediately following a while or until keyword, part of the
test following the if or elif reserved words, part of any command executed in a
&& or ││ list except the command following the final && or ││, any command in a
pipeline but the last, or if the command's return value is being inverted with
!. A trap on ERR, if set, is executed before the shell exits. This option
applies to the shell environment and each subshell environment separately (see
COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before
executing all the commands in the subshell.

You can set this in three ways: Chang your shebang line to #!/bin/bash -e; call the script as bash -e scriptname; or simply use set -e near the top of your script.

The second part of the question is (to paraphrase) how to catch the exit and clean up before exiting. The answer is referenced above - You want to set a trap on ERR.

To show you how these work together, here's a simple script being run. Note that as soon as we have a non-zero exit code, execution transfers to the signal handler which takes care of doing the cleanup:

james@bodacious:tmp$cat test.sh
#!/bin/bash -e

cleanup() {
  echo I\'m cleaning up!
}

trap cleanup ERR

echo Hello
false
echo World
james@bodacious:tmp$./test.sh
Hello
I'm cleaning up!
james@bodacious:tmp$

Upvotes: 1

user000001
user000001

Reputation: 33317

Perhaps something like this could work:

./configure && make && make install || rm -rf /install/path

Upvotes: 3

Related Questions