I am building a script that should work in csh, bash, and fish with no change:

This does the right thing in all the shells,

perl -e '$bash=shift;$csh=shift;for(@ARGV){unlink;rmdir;}if($bash=~s/h//){exit$bash;}exit$csh;' "$?h" "$status" $PARALLEL_TMP

except that fish complains:

fish: $? is not the exit status. In fish, please use $status.

Is there a compatible way I can tell fish: Please do not warn, I know what I am doing.

  • Not knowing all of the details here, would it be at all possible to try to detect the shell before running and use an appropriate script accordingly?
    – ilkkachu
    Jun 23, 2020 at 8:01
  • @ilkkachu The goal is to avoid having to do that. As you can see I have found a way that works in both csh and bash.
    – Ole Tange
    Jun 23, 2020 at 9:29

2 Answers 2

$ bash -c 'false; echo "[$status]" "[`echo \$?h`]"'
[] [1h]
$ csh -c 'false; echo "[$status]" "[`echo \$?h`]"'
[1] [0]
$ fish -c 'false; echo "[$status]" "[`echo \$?h`]"'
[1] [`echo $?h`]

Uses the fact that ` is not special in fish, and that Bourne-like shells do an extra level of backslash processing within `...`.

You should also be able to use eval, supported by all three shells, and have different code ready for all three in some environment variable, which would simplify things.

if ($csh || $fish) {
   ENV{CHECK_STATUS} = q{perl -e '...' $status};
} else {
   ENV{CHECK_STATUS} = q{perl -e '...' "$?"};
exec $shell, "-c", ...;

and the shell code would be eval "$CHECK_STATUS" (beware that for csh, $CHECK_STATUS must not contain newline characters).


Is there a compatible way I can tell fish: Please do not warn, I know what I am doing.

There is not. Cross-compatible scripts between fish and other shells are not a thing, because fish is not compatible.

In most cases what you want to do is to pick a shell and write a script for it, with a shebang and everything, the most widely available choice being "/bin/sh".

Then fish can just run your script like it can run a script written in python or ruby, or programs written in C.

In some cases you do need the shell to source your script because you want it to effect environment changes in your shell. In that case the simplest way to allow that would be to have the script print

export var=val

lines and have fish source it via /path/to/script | source (and other shells similarly).

  • 1
    For a bit of context, Ole maintains GNU parallel. GNU parallel (written in perl) runs commands through the user's shell so the user can use constructs of their shell in the command they parallelize. I suppose that means parallel needs to sometimes run polyglot code valid in the syntax of a few different shells. You can see he already has a solution for csh vs sh (two incompatible languages) where $? would be a syntax error in csh but $?h is not. Jun 23, 2020 at 6:37
  • Executing non-user supplied code in $SHELL is the wrong thing to do 99 times out of 100. That might be the one case where it's okay. Without that context, my answer stands. With it, I'd still check the value of $SHELL and just pick the code to add instead of hacking it to be polyglot.
    – faho
    Jun 23, 2020 at 7:15
  • Just guessing here, but note that parallel also runs code remotely over ssh when parallelizing to several machines, and ssh interprets commands using the login shell of the remote user, so Ole may not have the option to switch shell there. In any case, I agree it's a bad idea to use a shell to run commands, and I've argued that with Ole in the past. I even wrote How to execute an arbitrary simple command over ssh without knowing the login shell of the remote user? (polyglot code) as a way to show it was possible to abstract away the shell when running ov Jun 23, 2020 at 7:29
  • (continued) er ssh after such a discussion. Jun 23, 2020 at 10:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .