Comparing Bash, Zsh, and Fish Shell

Shell environments play a crucial role in a developer’s workflow. Here’s a comparison of three popular shells: Bash, Zsh, and Fish.

Bash (Bourne Again SHell)

The default shell on most Linux distributions and macOS (until Catalina).

Key Features

  • POSIX-compliant
  • Command history
  • Tab completion
  • Scripting capabilities
  • Configurable via .bashrc and .bash_profile

Example Commands

# Variable assignment
name="John"
echo $name

# Conditional
if [ -f file.txt ]; then
    echo "File exists"
fi

Zsh (Z Shell)

A powerful shell that extends Bash functionality.

Key Features

  • Improved tab completion
  • Spelling correction
  • Path expansion
  • Shared command history across sessions
  • Theme support via Oh-My-Zsh
  • Configurable via .zshrc

Example Commands

# Array operations
array=(one two three)
echo ${array[2]}  # Outputs "two" (zero-indexed)

# Extended globbing
ls -la **/*.txt  # Recursive search

Fish (Friendly Interactive SHell)

A user-friendly shell focused on interactivity and usability.

Key Features

  • Syntax highlighting
  • Autosuggestions based on history
  • Web-based configuration
  • Out-of-the-box experience with minimal setup
  • Configurable via config.fish

Example Commands

# Variable assignment (note the different syntax)
set name "John"
echo $name

# Conditionals use different syntax
if test -f file.txt
    echo "File exists"
end

Major Differences

FeatureBashZshFish
SyntaxPOSIX-compatiblePOSIX-compatibleNot POSIX-compatible
Configuration.bashrc.zshrcconfig.fish
Tab completionBasicAdvancedAdvanced with preview
ScriptingWidely usedSimilar to BashDifferent syntax
Default promptBasicCustomizableFeature-rich by default
Plugin ecosystemLimitedOh-My-ZshFisher/Oh-My-Fish

When to Choose Each

  • Bash: When portability and wide compatibility are priorities
  • Zsh: When you want a balance of features and compatibility
  • Fish: When you prioritize user-friendliness and modern features over compatibility

Fish is great for interactive use, but scripts written for it won’t work in Bash or Zsh environments. Zsh offers a good balance, while Bash remains the standard for script compatibility.

Command Compare

Command Comparison

Here’s a comparison of common commands across the three shells:

OperationBashZshFish
Define variablevar="value"var="value"set var "value"
Reference variable$var or ${var}$var or ${var}$var
Conditionalif [ $var -eq 0 ]; thenif [ $var -eq 0 ]; thenif test $var -eq 0
End blockfifiend
For loopfor i in {1..5}; dofor i in {1..5}; dofor i in (seq 1 5)
Functionfunction name() {function name() {function name
Command substitution$(command)$(command)(command)
Pipe and grepls -la | grep "file"ls -la | grep "file"ls -la | grep "file"
Environment varsexport VAR=valueexport VAR=valueset -x VAR value
Source filesource file.shsource file.zshsource file.fish
Comment# Comment# Comment# Comment

Key Command Differences Between Bash and Zsh

While Bash and Zsh share many commands, Zsh offers several enhancements:

Globbing (Pattern Matching)

  • Bash: ls *.txt
  • Zsh: Supports recursive globbing with ** - ls **/*.txt

Array Indexing

  • Bash: Zero-based indexing ${array[0]}
  • Zsh: Both zero-based and one-based indexing supported

Wildcard Matching

  • Bash: Basic wildcards
  • Zsh: Extended options like ls -d ^*.txt (negation)

Directory Stack

  • Both: Support pushd/popd
  • Zsh: Additional dirs -v with enhanced output

Spelling Correction

  • Bash: None built-in
  • Zsh: setopt CORRECT or CORRECT_ALL

History Expansion

  • Bash: Basic history with !
  • Zsh: Enhanced with options like !:0 (command name only)

Path Replacement

  • Bash: Limited
  • Zsh: cd old new replaces “old” with “new” in current path