Use #!/bin/sh or #!/bin/bash for Ubuntu-OSX compatibility and ease of use & POSIX

I know that I can use either as the first line of scripts to invoke the desired shell.

Would #!/bin/sh be recommended if compatibility with all unix systems is an absolute requirement?

In my case the only OS’ I care about are Ubuntu (Debian) and OSX.
Given that, could I use #!/bin/bash and be assured it would work on both systems?
Would this also make it easier to use scripts with more modern and clearer syntax for commands? Does using #!/bin/sh also relate to using POSIX ?

Asked By: Michael Durrant


In Debian and Ubuntu, /bin/sh is dash, which is a POSIX-compliant shell. If you specify #!/bin/sh, you must limit yourself to POSIX statements in your script. (The advantage being that dash starts faster than bash, so your script can get its job done in less time.)

On many (most?) other Linux systems, /bin/sh is bash, which is why many scripts are written with #!/bin/sh as their shebang line even though they use bash extensions.

If you want to use bash extensions, the safest approach on all systems is to specify #!/bin/bash; that way you’re explicitly stating your dependency on bash. You need to do this on Debian and Ubuntu. As an added bonus, when started as /bin/sh bash de-activates some extensions (see the description of bash POSIX mode for details); so specifying #!/bin/bash is necessary to get the full benefit of bash.

On OS X /bin/bash is available too, and /bin/sh is bash. Specifying #!/bin/bash will work fine there as well.

Answered By: Stephen Kitt

Yes, both OSX and Linux will come with /bin/bash. You should be perfectly safe. However, that is not POSIX. The POSIX shell is at /bin/sh on most (all?) systems and that is the most portable approach and the only way to be POSIX compatible.

Note that while on many systems /bin/sh points to bash, on others it can point to different shells. It’s a symlink to dash on Debian and Ubuntu for example. Also, even if /bin/sh is a link to bash, the behavior of the shell changes when it is called as sh (from man bash, emphasis mine):

If bash is invoked with the name sh, it tries to mimic the
startup behavior of historical versions of sh as closely as
possible, while conforming to the POSIX standard as well.
invoked as an interacā€ tive login shell, or a non-interactive
shell with the –login option, it first attempts to read and
execute commands from /etc/profile and ~/.profile, in that
order. The –noprofile option may be used to inhibit this
behavior. When invoked as an interactive shell with the name
sh, bash looks for the variable ENV, expands its value if it is
defined, and uses the expanded value as the name of a file to read
and execute. Since a shell invoked as sh does not attempt to read
and execute commands from any other startup files, the –rcfile
option has no effect. A non-interactive shell invoked with
the name sh does not attempt to read any other startup files. When invoked as sh, bash enters posix mode after the startup
files are read.

Answered By: terdon

For starters, if you can make the assumption that Bash is preinstalled (which, to my knowledge is the case on all the systems you list), use the following hashbang to be compatible:

#!/usr/bin/env bash

this invokes whatever bash happens to be configured, no matter whether it’s in /bin or /usr/local/bin.

While on most systems across a wide range (including AIX, Solaris, several BSD flavors), bash ended up in different locations, env always ended up in /usr/bin/env. The trick, however, is not mine but from the author of the Bash Cookbook.

Anyway, yes Bash would allow you to use some “modern” features that make your life easier.

For example the double brackets:

[[ -f "/etc/debian_version" ]] && echo "This is a Debian flavor"

whereas in traditional shell dialects you’d have to resort to:

test -f "/etc/debian_version" && echo "This is a Debian flavor"

but the best about the double brackets is that they allow regular expressions for matching. The Bash Hackers Wiki will give you many tricks in that direction.

You can also use quite convenient expressions like $((2**10)) or other arithmetic expressions inline with the $((expression)) syntax.

Using backticks for subshells is fine, albeit a bit outdated. But the nesting capabilities of $(command ...) invocations are way more convenient as you won’t have to escape many things at different subshell levels.

These are but a few things Bash gives you over the traditional common POSIX sh syntax.

But if you want more power on the shell (not just in scripts), also have a look at zsh.

Answered By: 0xC0000022L

If compatibility with “all Unix systems” is an absolute requirement — and if it isn’t, why are you writing a shell script? — then, yes, you should be using #! /bin/sh, because Bash is not guaranteed to be installed anywhere, let alone in /bin.

It’s actually much, much worse than that. If you need compatibility to all Unix systems, that includes things like Solaris and AIX that froze their shell environments circa 1995. Which means you have to use things like the old-fashioned sort +N syntax — that newer systems have dropped! And it also means no shell functions, no arrays, no [[ ... ]], no ${foo#glob}, no $(( ... )) for arithmetic, possibly no $( ... )-style command substitution, small and undocumented upper limits on how big input can get, …

You can probably get away with not bothering with that much compatibility, but if it’s even an issue in the first place, I strongly recommend you consider a language that is less terrible than shell. The basic Perl interpreter is more likely to be available than Bash.

Answered By: zwol
Categories: Answers Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.