Bash – Compatibility scripting: Save $? for use later

bashcshtcshzsh

I would like to write a small part of a script that saves the error status, executes some other code, and sets the error status to the original error status. In Bash it looks like this:

<< some command >>; _EXIT=$?;( <<other code here>> ;exit $_EXIT)

But I need code that will run no matter if it is being run under bash, zsh, csh or tcsh. I do not know which shell will be used in advance, because it is decided by the user. The user also decides << some command >>.

It is safe to assume that << other code >> will work in all shells, but it is not safe to assume that you can write a file (so putting it into a file will not work).

Background

GNU Parallel executes commands given by the user in the shell decided by the user. When the command is finished, GNU Parallel has some cleanup to do. At the same time GNU Parallel needs to remember the exit value of the command given by the user. The code run before the snippet above is the user given command. << other code >> is the cleanup code.

Best Answer

This answer works in bash-like shells and csh-like shells, and it doesn’t require perl:

sh -c '<< other code >>; [ "$0" != "" ] && exit "$0"; exit "$1"' "$?" "$status"

or you can add a dummy/pad parameter:

sh -c '<< other code >>; [ "$1" != "" ] && exit "$1"; exit "$2"' foo "$?" "$status"

and the test can be changed to [ ! -z "$0" ] (or "$1").  I don’t know whether it will work in zsh or fish.  Every shell that I’m familiar with will evaluate $? and $status when the command is parsed, and pass them to the sh -c command as parameters.  The sh shell assigns them to the last two positional parameters.  When the “other code” finishes, the shell will check the positional parameter that got its value from $? to see whether it is null or non-null, and exit with the appropriate positional parameter value.

This will fail if the ambient shell uses something other than $? or $status to hold the exit status.  It will also fail if the shell allows $? to be set (non-null) if it is not the exit status, or if $? is an invalid character sequence that causes the entire command to be rejected as a syntax error.

Related Question