Is there any reason to have a shebang pointing at /bin/sh rather than /bin/bash?

6,847

Solution 1

  1. There are systems not shipping bash by default (e.g. FreeBSD).
  2. Even if bash is installed, it might not be located in /bin.
  3. Most simple scripts don't require bash.
  4. Using the POSIX shell is more portable and the scripts will run on a greater variety of systems.

Solution 2

Most system scripts in (Debian related: Ubuntu, Mint, etc.) Linux are written to run in the faster dash, which is the default /bin/sh in those systems. The reason is twofold:

  • Speed of the system. A smaller code of dash loads faster and also run faster. With some (small?) additional effort (cost) to programmers of shell scripts.

  • Security. Having diversity of shells helps resilience to bugs. Debian systems were mostly not vulnerable to shellshock because the default shell did not have such vulnerability.

Bash is indeed the default shell for users, as is more powerful and has much more elements to make coding easier. It is also the default sh in Mac OS (the one linked from /bin/sh). However, calling bash with the link name of sh makes it start as a posix compliant shell.

Solution 3

Others have pointed out that the premises of the question, that the Bourne Again shell is default and ubiquitous, are downright wrong.

In addition to that, there are indeed good reasons for using something other than the Bourne Again shell for interpreting shell scripts. These reasons motivated Ubuntu's and Debian's big project, over a number of years, to remove bashisms and to make as many of the shell scripts run by system initialization (which was a lot of shell scripts with System 5 rc) and package installation/removal use the Debian Almquist shell instead of the Bourne Again shell.

Simply put: The Bourne Again shell, chock full of interactive features as it is, is not the fastest shell interpreter for a POSIX-conformant shell script. So if one can make one's shell scripts POSIX-conformant, interpreting them with a more lightweight program, like the Debian Almquist shell, one's system will perform better. (In the end, Debian had to make slight adjustments to the Almquist shell, to add support for a couple of non-POSIX shell constructs that were simply too deeply and widely embedded and too useful to get rid of.)

The result of it all was a major gain in bootstrap performance.

So there are two distinct classes of shell to consider, here:

  • The shells with all of the flashy interactive features, which are configured as the interactive login shells for users in the accounts database.
  • The shells that interpret lots of scripts quickly, which are used as the script interpreters by shell script programs.

Note that talking about this as "preferring /bin/sh" is oversimplifying. Debian actually had at least two goals:

  1. In the face of administrators using the Debian Almquist shell, the Z Shell (in POSIX mode), the Bourne Again shell (in POSIX mode), the MirBSD Korn shell, and others as /bin/sh, there was either …

    1. … making the scripts as portable as possible, so that switching what /bin/sh mapped to didn't break things; or

    2. … making non-portable scripts explicitly target the correct interpreter program, instead of simply expecting that /bin/sh map to it.

  2. There was making the Debian Almquist shell the default mapping for /bin/sh instead of the Bourne Shell, so that those scripts that were POSIX-conformant (or, more properly, Debian Policy Manual conformant) ran more quickly.

And of course once one gets into this, one can take it a lot further; such as considering the efficiency tradeoffs of the likes of /bin/true and /usr/bin/clear being shell scripts or compiled programs. But that's beyond the scope of this answer, fortunately. ☺

None of this is very new, nor even Unix-specific, of course. Back before the turn of the century, I wrote and published a command-line interpreter that came in both "interactive" and "non-interactive" flavours, explaining this very division in its doco and noting the difference between the COMSPEC and OS2_SHELL environment variables. Similarly, discussion of removing bashisms in Debian's System V rc and package installation/removal scripts dates back to the 1990s.

Further reading

Solution 4

Is there any reason to have a shebang pointing at /bin/sh rather than /bin/bash?

Yes. @Marco's excellent answer outlines this well.

What should I use in my shebang?

When writing your own scripts, you should point the shebang to the most general thing that you have tested against.

On my system (Centos 6.6), sh is symlinked to bash:

$ ls -l /bin/sh 
lrwxrwxrwx 1 root root 4 Dec  2  2014 /bin/sh -> bash

This means that I always use #!/bin/bash in my shebang unless I have verified that I don't have bashims in my script.

By setting the shebang to #!/bin/sh you are promising that the script will work with all implementations of sh.

This is a much bigger promise than saying the script will work with bash.

Here is an example of a script that will behave incorrectly depending on what sh implementation the system is using:

#!/bin/sh
n=1
a=$((++n))
echo $n

When using bash the script will print:

2

When using dash the script will print:

1

