bash Is Not sh
As promised, this is a post about shebang usage, shell script portability, and bash vs sh syntax.
If you need bashisms, use
If you want to use
#!/bin/sh, make sure your code can run on an
implementation other than
sh, or the Bourne Shell, was a shell first introduced in 1977 with
Research UNIX 7, from Bell labs.
sh was written by Stephen Bourne, as a replacement for earlier UNIX shells.
sh takes much of its syntax from ALGOL68, a programming language first
introduced in 1968. While most folks have never written a line of ALGOL68,
they’d likely be very familiar with the syntax! Structures like
esac, come from ALGOL.
For many years,
sh was the default UNIX shell, and it inspired successors
bash, and the Almquist shell
ash currently in use by FreeBSD,
NetBSD, Debian and Ubuntu (as
dash), and BusyBox!
bash, or the Bourne-Again Shell, is a GNU replacement for the Bourne Shell,
which seeks to extend
bash is perfectly capable of running
sh scripts, it brings some extra
non-portable features to the table.
sh is the original implementation of the language.
bash seeks to replace and extend the language, as a GNU developed clone.
This is not inherently bad.
However, as you will see, assuming that everyone has
bash can be a bad thing.
Location location location!
By default, there is always an
This leads to the common usage of
#!/bin/sh as the shebang in most shell
When writing POSIX
sh, or adhering to the common feature set shared by
bash, this is fine!
However, on Linux,
/bin/sh tends to be a symlink to
bash, which on Linux
resides usually at
When using the
#!/bin/sh shebang and writing bashisms, you inadvertently
open yourself up to portability issues, as bashisms can only be interpreted by
For more on bashisms, see:
The Debian project famously ran into this issue when replacing their
dash as opposed to
The same occurred with Ubuntu.
“there have been a certain number of shell scripts written specifically for
Linux systems, some of which incorrectly stated that they could run with
/bin/sh when in fact they required
bash, and these scripts will have
broken due to this change.”
As you can see, assuming
bash can lead to some problems!
Why not just use
#!/bin/bash, the favorite of many Linux shell programmers who believe
they’ve solved the issue of assuming
Why not just specify that you want
While the logic is sound, there’s still a problem.
See, many OSes that aren’t Linux (yes, Linux isn’t UNIX and wasn’t even the
first UNIX-like,) either don’t have
bash installed at all, or have it
somewhere other than
For example, in FreeBSD, we use the Almquist Shell,
ash, as our
ash is a very light re-implementation of
sh. We don’t have
bash as part
of the base system at all!
If one wants to use
bash, they can. A simple
pkg install bash will do the
trick! However, ports and Base are separate in FreeBSD.
bash will install to
/usr/local/bin/bash, and not
I’m sure you can see the problem of using
#!/bin/bash now: it won’t work
on anything other than Linux distros that have
bash installed to
What then, can we do?
env is fucking magic!
python programmer will undoubtedly be familiar with, the shebang
#!/usr/bin/env python is the recommended way to call
This too holds true for any shell script that doesn’t adhere to POSIX or the
common set of features amongst
sh and its replacements and clones!
If you wish to use
#!/bin/sh, you should be sticking to these minimal common
features, as a matter of course. It may be a little harder, but it supports
portability between platforms and ensures your code will run on the widest
variety of systems there is!
But I NEEEEEED
bash features! (I don’t want to use
awk for arrays)
That’s fine! As mentioned before,
env is fucking magic!
It will search your
$PATH, for the interpreter specified, and use it!
While we’ve established that
#!/bin/bash isn’t portable,
#!/usr/bin/env bash will result in
env searching your
any version of
bash it can find.
This means whether
bash is at
/usr/local/bin/bash, or anywhere
else, you’ll be able to correctly call
bash and use them there bashisms to
your heart’s content!
Additionally, it serves as a notice to anyone seeking to use your program
that it requires
bash, and not
sh, or any other
This is especially important for code you want to ensure is run correctly on non-Linux systems.
But I can’t find any resources on pure
sh! (How do I know if it’s a bashism?)
Do you want to write pure
sh? In my opinion, this is the best way to go,
though I understand many folks like their bashisms, or simply don’t know
Resources are available all over the internet!
ash documentation is a great start.
Additionally, using a linter like
shellcheck is a great way to find those
sneaky bashisms and learn new ways of doing The Thing!
Can you really write good shell code without bashisms?
Perfect examples are iocage Legacy (or the fork
iocell), a FreeBSD
manager written in pure
vzvol, a ZFS zvol manager written by
You can find
iocell at https://github.com/bartekrutkowski/iocell
vzvol at https://github.com/RainbowHackerHorse/vzvol
vzvol is also a great example of modular shell code, which is a great way
to ensure extensibility and cleanliness of code.
I source functions from files the same way you would import functions
and libraries in C.
bash isn’t bad. This isn’t me bashing
I just want things Done Right, so we can all benefit from each others’ code
without having to modify or edit!
Examples of pure
Using env for Portability (Thanks, @nixcraft !)