14

Which one is better:

  • #!/usr/bin/env sh
  • #!/bin/sh
  • empty/no header

I used to think the 1st one is the best, anyway i've found on some Linux-based systems (like Android) that pathname is missing, so now i'm thinking the only way to have "portable" shell scripts is to not include any header...

eadmaster
  • 1,643
  • It really is about how portable you want to be. Only Unixes ? All Linux-kernel based boxes ? All systems even old Win 3.0 and VMS (kidding) ? /bin/sh is usually a good minimum, just be aware that sh is not bash... expect on most GNU/Linux systems. – Ouki Mar 25 '14 at 19:42
  • Although Android uses the linux kernel and the default shell is derived from bash via ash, the userland is not very unix-like and missing many standard tools. – goldilocks Mar 25 '14 at 21:58

2 Answers2

10

For portability, you can safely assume that #!/bin/sh will find a mostly POSIX-compliant shell on any standard Unix or Linux system, but that's really about it.

In FreeBSD, OpenBSD and NetBSD (along with DragonFly, PC-BSD and some other derivatives), bash is located at /usr/local/bin/bash (if it is installed), so the /usr/bin/env approach provides portability between Linux and BSD.

Android is not a standard Unix or Linux system. On my not-rooted Android phone, none of /usr/bin/env, /bin/bash or even /bin/sh exist, and the system shell is /system/bin/sh.

A shell script that is missing the #! (shebang) will attempt to run in the shell that called it on some systems, or may use a different default interpreter (/bin/bash for example), on other systems. And while this may work in Android, it isn't guaranteed to work in other operating systems, where users may elect to use an interactive shell that is not bash. (I use tcsh in FreeBSD, where it is the default shell, and shebang-less script are interpreted by the calling shell.)

So from where I sit, it looks like it is not possible to create a shell script that is portable between Android and non-Android (Linux or Unix) systems, because Android does things differently.

ghoti
  • 6,602
  • In my experience /bin/sh is supposed to point to something more like bsh or dash than bash, which is relatively bloated and thus preferred for interactive use. – Wutaz Mar 25 '14 at 20:37
  • 2
    A few mistakes in here. There are still many (mostly commercial) Unices around here (most of them Solaris 10 and before) where /bin/sh is the Bourne shell, not a POSIX shell, POSIX doesn't specify the path of sh. Most shells (and execp/env/find -exec...) will interpret a she-bang-less script with the system's sh, few interpret it with themselves and when they do, they do it in POSIX compatibility mode. That's the standard/POSIX way to run script, but that assumes the caller is in a POSIX environment. – Stéphane Chazelas Mar 25 '14 at 20:38
  • Regarding *BSD bash is optional. It means there are good chances that even /usr/local/bin/bash does not exists on the systems (not to mention proprietary systems like Solaris, AIX or HP-Ux). – Ouki Mar 25 '14 at 21:38
  • @StephaneChazelas - re POSIX, I agree of course, and that's why the words I used were "mostly POSIX-compliant". But this question was about portability, rather than where to find POSIX, and one can't rely on a shell always being found at one location. Regarding the default interpreter, a shebang-less script run in tcsh gets interpreted by tcsh on FreeBSD. Obviously, there's inconsistency there too, so I've updated the answer accordingly. – ghoti Mar 26 '14 at 13:36
  • @Ouki - yes of course, I just didn't think that was relevant to the question. But I've added that clarification to the answer for clarity. – ghoti Mar 26 '14 at 13:40
  • @ghoti: didn't ask for more ;) thanks – Ouki Mar 26 '14 at 13:42
  • The Bourne shell is not remotely POSIX compliant (POSIX sh is based on a subset of ksh88, with many extensions over the Bourne shell), but POSIX shells are mostly backward compatible with the Bourne shell. So, you can say that /bin/sh is likely to be mostly Bourne compatible, but not POSIX compliant unless you want to exclude all the Unices that still have the Bourne shell as their /bin/sh (though those are becoming rarer and rarer now that Solaris has moved to ksh for /bin/sh (at last)) – Stéphane Chazelas Mar 26 '14 at 13:47
  • csh/tcsh will run the script with csh if the script starts with # and with sh otherwise as that's the historical behaviour before #! was invented and before the Bourne shell supported # as a comment leader. So in addition to leave off the she-bang, you may want to make sure the first character is not #. – Stéphane Chazelas Mar 26 '14 at 14:02
  • That may be the case where you are testing this, but in the OS I'm using, the behaviour is not as you describe. Please consider that what you describe as "historical behaviour" may not be universal. And we don't need to get into a debate about the "one true tcsh". – ghoti Mar 26 '14 at 14:48
3

In my experience, #!/bin/sh and #!/bin/bash have always ended up finding the right environment on the few systems I have worked on. I am yet to encounter an exception. I also find it being used routinely in shell scripting related texts which I suppose are written keeping portability in mind because of diverse audience.

Can't say the same with #!/usr/bin/env. Some systems have it installed as #!/bin/env and have broken my python scripts in the past. So, I'll go with the second bullet.

Here is some supporting for my above statement:

On CentOS release 5.7 I get the following:

$ which env
/bin/env

On Ubuntu 12.04 Precise Pangolin:

$ which env
/usr/bin/env

Additionally, at least in one older system, I remember the admins installed coreutils on /opt for some reason (may be not a best practice). Since env is part of coreutils, users ended up getting it at /opt/coreutils/bin/env. Admittedly, I have not used all the systems out there so the answer is based on my limited experience.

Ketan
  • 9,226
  • 5
    For instance /bin/bash is definitely not FreeBSD (would be /usr/local/bin/bash as bash is not part of the default shells). – Ouki Mar 25 '14 at 19:37
  • 1
    It's rather the opposite: only some very rare systems lack /usr/bin/env (SCO which is barely extant, NextStep which is all but extinct — plus of course most common non-unix systems such as Android or Windows). On the other hand, /bin/bash pretty much only exists on non-embedded Linux. /bin/sh is a safe bet on any unix, but a few older systems have a non-POSIX Bourne shell there. – Gilles 'SO- stop being evil' Mar 25 '14 at 23:26
  • @Gilles Thanks for the info. I updated my answer providing some evidence of what I have seen on some systems I have come across. – Ketan Mar 26 '14 at 02:05
  • 3
    which env isn't relevant: there are quite a few systems where /usr/bin is a symlink to /bin or vice versa, which makes both /usr/bin/env and /bin/env usable. What matters is that /usr/bin/env is present, which is the case on all Linux distributions I've ever seen or heard of (and removing it would break so many things that nobody would do it). – Gilles 'SO- stop being evil' Mar 26 '14 at 09:53