3

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.

Ole Tange
  • 35,514
  • 1
    You forgot fish :-) ... The problem is that variable assignment is different for sh/csh/fish, and it's not easy to make this compatible (using test or any other commands will never work, since that will reset $?)... I'm not sure what you're trying to do here, why don't you just execute your script with /bin/sh or whatever? ... The only option is to create separate scrips. – Martin Tournoij Jan 15 '15 at 23:59
  • I'm not crystal clear on what the goal is. As a general construct, you might be better with _EXIT=$?; <<other code here>>; (exit $_EXIT), to allow the "other code" to modify the state of the shell (e.g., cd, set variables, etc.) – Scott - Слава Україні Jan 16 '15 at 00:02
  • @Scott That will not work in csh. – Ole Tange Jan 16 '15 at 00:27
  • @Carpetsmoker Because the user of this code decides which shell I must use, and I do not know beforehand. – Ole Tange Jan 16 '15 at 00:28
  • @OleTange: My point was that, by including the "other code" in the parentheses, you are preventing it from having an effect on the main shell process. – Scott - Слава Україні Jan 16 '15 at 00:31
  • @Scott Ahh... but that is the goal. – Ole Tange Jan 16 '15 at 00:41
  • So, what are you trying to do? This sounds like a case of the XY problem; perhaps with a description of your goal someone can find a solution (what you want to do there is nigh-impossible). – Martin Tournoij Jan 16 '15 at 00:55
  • Why does the shell that the user's command runs in affect the shell that you write YOUR script in? – Barmar Jan 16 '15 at 19:47
  • @Barmar Because it is not a script: It is part of a script where the user decides the shell. So before _EXIT=$? there are commands written by the user in the user's shell. – Ole Tange Jan 16 '15 at 20:32
  • 1
    You can't generally use the same code in both sh-like shells and csh-like shells. Their syntaxes for variable assignment and conditionals are totally incompatible. – Barmar Jan 16 '15 at 20:36
  • @Barmar ... which is the reason this question is here. Given the syntaxes are incompatible, how do you achieve the effect. See a working solution below. – Ole Tange Jan 16 '15 at 21:41
  • Why can't you use the user's shell to execute the command they give, but bash (or whatever shell you prefer) to execute the wrapper? – Barmar Jan 16 '15 at 21:45
  • @Barmar Show me how you would do it: Post an answer. It needs to work just as well as the perl answer. – Ole Tange Jan 17 '15 at 01:08
  • I don't have an answer. I'm not sure I even understand the context. I don't know GNU Parallel. – Barmar Jan 17 '15 at 01:12
  • See also: http://unix.stackexchange.com/q/99112 – Stéphane Chazelas Jan 20 '15 at 15:35

2 Answers2

0

Use perl:

perl -e '$a=shift; `<< other code >>`; exit $a' $? # Fails in csh

Edit This works in csh, too (and zsh, tcsh, sh, pdksh, ksh93 - though not fish):

perl -e '$a=shift; `<< other code >>`; $a=~s/h// and exit $a; exit shift' "$?h" "$status"
Ole Tange
  • 35,514
0

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.