DashAsBinSh

Differences between revisions 5 and 6
Revision 5 as of 2008-08-06 16:30:37
Size: 10829
Editor: localhost
Comment: converted to 1.6 markup
Revision 6 as of 2008-08-07 21:58:42
Size: 10831
Editor: leibniz
Comment: The example #! lines were being treated as comments and not rendered in the HTML
Deletions are marked like this. Additions are marked like this.
Line 20: Line 20:
#! /bin/sh  #! /bin/sh
Line 22: Line 22:
#! /bin/bash  #! /bin/bash

Dash as /bin/sh

In Ubuntu 6.10, the default system shell, /bin/sh, was changed to dash (the Debian Almquist Shell); previously it had been bash (the GNU Bourne-Again Shell). The same change will affect users of Ubuntu 6.06 LTS upgrading directly to Ubuntu 8.04 LTS. This document explains this change and what you should do if you encounter problems.

The default login shell remains bash.

Why was this change made?

The major reason to switch the default shell was efficiency. bash is an excellent full-featured shell appropriate for interactive use; indeed, it is still the default login shell. However, it is rather large and slow to start up and operate by comparison with dash. A large number of shell instances are started as part of the Ubuntu boot process. Rather than change each of them individually to run explicitly under /bin/dash, a change which would require significant ongoing maintenance and which would be liable to regress if not paid close attention, the Ubuntu core development team felt that it was best simply to change the default shell. The boot speed improvements in Ubuntu 6.10 were often incorrectly attributed to Upstart, which is a fine platform for future development of the init system but in Ubuntu 6.10 was primarily running in System V compatibility mode with only small behavioural changes. These improvements were in fact largely due to the changed /bin/sh.

The Debian policy manual has long mandated that "shell scripts specifying '/bin/sh' as interpreter must only use POSIX features"; in fact, this requirement has been in place since well before the inception of the Ubuntu project. Furthermore, any shell scripts that expected to be portable to other Unix systems, such as the BSDs or Solaris, already honoured this requirement. Thus, we felt that the compatibility impact of this change would be minimal.

Of course, 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 /bin/bash, and these scripts will have broken due to this change. We regret this breakage, but feel that the proper way to address it is to make the small changes required to those scripts, discussed later in this document. In the longer term, this will promote a cleaner and more efficient system.

(This applies the same philosophy as in C and C++. Programs should be written to the standard, and if they use extensions they should declare them; that way it is clear what extensions are in use and they will at least fail with a much better error message if those extensions are not available.)

My production system has broken and I just want to get it back up!

If you are unlucky enough to have been negatively affected by this change, and only one or two shell scripts are affected, then the quickest way to fix this is to edit these scripts and change the first line to use the correct interpreter. The first line should look something like this (perhaps with some additional options):

 #! /bin/sh

Change that to the following (you can preserve any options you see):

 #! /bin/bash

In Makefiles, you can set the following variable at the top:

SHELL = /bin/bash

If the problems are more widespread and you want to change the default system shell back, then you can instruct the package management system to stop installing dash as /bin/sh:

sudo dpkg-reconfigure dash

Beware that this is a more invasive change, will undo boot speed benefits, and there is even an outside chance that there are a few scripts that now depend on some feature of dash that bash does not provide! (We expect the last problem to be rare, as the feature set of dash is largely a subset of that offered by bash, but we mention it for completeness.)

I am a developer. How can I avoid this problem in future?

We recommend that developers of shell scripts adhere to the POSIX standard, omitting those items flagged as XSI extensions. (This subset will be referred to as "POSIX shell" throughout the remainder of this document.) Doing so will improve portability to a variety of Unix systems, and will provide assurance that problems you encounter will be treated as bugs rather than as undocumented features! A special exception is that echo -n is guaranteed to be supported, although not other echo options (see below).

If you cannot use POSIX shell for some reason (perhaps you inherited maintenance of a large set of scripts and do not have time to rewrite them), then at least ensure that the first line of your script specifies /bin/bash as the interpreter.

Developers targetting the /bin/sh shipped by Solaris will have more stringent requirements, since that shell is a traditional Bourne shell predating the Korn shell and the POSIX standard: in particular, the test -e option is absent with no replacement (though most uses can be replaced with test -r), and test -h must be used rather than the test -L alias to test for symlinks. This document does not attempt to address Solaris in depth; consult your system's documentation.

