Bash vs sh vs zsh vs fish
There are many shells available on Linux and macOS. Each makes different trade-offs between portability, features, and ease of use. Knowing the difference helps you pick the right one for each situation.
POSIX sh — Maximum Portability
sh is the POSIX standard shell — the baseline that every UNIX-like system must provide. Scripts written in POSIX sh will run on Linux, macOS, BSDs, and even some embedded systems.
#!/bin/sh
# This script runs on virtually any UNIX-like system
name="Alice"
echo "Hello, $name"
What sh does NOT have:
- Arrays
[[ ]](only[ ])$'...'syntaxlocalvariable declarations (some shells support it, not required by POSIX)- Process substitution
<(cmd)
When to use sh: When your script needs to run on systems where Bash is not guaranteed — like Alpine Linux containers, legacy systems, or /etc/init.d/ scripts.
On Ubuntu,
/bin/shis actually dash — a faster, minimal POSIX shell, not Bash.
Bash — The Linux Standard
Bash (Bourne Again Shell) is the default shell on most Linux distributions and the scripting standard for DevOps and sysadmin work.
#!/usr/bin/env bash
# Bash-specific features
declare -A config # associative array (not in sh)
config[host]="localhost"
config[port]="5432"
files=(/etc/*.conf) # array from glob
echo "Found ${#files[@]} config files"
[[ "$1" =~ ^[0-9]+$ ]] && echo "Numeric argument" # regex in [[
What Bash adds over sh:
- Arrays (indexed and associative with
declare -A) [[ ]]with regex support (=~)(( ))arithmeticmapfile/readarray- Brace expansion:
{a,b,c},{1..10} - Process substitution:
<(cmd),>(cmd) $'...'escape sequences:$'\n',$'\t'
When to use Bash: For all your scripts unless you specifically need maximum portability. For interactive use on Linux. For all DevOps scripting, CI/CD scripts, and automation.
zsh — macOS Default
zsh (Z Shell) has been the default shell on macOS since Catalina (2019). It is Bash-compatible for most everyday use, with a richer feature set.
# zsh-specific features
setopt autocd # type directory name to cd into it
setopt globdots # include hidden files in globs
bindkey -v # vi key bindings in zsh
# Better tab completion
autoload -Uz compinit && compinit
What zsh adds:
- Better interactive tab completion (context-aware, menu-driven)
- Spell correction for mistyped commands
- Shared command history across multiple terminal sessions
- Richer glob patterns (e.g.,
**/*.logwithoutglobstar) oh-my-zshplugin ecosystem — themes, Git prompt, hundreds of plugins
When to use zsh: As your interactive shell on macOS or if you prefer it on Linux. Do NOT use zsh-specific syntax in scripts you intend to share or run on Linux servers — use Bash for scripts.
fish — Friendly Interactive Shell
fish (Friendly Interactive Shell) is designed for usability out of the box, with no configuration required.
# fish syntax is different from bash — NOT POSIX compatible
function greet
echo "Hello, $argv[1]"
end
greet "Alice"
What fish offers:
- Autosuggestions as you type (grey ghost text showing the likely completion)
- Syntax highlighting in the prompt (invalid commands appear red)
- Browser-based configuration:
fish_config - No need to learn configuration — it works well immediately
What fish does NOT have:
- POSIX compatibility (different syntax for variables, loops, functions)
- It cannot run most Bash scripts without modification
When to use fish: As your personal interactive shell if you prefer the experience. Never for scripting — always write scripts in Bash or sh.
dash — The Fast POSIX Shell
dash is the default /bin/sh on Ubuntu and Debian. It starts and runs scripts significantly faster than Bash because it implements only POSIX sh with no extras.
# Ubuntu: /bin/sh is dash
$ ls -la /bin/sh
lrwxrwxrwx 1 root root 4 Jan 1 2023 /bin/sh -> dash
When you see dash: When system startup scripts (/etc/init.d/) or package manager scripts run — they use /bin/sh for speed. They do not use Bash features.
Comparison Table
| Shell | POSIX? | Arrays? | Interactive | Scripting | Best For |
|---|---|---|---|---|---|
sh / dash |
✅ Yes | ❌ No | ⚠️ Basic | ✅ Portable | System scripts, containers |
bash |
✅ Yes | ✅ Yes | ✅ Good | ✅ Standard | All scripting, DevOps |
zsh |
⚠️ Mostly | ✅ Yes | ✅ Excellent | ⚠️ Non-portable | macOS daily use |
fish |
❌ No | ✅ Yes | ✅ Excellent | ❌ Non-portable | Personal interactive use |
The Golden Rule
Interactive shell: use whatever you prefer (zsh or fish are great).
Scripts: always use#!/usr/bin/env bash(or#!/bin/shfor max portability).
Never write a script in fish syntax and expect it to run on a Linux server.
| ← Back to What is a Shell | Next → Bash Versions |