Zsh and Bash are powerful Unix shells that can make your command-line work more productive, but they take different approaches.
Bash comes standard on most Linux systems and older Mac versions. It's everywhere, works the same way across different systems, and follows standard rules. You'll find it useful if you need to write scripts that run on many different computers.
Zsh builds on what Bash offers but adds many customization options and helpful features that make your life easier. Apple made it the default Mac shell in 2019, and now many developers use it to create a more personal terminal experience.
This article compares these shells so you can pick the one that works best for you.
What is Bash?
Bash (Bourne Again SHell) forms the backbone of command-line interfaces in the Unix world. Linux users have relied on it as their main terminal experience since 1989.
Brian Fox created it for the GNU Project to replace the Bourne Shell with a free alternative. It's been the most widely used shell for over 30 years. Bash takes standard Unix shell functions and adds useful features from other shells like Korn and C shells.
Unlike fancier shells, Bash focuses on working reliably across different systems. It follows the POSIX standard while adding practical improvements for both daily use and scripting. You get command history, job control, and basic tab completion—making it a solid choice if you're a system administrator or developer working with many different systems.
What is Zsh?
Zsh (Z Shell) targets today's command-line users. It improves your terminal experience with powerful features while still working with Bash commands. Paul Falstad created it in 1990, and it's grown into a highly flexible shell that balances power features with simplicity.
Zsh puts your experience first, offering smart auto-completion, automatic spelling correction, customizable prompts, and plugin support. Tools like Oh My Zsh have made it easy to customize, letting you turn your terminal into an efficient, good-looking workspace without learning complex configuration details.
Zsh vs. Bash: a quick comparison
When you choose between these shells, you affect how efficiently you work daily and how easily your scripts run on different systems. Each shell was built with different goals in mind, so they fit different users and situations better.
Here are the key differences you should think about:
Feature | Zsh | Bash |
---|---|---|
Default installation | macOS (since Catalina), optional on Linux | Most Linux distributions, macOS (pre-Catalina) |
Configuration files | ~/.zshrc, ~/.zprofile, ~/.zshenv | ~/.bashrc, ~/.bashprofile, ~/.bashlogin |
Tab completion | Enhanced, context-aware with menu selection | Basic, improved in newer versions |
Themeable prompt | Built-in support via prompt themes | Limited, requires manual configuration |
Plugin frameworks | Oh My Zsh, Prezto, Zinit | Bash-it, some external tools |
Globbing (pattern matching) | Extended globbing by default | Requires enabling extended globbing |
Directory navigation | Auto cd, directory stacks, named directories | Basic directory navigation |
Command history | Shared history, substring search | Sequential history |
Scripting compatibility | Highly compatible with Bash | POSIX-compliant, widely supported |
Spelling correction | Built-in | Not available natively |
Path expansion | Smart path expansion and completion | Basic path expansion |
Array indexing | Zero-based | Zero-based |
Customization complexity | Simpler with frameworks, more options | More manual, fewer options |
Performance | Slightly more resource-intensive | Lightweight |
Community resources | Growing community, extensive themes/plugins | Established documentation, widespread examples |
Configuration and customization
How you set up and customize your shell affects how easy it is to use and learn. Zsh and Bash handle customization very differently because of their different design goals.
Bash keeps things simple and consistent with a straightforward setup. You'll mainly work with a few key files that load in a predictable order:
# ~/.bashrc - for interactive non-login shells
# This file contains most of your personal configuration
# Add colorized output for ls command
alias ls='ls --color=auto'
# Custom command prompt with username, hostname, and current directory
PS1='\u@\h:\w\$ '
# Set command history size
HISTSIZE=1000
HISTFILESIZE=2000
# Add custom directory to PATH
export PATH=$PATH:$HOME/bin
# ~/.bash_profile - for login shells
# Often just sources ~/.bashrc plus environment variables
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
export EDITOR=vim
Bash's setup approach is direct but you must edit files manually for most changes. You'll need to know more about shell scripting, but you'll get a light, predictable environment without extra features you don't need.
Zsh offers more ways to customize your experience and comes with many frameworks that make the process simpler:
# ~/.zshrc - primary configuration file
# Load Oh My Zsh framework
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="robbyrussell"
plugins=(git docker kubectl macos)
source $ZSH/oh-my-zsh.sh
# Custom aliases beyond what plugins provide
alias zshconfig="vim ~/.zshrc"
alias ohmyzsh="vim ~/.oh-my-zsh"
# Enable case-insensitive auto-completion
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
# Directory shortcuts
hash -d projects=~/Documents/Projects
hash -d docs=~/Documents
# Custom functions
mkcd() {
mkdir -p "$1" && cd "$1"
}
You'll find the real power of Zsh customization in frameworks like Oh My Zsh. These tools give you a plugin system that makes adding complex features easy:
# Example of installing Oh My Zsh
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# Edit ~/.zshrc to choose themes and plugins
ZSH_THEME="agnoster"
plugins=(git docker python vscode)
Zsh's setup might look more complicated at first, but these frameworks make it easier to access advanced features even if you don't know much about shells. This mix of power and ease of use has helped make Zsh increasingly popular.
Command line navigation and productivity
The faster you can use the command line, the more you can get done. Bash and Zsh help with this in different ways, but Zsh comes with more powerful features right out of the box.
Bash includes the basic navigation features that command-line users have relied on for decades. It works in a simple, functional way, though you'll need to type more for some tasks:
# Basic directory navigation in Bash
cd /path/to/directory
cd .. # Move up one directory
cd - # Toggle between current and previous directory
# Command history navigation
history # Show command history
!42 # Execute command #42 from history
!! # Execute previous command
!string # Execute most recent command starting with "string"
Ctrl+R # Reverse search through history
# Basic tab completion
cd Doc<Tab> # Completes to "Documents/" if unique
# Directory operations
mkdir -p new/nested/directory
pushd /tmp # Push directory onto stack and change to it
popd # Pop directory from stack and change to it
Bash's approach requires familiarity with specific commands and shortcuts, but provides reliable functionality across virtually any Unix-like system.
Zsh significantly enhances navigation and productivity with intelligent features that reduce typing and prevent common errors:
# Enhanced directory navigation in Zsh
cd /p/t/d<Tab> # Smart completion to "/path/to/directory"
cd ...<Tab> # Expands to "../.." (grandparent directory)
/u/l/b<Tab> # Expands to "/usr/local/bin"
# Auto-CD feature (no need for 'cd' command)
~/Documents # Changes to ~/Documents directory
# Extended history with substring search
history # Show command history
!42 # Execute command #42 from history
Alt+Up/Down # Search history based on current input
# Directory operations
take new/nested/dir # Creates and enters directory in one command
d # Show directory stack with numbers for quick access
cd -<Tab> # Interactive selection from directory history
# Advanced globbing (pattern matching)
ls -l **/*.txt # Find all .txt files recursively
ls ^*.o # List files that don't end with .o
rm -rf ^important/ # Remove all except 'important' directory
Zsh also offers spelling correction for commands and arguments, reducing frustration when dealing with typos:
# Automatic correction of mistyped commands
$ appel<Enter>
zsh: correct 'appel' to 'apple' [nyae]?
# Correction for paths
$ cd /usr/loca/bin<Enter>
zsh: correct '/usr/loca/bin' to '/usr/local/bin' [nyae]?
This emphasis on reducing friction in daily command-line tasks makes Zsh particularly appealing for developers and power users who spend significant time in the terminal.
Scripting and compatibility
Shell scripting is key for automation, and choosing between Bash and Zsh impacts how you write scripts and how easily they can run on different systems.
Bash has established itself as the de facto standard for shell scripting on Unix-like systems. Its widespread adoption means Bash scripts can generally run on any Linux distribution, macOS, or Unix-like system without modification:
#!/bin/bash
# Example Bash script demonstrating common patterns
# Define variables
USER_NAME="Admin"
LOG_FILE="/var/log/backup.log"
# Function definition
backup_data() {
local source_dir="$1"
local dest_dir="$2"
echo "Starting backup from $source_dir to $dest_dir"
# Error handling with exit codes
rsync -av "$source_dir" "$dest_dir"
if [ $? -ne 0 ]; then
echo "Backup failed with error code $?" | tee -a "$LOG_FILE"
return 1
fi
echo "Backup completed successfully at $(date)" | tee -a "$LOG_FILE"
return 0
}
# Command-line argument handling
if [ $# -lt 2 ]; then
echo "Usage: $0 <source_directory> <destination_directory>"
exit 1
fi
# Main script execution
echo "=== Backup Script Started by $USER_NAME ==="
backup_data "$1" "$2"
EXIT_CODE=$?
# Using arrays
FAILED_ITEMS=()
if [ $EXIT_CODE -ne 0 ]; then
FAILED_ITEMS+=("$1")
echo "Failed items: ${FAILED_ITEMS[*]}"
fi
exit $EXIT_CODE
Bash scripting emphasizes portability and follows POSIX standards closely, making scripts robust across environments. This universality is why most system scripts, installation procedures, and cross-platform tools use Bash rather than more specialized shells.
Zsh offers more powerful scripting features while maintaining reasonable compatibility with Bash. Its additional capabilities can make scripts more concise and readable, though with a potential trade-off in portability:
#!/usr/bin/env zsh
# Example Zsh script demonstrating enhanced features
# Define variables similarly to Bash
USER_NAME="Admin"
LOG_FILE="/var/log/backup.log"
# Function with enhanced parameter handling
backup_data() {
local source_dir="$1"
local dest_dir="$2"
print "Starting backup from $source_dir to $dest_dir"
# Error handling with extended features
rsync -av "$source_dir" "$dest_dir"
if (( $? != 0 )); then
print "Backup failed with error code $?" | tee -a "$LOG_FILE"
return 1
fi
print "Backup completed successfully at $(date)" | tee -a "$LOG_FILE"
return 0
}
# Enhanced argument handling
if (( $# < 2 )); then
print "Usage: $0 <source_directory> <destination_directory>"
exit 1
fi
# Array handling (note the different syntax)
typeset -a FAILED_ITEMS
typeset -A SUCCESS_MAP # Associative array (not available in basic Bash)
# Main script execution
print "=== Backup Script Started by $USER_NAME ==="
backup_data "$1" "$2"
EXIT_CODE=$?
# Extended pattern matching and array operations
if (( EXIT_CODE != 0 )); then
FAILED_ITEMS+=("$1")
print "Failed items: ${(j:, :)FAILED_ITEMS}" # Join array with commas
else
SUCCESS_MAP[$1]="$(date)" # Store success timestamp in associative array
print "Successful backups:"
for key val in "${(@kv)SUCCESS_MAP}"; do
print " - $key: $val"
done
fi
exit $EXIT_CODE
Zsh scripting provides more expressive syntax for arrays, pattern matching, and string manipulation. While these features can make scripts more elegant, they reduce portability to systems where Zsh isn't installed or is configured differently.
For maximum compatibility, many developers follow this guideline: - Use Bash for scripts that need to run across various systems - Use Zsh for personal automation or when Zsh is guaranteed to be available with consistent configuration
When writing Zsh scripts intended for wider distribution, it's common to include a shebang that falls back gracefully:
#!/usr/bin/env zsh
# Fall back to bash if zsh is not available
if [ -z "$ZSH_VERSION" ]; then
exec bash "$0" "$@"
fi
# Zsh-specific code follows
This approach allows leveraging Zsh's advanced features while maintaining basic compatibility with Bash environments.
Performance considerations
Performance matters, especially for scripts that run often or on systems with limited resources.
Bash is generally recognized for its lighter resource footprint and faster startup time. Its simpler design results in efficient execution for most common tasks:
# Time the startup of a minimal Bash session
time bash -c exit
# Example output:
# real 0m0.006s
# user 0m0.001s
# sys 0m0.004s
# Benchmark a loop in Bash
time bash -c 'for i in {1..1000}; do echo $i > /dev/null; done'
Bash's performance advantage is most noticeable in scenarios involving: - System startup scripts - Containers where minimal overhead is desirable - Embedded systems with limited resources - CI/CD pipelines where many shell instances are launched
Zsh typically requires more resources due to its additional features, particularly when loaded with frameworks like Oh My Zsh:
# Time the startup of a minimal Zsh session
time zsh -c exit
# Example output (usually slightly slower than Bash):
# real 0m0.011s
# user 0m0.004s
# sys 0m0.007s
# Benchmark with Oh My Zsh loaded
time zsh -c 'source ~/.zshrc; exit'
The performance difference becomes more pronounced with extensive customization: - Oh My Zsh with multiple plugins can add 100-500ms to startup time - Complex prompt themes with Git status integration may cause perceptible delays - Real-time syntax highlighting can impact performance during typing
For interactive use on modern systems, these differences are rarely problematic, but they may influence decisions in specific contexts:
# Optimizing Zsh startup time by lazy-loading plugins
# In ~/.zshrc
zinit ice wait lucid
zinit light zsh-users/zsh-autosuggestions
zinit ice wait lucid
zinit light zsh-users/zsh-syntax-highlighting
Performance optimizations like lazy-loading plugins can significantly reduce Zsh's overhead while retaining its advanced features. For most users, the productivity benefits of Zsh's features outweigh the minor performance differences in day-to-day use.
Community and ecosystem
The tools, plugins, and community around a shell can have a big impact on your overall experience over time.
Bash benefits from its position as the default shell across most Linux distributions, resulting in extensive documentation and widespread knowledge:
# Finding Bash resources
man bash # Comprehensive manual
info bash # Detailed GNU documentation
help <builtin_command> # Help for specific builtins
# Online resources are abundant for Bash
# - Stack Overflow has 100,000+ questions tagged [bash]
# - The Bash Hackers Wiki provides extensive examples
# - Advanced Bash-Scripting Guide is a comprehensive reference
Bash's mature ecosystem includes: - Guaranteed presence on virtually all Unix-like systems - Native integration with most system utilities and documentation - Standardized behavior due to POSIX compliance - Extensive examples in books, courses, and online tutorials
Zsh has developed a vibrant community focused on enhancing the interactive shell experience:
# Finding Zsh resources
man zsh # Base manual
man zshall # Complete documentation (very extensive)
man zshmodules # Information about loadable modules
man zshcontrib # Community contributions
# Key community projects
# - Oh My Zsh: Framework with 300+ plugins and 150+ themes
# - Prezto: Lighter alternative to Oh My Zsh
# - Zinit: Advanced plugin manager with lazy-loading
# - Powerlevel10k: Highly customizable prompt theme
Zsh's ecosystem is characterized by: - Strong focus on user experience and customization - Active GitHub communities developing plugins and themes - Regular updates to frameworks and tools - Emphasis on making advanced features accessible to non-experts
The difference in community focus is evident in how each shell evolves: - Bash development prioritizes stability, compatibility, and gradual improvement - Zsh community emphasizes new features, user experience, and customization options
This distinction means Bash tends to be the better choice for system-level scripting and cross-platform compatibility, while Zsh excels for daily interactive use where personalization and productivity features matter more.
Migration between shells
Switching between Bash and Zsh is common, especially for macOS users or anyone looking to improve their terminal setup.
Moving from Bash to Zsh is generally straightforward due to Zsh's compatibility features:
# Check current shell
echo $SHELL
# Install Zsh (if not already present)
# On Ubuntu/Debian:
sudo apt install zsh
# On macOS with Homebrew:
brew install zsh
# Set as default shell
chsh -s $(which zsh)
# Create initial Zsh configuration
# Option 1: Start with empty config
touch ~/.zshrc
# Option 2: Use Oh My Zsh installer (recommended for beginners)
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
When migrating to Zsh, transferring your Bash configuration requires some adaptation:
# In ~/.zshrc - Import Bash configuration
# Add this near the top before framework initialization
# Import aliases from Bash
if [ -f ~/.bash_aliases ]; then
source ~/.bash_aliases
fi
# Import environment variables from Bash profile
if [ -f ~/.bash_profile ]; then
source ~/.bash_profile
fi
# Note: Some Bash-specific syntax may need adjustment
# - Array indexing syntax differences
# - Test conditions may need parentheses instead of brackets
# - Some environment settings might use different variable names
The most common migration challenges include: - Adapting to different configuration file loading order - Adjusting to Zsh's array indexing and parameter expansion syntax - Reconfiguring prompt customizations - Learning Zsh-specific globbing patterns
For users moving from Zsh back to Bash (less common but sometimes necessary for compatibility), the process requires more manual configuration:
# Set Bash as default shell
chsh -s $(which bash)
# Create basic Bash configuration
touch ~/.bashrc ~/.bash_profile
# Configure Bash to approximate some Zsh features
# In ~/.bashrc:
# Enable advanced tab completion
if [ -f /etc/bash_completion ]; then
source /etc/bash_completion
fi
# Enhance history
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoreboth
shopt -s histappend
# Improve directory navigation
shopt -s autocd
shopt -s cdspell
shopt -s dirspell
# Customize prompt (simplified version of common Zsh prompts)
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
Most users find that the transition to Zsh is worth the adjustment period due to its enhanced features, while maintaining sufficient compatibility with their existing Bash scripts and knowledge.
Final thoughts
This article compared Zsh and Bash to help you choose the right shell for your workflow.
Zsh is excellent for interactive use, offering customization, helpful features, and a strong plugin and theme ecosystem. Bash is a solid, reliable choice with broad compatibility, fast performance, and strong support for standard scripting.
If you use the terminal often and want productivity boosts, Zsh is probably the better fit. But if you care more about portability and working in minimal or standardized environments, Bash is still a top choice.
Ultimately, both are outstanding shells—choose based on your specific workflow requirements, system environment, and preferences for customization versus standardization.
Make your mark
Join the writer's program
Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.
Write for us
Build on top of Better Stack
Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.
community@betterstack.comor submit a pull request and help us build better products for everyone.
See the full list of amazing projects on github