Bash, Dash, and Common Shell Packages

· linux · #linux #shell #bash #dash #sh #ubuntu #debian

Overview

On Linux systems, names such as bash, dash, sh, zsh, and fish often appear together. They are all shells: command interpreters between the user and the operating system.

A shell is responsible for several things:

  • Reading commands entered by the user.
  • Expanding variables, pipelines, redirections, and wildcards.
  • Finding and executing external programs.
  • Providing scripting language features.
  • Managing interactive sessions, such as history, completion, and prompts.

In simple terms:

User command -> Shell parses it -> Kernel/program executes it

What is bash?

bash stands for Bourne Again SHell.

It is the GNU shell implementation and is the default interactive shell on many Linux distributions.

Common paths:

/bin/bash
/usr/bin/bash

On Debian and Ubuntu, the package name is usually:

bash

You can check it with:

dpkg -S /bin/bash
apt show bash

Features of bash

bash is feature-rich. It works well as an interactive shell and is also suitable for complex scripts.

Common features include:

  • Command history.
  • Tab completion.
  • Command aliases with alias.
  • Arrays.
  • [[ ... ]] conditional expressions.
  • (( ... )) arithmetic expressions.
  • Brace expansion, such as {1..5}.
  • Process substitution, such as <(cmd).
  • Startup files such as .bashrc and .bash_profile.

Example:

#!/usr/bin/env bash

names=(alice bob charlie)

for name in "${names[@]}"; do
	if [[ "$name" == a* ]]; then
		echo "match: $name"
	fi
done

This script uses Bash arrays and [[ ... ]], so it is not a pure POSIX sh script.


What is dash?

dash is usually expanded as Debian Almquist Shell.

It is a small, fast shell that closely follows the POSIX shell standard.

Common paths:

/bin/dash
/usr/bin/dash

On Debian and Ubuntu, the package name is usually:

dash

You can check it with:

dpkg -S /bin/dash
apt show dash

Features of dash

dash is not designed to provide a rich interactive experience. Its main goal is to execute standard shell scripts quickly.

Its main characteristics are:

  • Fast startup.
  • Small size.
  • Few dependencies.
  • Good fit for system startup scripts.
  • Mostly follows POSIX sh syntax.
  • Does not support many Bash-specific extensions.

For example, this POSIX-style script works well with dash:

#!/bin/sh

name="alice"

if [ "$name" = "alice" ]; then
	echo "hello $name"
fi

However, these Bash-specific constructs usually do not work in dash:

[[ "$x" == foo* ]]
array=(a b c)
echo ${array[0]}
for i in {1..5}; do echo "$i"; done

Relationship between /bin/sh, bash, and dash

Many scripts start with:

#!/bin/sh

This means the script should be interpreted by /bin/sh.

However, /bin/sh is usually not a separate shell implementation. It is commonly a symbolic link.

On Debian and Ubuntu, it usually points to dash:

/bin/sh -> dash

On some other distributions or older systems, it may point to bash:

/bin/sh -> bash

Therefore, the same script:

#!/bin/sh

may be executed by different shells on different systems.

This is why shell script compatibility matters.


Why Ubuntu and Debian use dash as /bin/sh

The main reasons are performance and system startup speed.

Many system scripts use:

#!/bin/sh

If these scripts only use POSIX shell syntax, there is no need to start a larger and more feature-rich shell like bash.

dash starts faster and has lower overhead for simple scripts, so Debian and Ubuntu commonly use it as the default /bin/sh.

This does not mean bash is obsolete. In practice:

  • Interactive terminals still often use bash by default.
  • System scripts that use /bin/sh may be executed by dash.
  • Scripts that require Bash features should explicitly use #!/usr/bin/env bash or #!/bin/bash.

Interactive shells and scripting shells

Shells are often used in two different contexts.

Interactive shells

The command-line environment you see after opening a terminal is an interactive shell.

It emphasizes:

  • Command completion.
  • History.
  • Prompt customization.
  • Keyboard shortcuts.
  • Plugin ecosystems.

Common choices include:

  • bash
  • zsh
  • fish

Scripting shells

The shell used to execute scripts usually emphasizes:

  • Compatibility.
  • Portability.
  • Startup speed.
  • Stable syntax.

Common choices include:

  • /bin/sh
  • bash
  • dash
  • busybox sh

Common shell packages

The following table summarizes common shells on Linux.

Shell Common package name Main characteristics Suitable use cases
bash bash Feature-rich, compatible, mature ecosystem Default terminals and complex scripts
dash dash Small, fast, close to POSIX /bin/sh and system scripts
zsh zsh Strong interactive experience and plugin ecosystem Daily terminals and developer environments
fish fish Friendly defaults and modern syntax, but not POSIX-compatible Interactive terminals
ksh ksh / mksh KornShell family with a long history Unix scripts and compatibility environments
mksh mksh MirBSD Korn Shell, lightweight Embedded systems and compatible scripts
busybox ash busybox Lightweight shell bundled with BusyBox Containers, initramfs, and embedded systems
tcsh tcsh Improved C shell Older Unix environments and specific user preferences

zsh

zsh is a powerful interactive shell.

It supports some sh and bash-style syntax, but it is not a complete drop-in replacement for Bash.