If I want to use #!/bin/sh what do I need to do?

  • Check the script with checkbashisms - Note that this will not find all bashims. It didn't find the bashism in my script above
  • Test the script using another sh implementation. I typically test with dash, however I expect that some bashisms or dashims can still slip through.

The DashAsBinSh page on the Ubuntu wiki has lots of interesting info.

Solution 5

The only remaining reason to write a shell script, instead of a script in a more powerful and ergonomic language, is if portability to legacy systems with an unknown set of installed software is more important than any other factor.

/bin/sh is the one and only script interpreter that's available on everything that calls itself Unix. But on an awful lot of legacy systems, /bin/sh and the associated utilities are not even compliant with the POSIX.1-1996 "shell and utilities" spec, let alone anything more modern. Standard-compliant tools are an optional add-on, installed in /usr/xpg4 or some such non-obvious location.1 Scripting to the portable-subset shell language is even more tedious and error-prone than scripting to the POSIX shell language. (Read through an Autoconf-generated configure script sometime if you don't believe me. Just the setup should be enough to convince you.)

But if you can assume any other script interpreter is installed (e.g. Bash) then you can assume an interpreter for a better scripting language is installed. Perl, for instance, is more likely to be available than Bash is.

Therefore, you should never write a #! /bin/bash script, because if that's an option, a better language is also an option.

1 For instance, Solaris 10 and older shipped the original Bourne shell as /bin/sh. I am informed that Solaris 11 updated it to ksh93.

Share:
6,847

Related videos on Youtube

Jules
Author by

Jules

Expert Python programmer with experience working with the Linux network stack, REST APIs, and relational databases (and Postgres in particular). There's some devops experience in there too, but software dev is my preference. Not currently open to new work.

Updated on September 18, 2022

