Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Bash Dictionary
  3. Bash vs zsh Differences

Bash vs zsh Differences

Since macOS Catalina (2019), the default shell changed from bash to zsh. The two shells share most of their syntax, but there are differences in array indexing and glob behavior that can trip you up if you are unaware of them. This page summarizes the key differences to keep in mind when writing scripts.

Key Differences

FeatureBashzsh
Array starting index0-based1-based
Word splitting on variables$var is split on spaces automatically$var is not split (safer)
Glob (**)Requires shopt -s globstarAvailable by default
No-match globPattern string is passed as-isRaises an error (nomatch)
Prompt variables\u, \h, \w%n, %m, %~
Option settingsshopt -s option-namesetopt option-name
Config file~/.bashrc~/.zshrc
Completion systembash-completion (external)compinit / compsys (built-in)
Right promptNot availableConfigurable via RPROMPT
Spell correctionNot availableEnabled with setopt correct

Sample Code

Array starting indices differ. bash uses 0-based indexing; zsh uses 1-based indexing.

bash
fruits=("apple" "banana" "cherry")
echo ${fruits[0]}
apple
echo ${fruits[1]}
banana
zsh
fruits=("apple" "banana" "cherry")
echo ${fruits[1]}
apple
echo ${fruits[2]}
banana

In bash, an unquoted variable is automatically split on spaces. In zsh, it is not split.

bash
msg="hello world"
for word in $msg; do echo "[$word]"; done
[hello]
[world]
zsh
msg="hello world"
for word in $msg; do echo "[$word]"; done
[hello world]

Use the recursive glob (**) to search files in subdirectories. In bash, you must first run shopt -s globstar.

bash
shopt -s globstar
ls **/*.txt
zsh
ls **/*.txt

The behavior when a glob matches no files differs. bash passes the pattern string as-is, while zsh raises an error.

bash
ls *.xyz
ls: *.xyz: No such file or directory
zsh
ls *.xyz
zsh: no matches found: *.xyz

Prompt configuration syntax differs.

bash (~/.bashrc)
PS1='\u@\h:\w\$ '
zsh (~/.zshrc)
PS1='%n@%m:%~%# '
MeaningBashzsh
Username\u%n
Hostname\h%m
Current directory\w%~
Prompt symbol (# for root)\$%#

Shell option syntax differs.

bash
shopt -s nocaseglob
shopt -s globstar
shopt -u nocaseglob
zsh
setopt no_case_glob
setopt glob_star_short
unsetopt no_case_glob

Notes

A shell script written with #!/bin/bash always runs under bash syntax, so using zsh as your daily shell does not affect script behavior. The differences matter when you type commands directly in the terminal or when writing settings in ~/.bashrc vs ~/.zshrc.

The most important difference to watch for is the array starting index. bash is 0-based and zsh is 1-based, so the same code can produce different results. If portability matters, specify #!/bin/bash explicitly and run the script with bash.

For shebang usage, see shebang / chmod +x. For variable expansion, see ${Parameter Expansion}. For glob usage in for loops, see for.

If you find any errors or copyright issues, please .