Save exit code for later

24,194

Solution 1

You can wrap the exit and rm commands up into a single simple-command with eval like:

java ... && java ...
eval "rm -f *.class; exit $?"

That way $?'s value when passed to exit is whatever it gets assigned immediately before eval runs.

Solution 2

I'd go with:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
exit "$test_exit_code"

Why jump around when exit is available?


You could use a trap:

trap 'last_error_code=$?' ERR

For example:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0

Solution 3

As far as I know the closest thing bash has to a try...finally block from a more C-like programming language (which is what you probably would want if it were available) is the trap construction, which works like this:

trap "rm -f *.class" EXIT
javac *.java && java -ea Test

This will execute "rm -f *.class" when your script exits. If you have something more complex to do, you could put it in a function:

cleanup() {
    ...
}
trap cleanup EXIT
javac *.java && java -ea Test

If you are so inclined, you can turn this into a fairly general idiom that works roughly like a try...catch...finally block in C. Something like this:

(
  trap "catch_block; exit" ERR
  trap finally_block EXIT
  # contents of try goes here
)

Note that the parentheses delimit a subshell; with this construction, only the subshell exits if a command fails, not the whole script. Remember that subshells are somewhat computationally expensive so don't use too many (hundreds) of them. Depending on your script you may be able to achieve the same effect more efficiently with shell functions and trap ... RETURN, but that's up to you to investigate.

Share:
24,194

Related videos on Youtube

math4tots
Author by

math4tots

Updated on September 18, 2022

Comments

  • math4tots
    math4tots almost 2 years

    So I have a little script for running some tests.

    javac *.java && java -ea Test
    rm -f *.class
    

    Now the problem with this is that when I run the script ./test, it will return a success exit code even if the test fails because rm -f *.class succeeds.

    The only way I could think of getting it to do what I want feels ugly to me:

    javac *.java && java -ea Test
    test_exit_code=$?
    rm -f *.class
    if [ "$test_exit_code" != 0 ] ; then false; fi
    

    But this seems like something of a common problem -- perform a task, clean up, then return the exit code of the original task.

    What is the most idiomatic way of doing this (in bash or just shells in general)?

  • muru
    muru over 9 years
    @math4tots Try the update.
  • math4tots
    math4tots over 9 years
    So with your update I would have to initialize last_error_code to zero and then return at the end so I would have non-zero exit code if any command threw an error? It is a cool trick, but for my two line hack script, I think I prefer @mikeserv 's answer.
  • mikeserv
    mikeserv over 9 years
    eval is always a fan-favorite.
  • muru
    muru over 9 years
    @math4tots You can always do exit ${last_error_code:=0}.
  • muru
    muru almost 7 years
    @avip whatever for? It's already in single quotes, so the variable is only evaluated when the trap is called.
  • avip
    avip almost 7 years
    @muru u r right, my code had double-quoted trap. Thanks.
  • uz7
    uz7 about 6 years
    If the code is a function body, you most likely want return instead of exit.