4

My question may seem related to SO question "What Linux shell should I use?", but my problem is to know which shell shall be used to write an application start script, knowing that this is a cross-platform Java application (almost all Linux distributions, MacOS, Solaris, ...). So i'm adding compatibility concerns here.

Please note that i'm not asking "which is the best shell to use" in general (which may have no sense in my opinion: subjective, depends on needs), but I'd like to know, which shell has the best chance, today, to be available (and suitable for Java application start) on most operating systems.

Also, may I simply have to use the shebang #!/bin/bash to "use bash"? (or for example #!/bin/ksh for Korn shell). What if this shell is not available on this OS?

We're actually using a ".sh" file with the shebang #!/bin/sh (which is Bourne shell I guess) but some users are complaining about errors on some Linux distributions (we don't know yet which one they use, but we would like to have a more global approach instead of fixing errors one by one). MacOS is currently using bash as the default shell but at this time we don't have any issue on MacOS using /bin/sh...

Note: we'd like to avoid having several start scripts (i.e. using different shells)

Community
  • 1
  • 1
xav
  • 5,452
  • 7
  • 48
  • 57
  • Don't use `#!/bin/sh`. POSIX allows compliant implementations to provide a non portable shell interpreter there. – jlliagre May 02 '14 at 21:56
  • @jlliagre Thank you. So if `/bin/sh` is not guaranteed to be POSIX compliant, which shebang should be used? – xav May 02 '14 at 22:06
  • How to get it for a particular platform is what I explain in my reply. – jlliagre May 02 '14 at 22:17

2 Answers2

5

For maximum portability, your best bet is /bin/sh using only POSIX sh features (no extensions). Any other shell you pick might not be installed on some system (BSDs rarely have bash, while Linux rarely has ksh).

The problem you can run into is that frequently, /bin/sh is not actually Bourne sh or a strictly POSIX sh -- it's frequently just a link for /bin/bash or /bin/ksh that runs that other shell in sh-compatibility mode. That means that while any POSIX sh script should run fine, there will also be extensions supported that will cause things that are illegal per POSIX to run as well. So you might have a script that you think is fine (runs fine when you test it), but its actually depending on some bash or ksh extension that other shells don't support.

You can try running your script with multiple shells in POSIX compatibility mode (try say, bash, ksh, and dash) and make sure it runs on all of them and you're not accidentally using some extension that only one supports.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Very good answer. If I could, I would accept both answers (@Chris Dodd & @jlliagre). For future readers: I accepted jlliagre's answer because I think that testing the script with `dash` is sufficient (no need to test in POSIX mode with other shells IMO), then dynamically updating the shebang may be needed on some derived UNIXes. – xav May 03 '14 at 15:32
3

You won't find a shell implementation that will be installed on every of these OSes, however, all of them are either POSIX compliant or more or less close to being compliant.

You should then restrict your shell scripts to stick to the POSIX standard as far as possible.

However, there is no simple way to tell a script is to be executed in a POSIX context, and in particular to specify what shebang to set. I would suggest to use a postinstaller script that would insert the correct shebang on the target platform retrieved using this command:

#!/bin/sh
printf "#!%s\n" `PATH=\`getconf PATH\` command -v sh`

You scripts should also include this instruction once and before calling any external command:

export PATH=$(getconf PATH):$PATH

to make sure the utilities called are the POSIX ones. Moreover, beware that some Unix implementations might require an environment variable to be set for them to behave a POSIX way (eg BIN_SH=xpg4 is required on Tru64/OSF1, XPG_SUS_ENV=ON on AIX, ...).

To develop your script, I would recommend to use a shell that has the less extensions to the standard, like dash. That would help to quickly detect errors caused by bashisms (or kshisms or whatever).

PS: beware that despite popular belief, /bin/sh is not guaranteed to be POSIX compliant even on a POSIX compliant OS.

jlliagre
  • 29,783
  • 6
  • 61
  • 72
  • Thank you. So if `/bin/sh` is not guaranteed to be POSIX compliant, which shebang should I use? – xav May 02 '14 at 21:59
  • As I wrote, you cannot know in advance what the shebang should be. The best way would be to use a script that would set/update it after you install your scripts on the target platform. – jlliagre May 02 '14 at 22:19
  • OK, thanks! (indeed you explained it in your answer, my fault!) – xav May 02 '14 at 22:23
  • It is unfortunate you accepted an answer that miss that point and in particular suggest something that would fail on Solaris 10 and older. – jlliagre May 02 '14 at 22:36
  • In fact I'm waiting the end of current tests on your solution (`getconf PATH`). If your answer helps us more than the other answer, I will of course unaccept the first one, to accept yours. I initially accept the other question because modifying the shebang dynamically after the installation seems a bit complex or overkill to me, but I may be wrong, we are testing this on several OSes. Thanks! – xav May 02 '14 at 22:46
  • Your advice of testing with `dash` was great and helped us. Yet, I still don't understand how your `printf` command works: on our CentOS box, `getconf PATH` returns `/bin:/usr/bin`, so what does your `PATH=` command do? OK, the whole `printf` command writes `/bin/sh` on our box as expected, but could you please explain why? Am i supposed to replace `command` with a specific command? – xav May 03 '14 at 10:07
  • Have a look to the updated command in my reply. It prints what should be used as shebang on the platform you run it. That would be `#!/usr/xpg4/bin/sh` on Solaris and `#!/usr/posix/bin/sh` on some System V derived Unixes. – jlliagre May 03 '14 at 10:14
  • I understand *what* it does but what I was asking is *how*? I don't understand why `PATH=/bin:/usr/bin command -v sh` prints `/bin/sh` and why `PATH=` is used here (not a Linux expert!). Although, your reply has only been updated 10 hours ago, am I supposed to find this answer in this reply? Thanks. – xav May 03 '14 at 10:22
  • `PATH=$(getconf PATH)` set the PATH to be POSIX compliant and `command -v` displays the full path to the command passed as an argument. You get `/bin/sh` on most Unix and Linux distributions but a different output with some other ones. I don't get why you are asking how these commands do what they do. They just do it by design. – jlliagre May 03 '14 at 13:44
  • OK thank you, I understand. I didn't think `command` was a real command, I thought I would have to replace _"command"_ with a real command... Answer accepted (see comment on the other answer) – xav May 03 '14 at 15:32