There follows a list of some of the more common bash extensions (often referred to as "bashisms"). This list is not complete, but we believe that it covers most of the common extensions found in the wild. You can use dash -n to check that a script will run under dash without actually running it; this is not a perfect test (particularly not if eval is used), but is good enough for most purposes. The checkbashisms command in the devscripts package may also be helpful.

As further reading, install the autoconf-doc package and read the "Portable Shell" pages in info autoconf. This documentation is aimed at people targetting a wider variety of systems which may not support the POSIX standard, but it is nevertheless useful even to those assuming POSIX.

[

The [ command (a.k.a. test) must be used carefully in portable scripts. A very common pitfall is to use the == operator, which is a bashism; use = instead.

While dash supports most uses of the -a and -o options, they have very confusing semantics even in bash and are best avoided. Commands like the following:

[ \( "$foo" = "$bar" -a -f /bin/baz \) -o ! -x /bin/quux ]

should be replaced with:

(([ "$foo" = "$bar" ] && [ -f /bin/baz ]) || [ ! -x /bin/quux ])

In other words, use a separate [ invocation for each single test, combine them with && and ||, and use ordinary parentheses for grouping.

[[

The [[ builtin is a bashism, and has somewhat better-defined semantics than [ (a.k.a. test). However, it is still quite reasonable to use [ instead, and portable scripts must do so. Note that argument handling is not quite the same; as above, use = rather than ==.

Do not be confused by use of [[ in Autoconf macros, used to double-quote literal strings. This is for a different purpose and is not the same as bash's [[ builtin.

((

((...)) performs arithmetic expansion, but is a bashism. You should normally use something involving $((...)) instead, although note that this substitutes the value of the expression.

{

bash supports brace expansions over strings, such as /usr/lib/libfoo.{a,so}; this syntax can be useful for abbreviation, but it is not portable. Use other pathname expansions instead, or if that is not possible simply write out all the possibilities.

$'...'

$'...' is a bashism to expand escape sequences; for example, $'\t' expands to a horizontal tab. Use "$(printf '\t')" etc. instead.

$"..."

$"..." is a bashism used for translation of strings. Even if you can assume bash, do not use this feature, as it has intrinsic security problems! See the GNU gettext info documentation for details.

Instead, you should use the functions provided by gettext.sh. Again, see the gettext info documentation.

${...}

The ${...} syntax performs variable expansion; many forms of this are portable (including the useful ${foo#bar}, ${foo##bar}, ${foo%bar}, and ${foo%%bar}, but a few are not.

${!...} performs indirect variable expansion, which is a bashism; use eval instead.

${foo/bar/baz} performs pattern replacement, which is a bashism. Use sed instead, although note that it takes a regular expression rather than a shell pattern.

Array variables are not portable.

$LINENO

Many shells, including bash, expand $LINENO to the current line number in a script or function. dash does not support this.

$PIPESTATUS

The $PIPESTATUS array variable in bash contains a list of exit status values from the processes in the most-recently-executed foreground pipeline, which can be useful to detect failing processes that are not the last in their pipeline. dash does not support this. Replacing this is messy; the least bad replacement is probably to echo the exit status to an unused file descriptor.

$RANDOM

Many shells, including bash, have a magic variable $RANDOM which expands to a random integer. You should not rely on this in portable scripts. Workarounds include reading random bytes from /dev/urandom or /dev/random.

echo

Options to echo are not portable. In particular, the echo -e option is implemented by some shells, including bash, to expand escape sequences. However, dash is one of the other family of shells that instead expands escape sequences by default. Do not rely on either behaviour. If you need to print a string including any backslash characters, use the printf command instead, which is portable and much more reliable.

As a special exception, echo -n is supported on Ubuntu systems, although you may replace it with printf if you are concerned about wider portability.

function

The function builtin is a bashism, and can almost always simply be removed. If you remove it, make sure that there are parentheses after the function name. A function definition in POSIX shell looks like this:

function_name () {
    function body
}

select

The select builtin is a bashism. If you need it, sometimes you can write the same logic out by hand, perhaps in a shell function; otherwise it may be best to use /bin/bash.

source

The source builtin is a bashism. Write this simply as . instead.

Specification

The old developer specification is preserved as DashAsBinSh/Spec.

DashAsBinSh (last edited 2017-12-16 09:23:41 by 1047481448-2)