Zsh vs Bash vs Fish: Choosing an Interactive Shell

Zsh vs Bash vs Fish: Choosing an Interactive Shell

Your login shell and your scripting shell are separate decisions, and conflating them causes most of the anxiety about switching.

Scripts run under their shebang. A file starting #!/usr/bin/env bash runs under bash regardless of what shell you type into. Changing your interactive shell breaks nothing.

Bash

The default nearly everywhere and the one every script assumes.

For. Universal. Every tutorial, every Stack Overflow answer, and every script works without translation. Modern bash has a reasonable feature set, and version 5 improved associative arrays and several other things.

Against. Tab completion is basic without add-ons. Configuration for a good interactive experience takes real effort. Defaults are conservative.

Worth knowing that macOS ships bash 3 for licensing reasons, so declare -A and other bash 4 features are absent there.

Zsh

Bash-compatible enough that most knowledge transfers, with a considerably better interactive experience.

For. Completion is the headline: it completes command options, argument values, remote paths over SSH, and git branches, with descriptions. Globbing is more powerful, including recursive **/*.txt without extra options and qualifiers like *(.) for regular files only. Spelling correction, shared history across sessions, and a large plugin ecosystem.

Against. Configuration is where time goes. Frameworks help and add startup cost.

chsh -s "$(which zsh)"

On frameworks: oh-my-zsh is the common entry point and loads a great deal by default, which shows in startup time. A minimal .zshrc with zsh-autosuggestions, zsh-syntax-highlighting, and a fast prompt gives most of the benefit and starts instantly.

Fish

The most pleasant out of the box, and deliberately not POSIX compatible.

For. Autosuggestions from history as you type, syntax highlighting showing invalid commands in red before you run them, completions generated from man pages, and a web-based configuration interface. All of this works with no configuration at all.

Against. The syntax is different, and that is the whole tradeoff.

# bash
export VAR=value
if [ -f file ]; then echo yes; fi
for i in $(seq 5); do echo $i; done

# fish
set -x VAR value
if test -f file; echo yes; end
for i in (seq 5); echo $i; end

Pasting a command from documentation frequently requires translating it. For simple commands nothing changes; anything with variable assignment, export, or command substitution does.

chsh -s "$(which fish)"

A common compromise is Fish interactively and bash for scripts, which works fine because they are separate concerns.

Startup time

A slow shell is noticeable every time you open a terminal.

time zsh -i -c exit

Anything over about 200ms is worth investigating. The usual causes are a heavy framework, version managers like nvm or pyenv initialising, and a prompt that runs git status on every render.

Bisect by commenting out sections of your config. Lazy-load version managers so they initialise on first use rather than at startup.

The honest recommendation

Stay on Bash if you administer many machines. Being fluent in the shell that is actually there matters more than a nicer local experience, and you will spend time in bash on servers regardless.

Zsh if you want a better daily experience without giving up compatibility. This is the common choice and the reason macOS switched its default.

Fish if you value the out-of-box experience most and do not mind translating occasional snippets. It is genuinely the friendliest, and the incompatibility is real rather than theoretical.

Whichever you choose, write scripts in bash. Our bash error handling and quoting guides apply to the scripts you write regardless of what you type into.

Frequently Asked Questions

Does changing my login shell break my scripts?

No. Scripts run under whatever their shebang line names, not your interactive shell. A bash script with a bash shebang runs identically whether your login shell is bash, zsh, or fish.

Is Fish not being POSIX compatible a real problem?

Only when you paste shell snippets from the internet, which is often. Fish syntax differs enough that most copied commands with variable assignments, exports, or command substitution need adjusting. For interactive use it is a small tax; for writing scripts you should use bash anyway.

What does Zsh give me over Bash?

Far better tab completion including command options and arguments, more powerful globbing, spelling correction, and a large plugin ecosystem. It is close enough to bash that most knowledge transfers directly.

Should I use oh-my-zsh?

It is the easiest way to get a configured Zsh and it loads a great deal you will never use, which noticeably slows startup. A minimal configuration with a few chosen plugins is faster and easier to reason about.

Why is my shell slow to start?

Usually a heavy framework, version managers initialising, or a prompt calling git on every render. Time it by running the shell with a timer and bisect your config by commenting out sections.

Which shell should I write scripts in?

Bash, with a bash shebang, or POSIX sh if it must run absolutely everywhere. Bash is present on every Linux system and is what other people can read and maintain. Interactive shell choice is a separate decision.