Difference between sh and Bash
Solution 1:
What is sh?
sh
(or the Shell Command Language) is a programming language described by the POSIX standard. It has many implementations (ksh88
, Dash, ...). Bash can also be considered an implementation of sh
(see below).
Because sh
is a specification, not an implementation, /bin/sh
is a symlink (or a hard link) to an actual implementation on most POSIX systems.
What is Bash?
Bash started as an sh
-compatible implementation (although it predates the POSIX standard by a few years), but as time passed it has acquired many extensions. Many of these extensions may change the behavior of valid POSIX shell scripts, so by itself Bash is not a valid POSIX shell. Rather, it is a dialect of the POSIX shell language.
Bash supports a --posix
switch, which makes it more POSIX-compliant. It also tries to mimic POSIX if invoked as sh
.
sh = bash?
For a long time, /bin/sh
used to point to /bin/bash
on most GNU/Linux systems. As a result, it had almost become safe to ignore the difference between the two. But that started to change recently.
Some popular examples of systems where /bin/sh
does not point to /bin/bash
(and on some of which /bin/bash
may not even exist) are:
- Modern Debian and Ubuntu systems, which symlink
sh
todash
by default; -
Busybox, which is usually run during the Linux system boot time as part of
initramfs
. It uses the ash shell implementation. -
BSD systems, and in general any non-Linux systems. OpenBSD uses
pdksh
, a descendant of the KornShell. FreeBSD'ssh
is a descendant of the original Unix Bourne shell. Solaris has its ownsh
which for a long time was not POSIX-compliant; a free implementation is available from the Heirloom project.
How can you find out what /bin/sh
points to on your system?
The complication is that /bin/sh
could be a symbolic link or a hard link. If it's a symbolic link, a portable way to resolve it is:
% file -h /bin/sh
/bin/sh: symbolic link to bash
If it's a hard link, try
% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash
In fact, the -L
flag covers both symlinks and hardlinks,
but the disadvantage of this method is that it is not portable —
POSIX does not require find
to support the -samefile
option, although both GNU find and FreeBSD find support it.
Shebang line
Ultimately, it's up to you to decide which one to use, by writing the «shebang» line as the very first line of the script.
E.g.
#!/bin/sh
will use sh
(and whatever that happens to point to),
#!/bin/bash
will use /bin/bash
if it's available (and fail with an error message if it's not). Of course, you can also specify another implementation, e.g.
#!/bin/dash
Which one to use
For my own scripts, I prefer sh
for the following reasons:
- it is standardized
- it is much simpler and easier to learn
- it is portable across POSIX systems — even if they happen not to have
bash
, they are required to havesh
There are advantages to using bash
as well. Its features make programming more convenient and similar to programming in other modern programming languages. These include things like scoped local variables and arrays. Plain sh
is a very minimalistic programming language.
Solution 2:
sh
: http://man.cx/sh
Bash: http://man.cx/bash
TL;DR: Bash is a superset of sh
with a more elegant syntax and more functionality. It is safe to use a Bash shebang line in almost all cases as it's quite ubiquitous on modern platforms.
NB: in some environments, sh
is Bash. Check sh --version
.
Solution 3:
This question has frequently been nominated as a canonical for people who try to use sh
and are surprised that it's not behaving the same as bash
. Here's a quick rundown of common misunderstandings and pitfalls.
First off, you should understand what to expect.
- If you run your script with
sh scriptname
, or run it withscriptname
and have#!/bin/sh
in the shebang line, you should expect POSIXsh
behavior. - If you run your script with
bash scriptname
, or run it withscriptname
and have#!/bin/bash
(or the local equivalent) in the shebang line, you should expect Bash behavior.
Having a correct shebang and running the script by typing just the script name (possibly with a relative or full path) is generally the preferred solution. In addition to a correct shebang, this requires the script file to have execute permission (chmod a+x scriptname
).
So, how do they actually differ?
The Bash Reference manual has a section which attempts to enumerate the differences but some common sources of confusion include
-
[[
is not available insh
(only[
which is more clunky and limited). See also Difference between single and double square brackets in Bash -
sh
does not have arrays. - Some Bash keywords like
local
,source
,function
,shopt
,let
,declare
, andselect
are not portable tosh
. (Somesh
implementations support e.g.local
.) - Bash has many C-style syntax extensions like the three-argument
for((i=0;i<=3;i++))
loop,+=
increment assignment, etc. The$'string\nwith\tC\aescapes'
feature is tentatively accepted for POSIX (meaning it works in Bash now, but will not yet be supported bysh
on systems which only adhere to the current POSIX specification, and likely will not for some time to come). - Bash supports
<<<'here strings'
. - Bash has
*.{png,jpg}
and{0..12}
brace expansion. - Bash has extended globbing facilities like
**
(globstar
) for recursing subdirectories, andextglob
for using a different, more versatile wildcard syntax. -
This is in POSIX, but may be missing from some pre-POSIX~
refers to$HOME
only in Bash (and more generally~username
to the home directory ofusername
)./bin/sh
implementations. - Bash has process substitution with
<(cmd)
and>(cmd)
. - Bash has Csh-style convenience redirection aliases like
&|
for2>&1 |
and&>
for> ... 2>&1
- Bash supports coprocesses with
<>
redirection. - Bash features a rich set of expanded non-standard parameter expansions such as
${substring:1:2}
,${variable/pattern/replacement}
, case conversion, etc. - Bash has significantly extended facilities for shell arithmetic (though still no floating-point support). There is an obsolescent legacy
$[expression]
syntax which however should be replaced with POSIX arithmetic$((expression))
syntax. (Some legacy pre-POSIXsh
implementations may not support that, though.) - Several built-in commands have options which are not portable, like
type -a
,printf -v
, and the perennialecho -e
. - Magic variables like
$RANDOM
,$SECONDS
,$PIPESTATUS[@]
and$FUNCNAME
are Bash extensions. - Syntactic differences like
export variable=value
and[ "x" == "y" ]
which are not portable (portable string comparison inexport variable
should be separate from variable assignment, and[ ... ]
uses a single equals sign). - Many, many Bash-only extensions to enable or disable optional behavior and expose internal state of the shell.
- Many, many convenience features for interactive use which however do not affect script behavior.
Remember, this is an abridged listing. Refer to the reference manual for the full scoop, and http://mywiki.wooledge.org/Bashism for many good workarounds; and/or try http://shellcheck.net/ which warns for many Bash-only features.
A common error is to have a #!/bin/bash
shebang line, but then nevertheless using sh scriptname
to actually run the script. This basically disables any Bash-only functionality, so you get syntax errors e.g. for trying to use arrays. (The shebang line is syntactically a comment, so it is simply ignored in this scenario.)
Unfortunately, Bash will not warn when you try to use these constructs when it is invoked as sh
. It doesn't completely disable all Bash-only functionality, either, so running Bash by invoking it as sh
is not a good way to check if your script is properly portable to ash
/dash
/POSIX sh
or variants like Heirloom sh