Its strengths are mostly in interactive usage:

  • Powerful completion system.
  • Themes and plugins.
  • Spelling correction.
  • Advanced globbing.
  • Often used with Oh My Zsh.

Installation example:

sudo apt install zsh

Check its path:

which zsh

Change the default login shell:

chsh -s /usr/bin/zsh

fish

fish stands for Friendly Interactive SHell.

It focuses on a friendly interactive experience out of the box.

Advantages:

  • Autosuggestions.
  • Syntax highlighting by default.
  • Simple configuration.
  • Good user experience.

Its limitations are also clear:

  • Its syntax is not compatible with POSIX sh.
  • Many Bash scripts cannot be executed directly by fish.
  • It is better as an interactive shell for humans than as a general-purpose script interpreter.

Installation example:

sudo apt install fish

BusyBox ash

BusyBox is a project that combines many Unix utilities into a single executable.

It is common in:

  • Container images.
  • initramfs environments.
  • Embedded Linux systems.
  • Router systems.
  • Alpine Linux.

The common shell included with BusyBox is ash.

For example, on Alpine Linux:

/bin/sh

usually points to BusyBox ash.

Like dash, it is lightweight and suitable for resource-constrained environments, but it does not support the full set of Bash extensions.


How to write the shebang

The #! line at the beginning of a script is called a shebang. It specifies which interpreter should run the script.

POSIX shell scripts

If the script only uses POSIX sh syntax, prefer:

#!/bin/sh

This is portable and can be executed by dash, Bash in sh mode, BusyBox ash, and other POSIX-compatible shells.

Bash scripts

If the script uses Bash features, explicitly write:

#!/usr/bin/env bash

or:

#!/bin/bash

Do not write:

#!/bin/sh

and then use Bash-specific features such as arrays, [[ ... ]], source, or function.

Such scripts often fail on systems where /bin/sh points to dash.


Common compatibility issues

source vs .

Bash commonly uses:

source ./env.sh

In POSIX sh, use:

. ./env.sh

[[ ... ]] vs [ ... ]

Bash supports:

if [[ "$name" == a* ]]; then
	echo yes
fi

In POSIX sh, use case for pattern matching:

case "$name" in
	a*) echo yes ;;
esac

For simple string comparison, use:

if [ "$name" = "alice" ]; then
	echo yes
fi

Arrays

Bash supports arrays:

items=(a b c)
echo "${items[0]}"

POSIX sh does not have Bash-style arrays.

If you need arrays, using Bash explicitly is usually clearer.

Differences in echo behavior

Different shells handle echo -e and backslash escapes differently.

For portable scripts, prefer printf:

printf '%s\n' "hello"

How to inspect shells on your system

Show the current login shell:

echo "$SHELL"

Show the currently running shell process:

ps -p $$ -o comm=

Show where /bin/sh points:

ls -l /bin/sh

Show command paths:

command -v bash
command -v dash
command -v sh

On Debian and Ubuntu, find which package owns a file:

dpkg -S /bin/bash
dpkg -S /bin/dash
dpkg -S /bin/sh

How to choose

Use these practical rules.

Daily terminal use

Recommended choices:

  • bash: safe, common, and well documented.
  • zsh: better interactive experience and plugin ecosystem.
  • fish: friendly defaults, but not compatible with traditional shell syntax.

System scripts

Recommended choices:

  • Simple and portable scripts: #!/bin/sh.
  • Scripts that require Bash features: #!/usr/bin/env bash.

Containers and embedded systems

Recommended choices:

  • dash
  • BusyBox ash
  • POSIX sh-style scripts.

Complex automation scripts

Recommended choices:

  • bash
  • or a language better suited to complex logic, such as Python, Go, or Perl.

Key differences

Item bash dash
Purpose Feature-rich shell Lightweight POSIX shell
Interactive experience Strong Weak
Startup speed Slower Fast
Scripting features Rich, with many extensions Minimal, POSIX-oriented
Good default terminal Yes Usually no
Good /bin/sh implementation Possible, but heavier Very suitable
Bash arrays Supported Not supported
[[ ... ]] Supported Not supported
POSIX script compatibility Good, but Bash extensions are tempting Good for checking portability

In one sentence:

bash is good for humans and complex scripts, while dash is good for quickly executing standard sh scripts.


Practical advice

  1. If a script starts with #!/bin/sh, only use POSIX sh syntax.
  2. If a script uses Bash features, explicitly use #!/usr/bin/env bash.
  3. Do not assume /bin/sh is always Bash.
  4. On Ubuntu and Debian, /bin/sh is likely to be dash.
  5. For portable scripts, prefer printf instead of relying on echo -e.
  6. For complex logic, do not force everything into shell scripts. Use Python or another suitable language when appropriate.

Takeaway

There is not just one shell implementation.

bash, dash, zsh, fish, and BusyBox ash are shells with different design goals:

  • bash: general-purpose, powerful, and commonly used by default.
  • dash: small, fast, and suitable for /bin/sh.
  • zsh: strong interactive experience.
  • fish: friendly, but not POSIX-compatible.
  • BusyBox ash: lightweight and suitable for containers and embedded systems.

The key is to distinguish between two questions:

Is this an interactive shell for humans?
Or is this an interpreter for scripts?

Once that distinction is clear, choosing the right shell becomes much easier.