Comments

  • Jules
    Jules over 1 year

    In most shell scripts I've seen (besides ones I haven't written myself), I noticed that the shebang is set to #!/bin/sh. This doesn't really surprise me on older scripts, but it's there on fairly new scripts, too.

    Is there any reason for preferring /bin/sh over /bin/bash, since bash is pretty much ubiquitous, and often default, on many Linux and BSD machines going back well over a decade?

    • Mathieu
      Mathieu over 8 years
      It's a good partice to use /bin/sh if you do not use specific bash functions. One day you could have to use one of your script on a system on which it is not installed (remote server, embedded computer...)
    • mikeserv
      mikeserv over 8 years
      in general you should avoid asking questions which lend themselves to answers based on opinion (which is the only possible way i believe this question can be answered - but that's just my opinion).
    • Jules
      Jules over 8 years
      @mikeserv I intended for this question to be more about best practices than opinion - and the answers so far are fairly subjective, at least IMO (no meta-humour intended!). I'm open to hearing more about whether or not, and why, this would qualify as opinion, though.
    • JdeBP
      JdeBP over 8 years
      I believe that it is possible to answer this question without involving mere opinion, by pointing to at least two organizations that addressed this very thing over a number of years, and looking at the history and outcome. With plenty of further reading attached, to boot. ☺
    • sixtyfootersdude
      sixtyfootersdude over 8 years
      @purplepsycho - This is only true if you test and verify that your script doesn't use specific bash functions. See my answer for more info.
    • Jules
      Jules over 8 years
      @user2338816 my bad, I meant objective. Thanks for your points!
    • mikeserv
      mikeserv over 8 years
      @JdeBP - i don't think so. the only way you could is to first assume the opinion of the asker - which means to take as a given that there might be some reason to do #!/bin/bash.
    • schily
      schily over 8 years
      #!/bin/sh should not be used if a script contains bashisms but it is also good practice to try to write scripts in a way that would allow execution with the SVr4 Bourne Shell. Compatibility with the SVr4 Bourne Shell can be checked using the osh binary from the Schily Bourne Shell schilytools.sourceforge.net/bosh.html
    • Thorbjørn Ravn Andersen
      Thorbjørn Ravn Andersen over 8 years
      "pretty much" is not the same as "always". /bin/sh is always there. Guaranteed.
    • Wildcard
      Wildcard over 8 years
      I think there are many excellent objective answers which have been given...but I wouldn't mind seeing this question "protected."
  • derobert
    derobert over 8 years
    Another reason: often /bin/sh is faster than bash, or at least loads quicker (due to being smaller).
  • Stéphane Chazelas
    Stéphane Chazelas over 8 years
    @derobert, /bin/sh is faster if it's not bash, there still are a few systems like OS/X or RHEL where /bin/sh is bash.
  • VarunAgw
    VarunAgw over 8 years
    Isn't /bin/sh just a symlink to the default shell?
  • Sobrique
    Sobrique over 8 years
    No. Some OS link it, because bash is backwards compatible.
  • Admin
    Admin over 8 years
    A more limited set of available tools of a simpler shell makes writing code for it more difficult (please avoid any fanatic war on this). That is why some users like zsh, which has even more tools available than bash. I like Bash more as is a balance between both extremes.
  • Admin
    Admin over 8 years
    In which language will you write an script to boot a system? Or inside a SOHO router (limited embebed systems)? Or in Android? A shell will be available in all those cases. Perl not.
  • schily
    schily over 8 years
    @Stéphane Chazelas The fastest shell for scripting is ksh93, followed by the Schily Bourne Shell and directly followed by dash. dash is however only fast because there is no multi-byte character support in dash.
  • jlliagre
    jlliagre over 8 years
    There are some inaccuracies about the Solaris related parts of your answer. On Solaris 10 and older, /bin/sh is not POSIX.1-1996 but actually a pre-POSIX syntax original Bourne shell. That makes #!/bin/sh a very poor shebang for scripts to run with these releases. Portable scripts on Solaris would use #!/usr/xpg4/bin/sh which won't be very portable on other OSes. On the latest Solaris, Oracle Solaris 11 first released 2011, /bin/sh is a modern ksh93 that complies with recent POSIX specs, and has many modern extensions.
  • DaleHarris541
    DaleHarris541 over 8 years
    @BinaryZebra: "A shell" will be available, yes. But bash is not necessarily that shell (in particular most SOHO routers use ash which is provided with busybox), and I tend to think zwol is right that perl is more commonly available than bash.
  • Admin
    Admin over 8 years
    @BenVoigt I am commenting on this sentence "The only remaining reason to write a shell script". There are still some instances that need a shell for some tasks (not all). Those were some instances from the top of my head. OTOH In systems with enough memory (almost all in today standards) and enough power installing Perl (or Python, or ....) is quite quick and easy. However, I am not advocating for any specific shell. Where do you read that in my comment?
  • Stéphane Chazelas
    Stéphane Chazelas over 8 years
    @schily, that depends a lot on the script. For instance ksh93 startup time is quite bad on Linux (on par with bash, not as bad as zsh), so not a very good choice for /bin/sh that is used for system(), popen() or other one-line simple commands and short and simple scripts. pdksh and derivatives can also beat dash or ksh93 on some things.
  • schily
    schily over 8 years
    How do you measure start up time and what values do you get?
  • zwol
    zwol over 8 years
    @jlliagre You're actually proving my point - a portable shell script can't use #! /usr/xpg4/bin/sh, it has to use #! /bin/sh, so prior to Solaris 11, it would get the pre-standardization shell. Thanks for the information about exactly when this changed; I'll update the answer to be more precise
  • schily
    schily over 8 years
    @jlliagre ksh93 is not POSIX compliant. As a result, /usr/xpg4/bin/sh is a ksh88 on Solaris.
  • Charles Duffy
    Charles Duffy over 8 years
    @RobertL, many of the facilities added in ksh and adopted by bash exist because they substantially ease the process of writing robust and correct scripts. Without the various additions allowing indirect assignment and evaluation to be performed safely, for instance, one can find oneself using eval, with attendant security exposure; similarly, without built-in regex support, one can need to use external commands (at a heavy performance penalty) for matching even within a single line, etc.
  • jlliagre
    jlliagre over 8 years
    @zwol, I would suggest avoiding /bin/sh. As the fisrt paragraph of the shell standards states As it stands, a strictly conforming application must not use "#!" as the first two characters of the file. In any case, that would be much better than using #!/bin/sh on Solaris. Also, a POSIX compliant script must be run from a POSIX compliant shell. The fact is if you want your scripts to be portable, you need to either adapt them at installation depending on the platform they are installed to, or to use other tricks to have them executed by the POSIX compliant shell.
  • zwol
    zwol over 8 years
    @jlliagre I don't understand. It sounds like you are saying that there is no portable way to run a script of any kind. But then how are you to do anything at all? (In practice, #! /bin/sh is an adequate starting point, although you do indeed have to jump through a great many hoops to bootstrap yourself from that point. Again, see the first couple thousand lines of any Autoconf-generated configure script.)
  • Tommy Dubé-Leblanc
    Tommy Dubé-Leblanc over 8 years
    Quite a few scripts assume that /bin/sh is /bin/bash. Ubuntu switching from bash to dash broke all of them.
  • jlliagre
    jlliagre over 8 years
    If you call your scripts from a POSIX shell, no shebang at all is more portable than whatever shebang. Just make the file executable and that's it. The shebang is usefull if you want the script to be interpreted by bash, ksh, zsh or any specific shell but if your script doesn't use any specific constructions or syntax, a #!/bin/sh shebang might hurt more than fix things, at least on Solaris 10 and older.
  • schily
    schily over 8 years
    Autoconf AFAIR depends on a Bourne Shell from at least 1983. A POSIX environment is more complex that just running a POSIX shell. To get a POSIX environment, you call: PATH= the result from command -p getconf PATH. Then type sh.
  • jlliagre
    jlliagre over 8 years
    @schily That's not enough, you'll also need to make sure PATH is exported, just in case. On some OSes, setting some environment variable is also a prerequisite for the POSIX environment to be enabled. One more issue is there is no fully portable way to set and export a variable regardless of the shell (e.g. csh), although anything named /bin/sh should support the original bourne way.
  • Soge
    Soge over 8 years
    @atamanroman: The scripts were incorrect in the first place, they should have used #!/bin/bash if they wanted Bash (nothing wrong with that!). The change was mostly done upstream in Debian. It improved user experience, it had a measurable impact on system startup time. It also positively impacted security, see "Shellshock".
  • Wildcard
    Wildcard over 8 years
    Disagree utterly with your highly opinionated and nonfactual claim that "if bash is available then so is a better language so you should never write any bash code." Also, as @sixtyfootersdude points out, you should always use #!/bin/bash unless you have tested that your script works with any POSIX shell.
  • Wildcard
    Wildcard over 8 years
    "In the end, Debian had to make slight adjustments to the Almquist shell, to add support for a couple of non-POSIX shell constructs that were simply too deeply and widely embedded and too useful to get rid of." — Which of your many links has info about this? That sounds very interesting. :)
  • Wildcard
    Wildcard over 8 years
    "When writing your own scripts, you should point the shebang to the most general thing that you have tested against....By setting the shebang to #!/bin/sh you are promising that the script will work with all implementations of sh." Excellent point. +1, and you have made me rethink how I will write my shebangs for the future. Thank you! :)
  • Jules
    Jules over 8 years
    From this answer: the advantage of #!/usr/bin/env python is that it will use whatever python executable appears first in the user's $PATH. The disadvantage of #!/usr/bin/env python is that it will use whatever python executable appears first in the user's $PATH. This probably also applies to sh and bash.
  • user2071406
    user2071406 over 8 years
    @schily time $shell -c '' seems to me like a good way of measuring the time a shell takes to start and to exit.
  • user2071406
    user2071406 over 8 years
    Well, not all implementations of /bin/sh; just the POSIX-compliant ones.
  • user2071406
    user2071406 over 8 years
    No, you should not use #!/bin/env anything in a portable script. It is perfectly reasonable to assume that /bin/sh exists and is a working, POSIX-compliant shell. On the other hand, none of my systems have a /bin/env.
  • slebetman
    slebetman over 8 years
    IMHO, performance is only a motivator to get people to agree with the changes. The initial big reason for changing was that bash kept breaking backwards compatibility. I've personally had to fix bash problems at least 3 times in my career because the script would work in one version of bash but failed in another (usually happens after OS upgrade). Dash is not merely a slightly faster interpreter. It is also much more stable in terms of behaviour.
  • schily
    schily over 8 years
    Well, I did some test with a similar command before (shell -c ':') and even on a 10 year old machine, I get something around 2ms per Bourne Shell call (dash is a bit faster) and 4ms for ksh93, but yash is even slower than ksh93 and times in that magnitude are not a problem - in special as /bin/echo (6kB) needs 1.5ms.
  • schily
    schily over 8 years
    I would be interested whether the comments in the Schily Bourne Shell man page, see schilytools.sourceforge.net/bosh.html are suitable to permit people to understand how to write a portable script (that only depends on Bourne Shell features from 1989). What I did was mentioning every enhancement that was not in old Bourne Shells. BTW: I am also interested to know a list of bashisms in dash.
  • Jan Hudec
    Jan Hudec over 8 years
    @RuiFRibeiro, nothing is statically linked in Debian. Dash is more limited, but it is limited mainly in interactive features (no completion and such) so that it is faster for scripts.
  • 41754
    41754 over 8 years
    @BinaryZebra when using legacy code as in strace, valgrind, ffmpeg, android...
  • Wayne Werner
    Wayne Werner over 8 years
    Also busybox and alpine linux which are much lighter weight than their counterparts.
  • VarunAgw
    VarunAgw over 8 years
    @Sobrique Consider tagging next time so the person can get notification ;)