zsh vs bash: Which Shell Should You Use?
Bash vs zsh compared honestly: POSIX scripting, tab completion, history, Oh My Zsh, Starship prompts, and exactly when each shell is the right choice.
Before you start
- ▸A working Linux terminal with sudo access
- ▸Basic familiarity with editing files in a text editor
- ▸curl and git installed (required for Oh My Zsh)
Both bash and zsh ship on virtually every Linux distribution. Bash has been the default GNU/Linux shell for decades; zsh has been macOS's default since Catalina and is increasingly popular on Linux desktops. The differences matter — choose the wrong one for the job and you'll hit subtle bugs in scripts or miss out on features that genuinely speed up interactive work. This guide lays out the real trade-offs so you can make an informed choice.
A Quick History
Bash (Bourne Again SHell) was written by Brian Fox in 1989 as a free replacement for the Bourne shell. It is the reference implementation for most shell scripting on Linux. Zsh (Z Shell) appeared in 1990, borrowing ideas from bash, ksh, and tcsh. It has always been feature-rich but complex. Today, bash is version 5.2 and zsh is version 5.9 — both are actively maintained.
POSIX Compliance: Why It Matters for Scripts
POSIX defines a standard shell language that ensures scripts run across different Unix-like systems. Bash is mostly POSIX-compliant but adds many extensions — [[ for tests, arrays, local variables, process substitution, and more. Scripts that use these extensions and start with #!/bin/bash are perfectly fine; scripts that claim to be portable by using #!/bin/sh but rely on bashisms will break on systems where /bin/sh is dash, busybox, or another shell.
Zsh is not a POSIX shell by default. It has a compatibility mode (emulate sh or emulate ksh), but interactive zsh behaves differently enough that bash scripts often fail when sourced inside zsh. The practical rule: write scripts with an explicit #!/bin/bash or #!/bin/sh shebang, and keep your interactive shell choice separate from your scripting choice.
Interactive Features: Where Zsh Wins
For daily terminal use, zsh's interactive features are meaningfully better out of the box and dramatically better with light configuration.
Tab Completion
Bash completion relies on the bash-completion package and is bolted on. Zsh has a built-in completion system (compinit) that understands command options, flags, and even remote hostnames. Enable it by adding these lines to ~/.zshrc:
autoload -Uz compinit
compinit
After this, typing git che<TAB> shows subcommand choices; kill <TAB> lists running processes by name. Bash can approximate this with extra packages, but zsh does it natively.
Spelling Correction
Add setopt CORRECT to ~/.zshrc and zsh will suggest corrections for mistyped commands:
setopt CORRECT
setopt CORRECT_ALL
Shared and Configurable History
Zsh lets multiple terminal sessions share history in real time and offers fine-grained deduplication:
setopt SHARE_HISTORY
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE # lines starting with a space are not saved
HISTSIZE=10000
SAVEHIST=10000
HISTFILE=~/.zsh_history
Glob Expansion
Zsh's extended globbing is powerful. Recursively match all .log files without find:
ls **/*.log
Enable it with setopt EXTENDED_GLOB. Bash can do recursive globs with shopt -s globstar, but zsh's qualifier syntax (ls *(m-7) for files modified in the last 7 days) goes further.
Prompt Customisation
Bash Prompts
Bash uses PS1 with escape codes. A coloured prompt showing user, host, and path:
PS1='\[\e[32m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]\$ '
Git branch info requires parsing git rev-parse yourself or sourcing /usr/lib/git-core/git-sh-prompt.
Zsh Prompts
Zsh uses PROMPT (or PS1) with percent-escape codes and supports right-side prompts (RPROMPT). Enable prompt substitution first:
setopt PROMPT_SUBST
PROMPT='%F{green}%n@%m%f:%F{blue}%~%f%# '
The vcs_info module (built into zsh) adds Git status without external tools. Both shells, however, are now routinely paired with Starship for serious prompt work.
Starship: Cross-Shell Prompt
Starship is a fast, Rust-written prompt that works identically in bash, zsh, fish, and others. Install it once and get Git status, language versions, exit codes, and more with zero per-shell configuration.
Debian/Ubuntu:
curl -sS https://starship.rs/install.sh | sh
Fedora/RHEL:
sudo dnf install starship
Arch:
sudo pacman -S starship
Then add the init line to the appropriate config file:
# For bash — add to ~/.bashrc
eval "$(starship init bash)"
# For zsh — add to ~/.zshrc
eval "$(starship init zsh)"
Starship is the pragmatic choice if you want a great prompt in either shell without learning shell-specific escape codes.
Oh My Zsh
Oh My Zsh is a community framework that manages zsh plugins and themes. It is wildly popular and genuinely useful, but understand what you're getting: it sources a large number of files at startup and can add 100–400 ms to your shell launch time on slow machines. On modern hardware it's unnoticeable.
Install it (requires curl or wget, git, and zsh already installed):
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
Useful built-in plugins to enable in ~/.zshrc:
plugins=(git docker kubectl z sudo history-substring-search)
The z plugin gives you directory jumping by frecency — type z proj to jump to the project directory you visit most. The history-substring-search plugin binds UP/DOWN to search history by what you've already typed, which is more useful than plain reverse-i-search.
If startup time concerns you, consider zinit or zplug as lighter alternatives, or skip frameworks entirely and configure zsh manually — it takes about 30 lines.
Installing Zsh and Making It Your Default
Debian/Ubuntu:
sudo apt install zsh
chsh -s $(which zsh)
Fedora/RHEL:
sudo dnf install zsh
chsh -s $(which zsh)
Arch:
sudo pacman -S zsh
chsh -s /usr/bin/zsh
Log out and back in for the change to take effect. Verify with:
echo $SHELL
Expected output: /usr/bin/zsh or /bin/zsh (varies by distro).
When to Use Bash
- Scripts intended to run on multiple systems — always use bash (or sh). Do not rely on zsh extensions in portable scripts.
- Server environments and containers — bash is always present; zsh may not be installed.
- CI/CD pipelines — GitHub Actions, GitLab CI, and most CI runners default to bash. Write your pipeline scripts accordingly.
- You're already comfortable with bash and don't need the extras — there's no award for switching.
When to Switch to Zsh
- You spend significant time at an interactive terminal and want better completion, history, and globbing.
- You want prompt frameworks (Oh My Zsh, Prezto) without writing your own prompt scaffolding.
- Your team or project uses zsh conventions and dotfiles.
- You're on macOS and want your local shell to match your Linux dev environment (macOS already defaults to zsh).
Verification
After switching to zsh and setting up your config, confirm the essentials are working:
# Confirm active shell
echo $SHELL
# Confirm completion is active
type compinit
# Check zsh version
zsh --version
Sample output will look something like zsh 5.9 (x86_64-pc-linux-gnu) — exact version varies by distro.
Troubleshooting
Bash scripts fail when sourced in zsh
Never source a bash script directly in zsh. Run it as a subprocess: bash myscript.sh. If you must source it, add emulate bash at the top of the script, though this is fragile.
chsh has no effect after reboot
On systems using LDAP or SSSD for user accounts, chsh may not persist because the shell is set centrally. Ask your sysadmin, or set the shell inside your ~/.bashrc as a workaround: exec zsh as the last line will launch zsh for every interactive bash session.
Oh My Zsh makes the terminal slow
Run zsh -i -c exit with timing to benchmark startup. Disable plugins one at a time. The nvm plugin is a notorious offender — replace it with lazy-loading or use fnm instead.
Completion not working after install
If compinit warns about insecure directories, your $fpath contains world-writable directories. Fix permissions:
compaudit | xargs chmod go-wFrequently asked questions
- Can I use Oh My Zsh plugins without Oh My Zsh itself?
- Yes. Most plugins are plain zsh scripts. You can clone individual plugin repos and source them directly in ~/.zshrc, or use a lightweight plugin manager like zinit or antibody.
- Will switching to zsh break my existing bash aliases and functions?
- Bash aliases use the same syntax as zsh, so most transfer directly. Functions that use bash-specific syntax like [[ with certain flags or $BASH_SOURCE may need small adjustments.
- Is zsh faster or slower than bash?
- For script execution, bash and zsh perform comparably. Interactive startup time is where zsh can lag if you load many plugins — a bare zsh with minimal config starts as fast as bash.
- Which shell is better for SSH sessions on remote servers?
- Bash. It is installed by default on essentially every Linux server. Relying on zsh on a remote machine means installing and configuring it everywhere, which is rarely worth it for admin work.
- Does Starship work on Wayland terminals?
- Yes. Starship is a prompt tool, not a terminal emulator, so it is completely display-server agnostic and works in any terminal running under Wayland or X11.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.