Zsh is the better interactive shell, and bash is the safer choice for scripts, because zsh does not follow POSIX by default and runs some scripts differently. Zsh completes abbreviated paths, corrects typos, and shares history between sessions. Typo correction and shared history are off until you enable them. Without a config file, both shells start in 2 ms on a ThinkPad X9-15 Gen 1 with Intel Core Ultra 7.
Zsh vs bash at a glance
Zsh and bash are Unix shells that run the same basic commands. They differ in interactive features, in defaults, and in how well scripts travel between machines. The table lists the differences that matter for daily work, and each one comes from a test on Ubuntu 24.04 (zsh 5.9, bash 5.2.21) or from a linked source.
| Bash | Zsh | |
|---|---|---|
| Default shell | Ubuntu: new users that adduser creates (/etc/adduser.conf) | macOS: all new accounts since macOS 10.15 (Apple Support) |
| Tab completion | Needs the full start of each path part | Completes abbreviated parts: /us/lo/b becomes /usr/local/bin/ |
| Typo correction | Directory names in cd, with shopt -s cdspell | Command names, with setopt CORRECT |
cd without the command | shopt -s autocd | setopt AUTO_CD |
Recursive ** | Needs shopt -s globstar | Works by default |
| History | Written to the history file when the session ends | Shared between sessions with setopt SHARE_HISTORY |
| Config file | ~/.bashrc | ~/.zshrc |
| Scripts | Portable | Not POSIX-compatible in its default mode (man zsh) |
What is zsh?
Zsh (Z shell) is a Unix command interpreter for interactive use and for scripts. According to its manual, zsh most closely resembles ksh among the standard shells and adds many enhancements. Bash is the GNU Bourne-Again SHell. Zsh does not provide compatibility with POSIX or other shells in its default mode (man zsh).
The manual lists the features of zsh: “command line editing, builtin spelling correction, programmable command completion, shell functions (with autoloading), a history mechanism, and a host of other features.” Paul Falstad wrote zsh as a student at Princeton, according to the zsh FAQ. The name comes from Zhong Shao, a teaching assistant at Princeton, according to a 2005 post on the zsh mailing list.
Zsh is the default shell on macOS (Apple Support). Ubuntu keeps bash for new users. See /etc/adduser.conf: “Default: DSHELL=/bin/bash”. Ubuntu 24.04 ships bash 5.2.21 and offers zsh 5.9 in its repositories (package 5.9-6ubuntu2).
How does tab completion differ in zsh?
Zsh completes each part of a path from its first letters, and bash needs the full start of each part. On Ubuntu 24.04, ls /us/lo/b plus Tab expands to /usr/local/bin/ in zsh. Bash leaves the line unchanged. With full parts, ls /usr/local/b plus Tab completes in both shells.
| Input plus Tab | Bash | Zsh |
|---|---|---|
ls /us/lo/b | no change | /usr/local/bin/ |
ls /usr/local/b | /usr/local/bin/ | /usr/local/bin/ |
ls /usr/bin/zs | list after the second Tab | list after the first Tab |
The zsh result needs no setup on Ubuntu, because the system file /etc/zsh/zshrc runs compinit, the function that starts the zsh completion system, before zsh reads your own .zshrc. I ran the table in a clean home directory with an empty .zshrc.
Both shells print the matches as plain columns, but zsh can open a menu instead. Add zstyle ':completion:*' menu select to .zshrc, press Tab twice, and move through the matches with the arrow keys. I tested this with Ubuntu’s recommended config plus the line: the first Tab prints the list, and the second Tab highlights a match in the menu.
Which interactive features does zsh add?
Zsh adds typo correction for commands and an option for shared history. The zsh manual lists both as off by default, and the Ubuntu setup wizard turns on shared history only if you press 2. Bash covers only part of this.
For typo correction, add setopt CORRECT to .zshrc. I typed ech hello, and zsh asked: zsh: correct 'ech' to 'echo' [nyae]?. Bash does not correct commands. On Ubuntu, bash prints a list of packages with similar command names, such as echo from coreutils, and runs nothing, while its cdspell option fixes only directory names in cd.
For shared history, add setopt SHARE_HISTORY. The zsh manual says that this option imports new commands from the history file, while bash copies its history list to $HISTFILE only when a session exits (man bash). The commands history -a and history -n append new lines and read unread lines, but you must run them yourself, for example from PROMPT_COMMAND.
Both shells can change into a directory when you type only its name. Set setopt AUTO_CD in zsh or shopt -s autocd in bash. Both options are off by default. In my test, /usr plus Enter moved the shell to /usr in both cases.
How does globbing differ in zsh?
Globbing, which the bash manual calls pathname expansion, matches file names against patterns such as *.md. Zsh expands **/ through all subdirectories by default, and bash needs shopt -s globstar first.
In a test directory with 48 Markdown files, **/*.md matches 48 Markdown files in zsh 5.9.
Bash 5.2.21 matches 1 file by default and 48 files after shopt -s globstar.
The bash manual describes the option:
If set, the pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories.
Without it, ** behaves like a single *.
Run shopt globstar to see the state. A bare bash prints globstar off. Add shopt -s globstar to .bashrc to keep it on. The find command avoids the difference, because it reads a quoted pattern itself: find . -name '*.md'.
Is zsh a good shell for scripts?
Zsh and bash treat the same script differently, because the zsh manual says that zsh does not provide compatibility with POSIX or other shells in its default mode. Bash stays the safer shell for scripts that run on other machines. Three tested examples show the gap.
| Example | Bash 5.2.21 | Zsh 5.9 |
|---|---|---|
v="a b c"; for x in $v | 3 iterations | 1 iteration |
First item of a=(x y z) | ${a[0]} prints x | $a[1] prints x, and $a[0] is empty |
echo *.nomatch with no such file | prints *.nomatch | stops with zsh:1: no matches found: *.nomatch |
In their default modes, zsh does not split an unquoted variable into words, counts array positions from 1, and reports a glob with no match as an error, while bash splits the variable, counts from 0, and passes the unmatched pattern through. Start scripts for other machines with #!/usr/bin/env bash. Keep zsh for your own interactive work. Run bash script.sh to test a script under bash.
Is zsh faster than bash?
Neither shell starts faster by itself. Without a config file, bash and zsh both start in 2 ms on a ThinkPad X9-15 Gen 1 with Intel Core Ultra 7. The config decides the start time: one line that loads gvm (Go Version Manager) in my .bashrc costs about 350 ms.
I measured with bash -i -c exit and zsh -i -c exit (bash 5.2.21, zsh 5.9, Ubuntu 24.04.5 LTS). Each row shows the median of 20 runs, and each run ends before the first prompt, so plugins that load later do not count. I tuned my .zshrc in the weeks before the test, but my .bashrc is mostly the Ubuntu default file.
| Shell | Setup | Start time |
|---|---|---|
| Bash | --norc --noprofile (no config file) | 2 ms |
| Zsh | -f (no config file) | 2 ms |
| Bash | my .bashrc without the gvm line | 11 ms |
| Bash | my .bashrc with the gvm line | 364 ms |
| Bash | source ~/.gvm/scripts/gvm alone | 319 ms |
| Zsh | sheldon source with my plugins | 21 ms |
| Zsh | Oh My Zsh with the git plugin only, for comparison | 32 ms |
| Zsh | my full .zshrc | 65 ms |
The gvm line is the outlier. My .zshrc loads gvm on first use, because the function gvm replaces itself: it deletes itself, loads the real gvm, and runs your command, while the Go environment stays ready from the first prompt.
if [[ -s "$HOME/.gvm/environments/default" ]]; then
source "$HOME/.gvm/environments/default"
gvm() { unfunction gvm; source "$HOME/.gvm/scripts/gvm"; gvm "$@" }
fi
My .bashrc still loads gvm at every start.
How do you install zsh and make it the default shell?
Zsh is in the Ubuntu repositories, so sudo apt install zsh installs it. Run zsh to try it in the current terminal.
The first start opens a setup wizard that asks what to write into .zshrc. Press 2 to write the config that Ubuntu recommends.
sudo apt install zsh
The wizard offers q (quit), 0 (create a .zshrc that holds only a comment), 1 (main menu), and 2 (write the config that the system administrator recommends). Choose 2. Ubuntu’s file saves 1000 history lines to ~/.zsh_history, shares them between sessions, and loads tab completion. Choose 0, and zsh saves no history. Shared history, AUTO_CD, and CORRECT then stay off. I checked both cases in a clean home directory: I pointed HOME at an empty folder, so that no old config file could change the result of the test.
If you pressed 0 already, copy the recommended file. This command overwrites your .zshrc:
cp /etc/zsh/newuser.zshrc.recommended ~/.zshrc
Add two lines at the end of ~/.zshrc for typo correction, cd without the command, and the completion menu:
setopt AUTO_CD CORRECT
zstyle ':completion:*' menu select
Make zsh the default shell with chsh. The chsh manual allows only shells that /etc/shells lists, and on my machine that file lists zsh.
chsh -s "$(command -v zsh)"
Log out and log in again. New terminals then start zsh. To go back, run chsh -s /bin/bash.
Do you need Oh My Zsh?
Oh My Zsh is, in the words of its website, “an open source, community-driven framework for managing your Zsh configuration.” You do not need it, because zsh works with a short .zshrc.
I do not use it. A plugin manager called sheldon loads my plugins, and I take one file from the Oh My Zsh repository: the terraform plugin.
Sheldon loads these plugins for me:
- Powerlevel10k as the prompt theme
zsh-defer- `zsh-autosuggestions
zsh-autoswitch-virtualenvdocker-zsh-completion- and the Oh My Zsh
terraformfile
Apart from Powerlevel10k and zsh-defer itself, sheldon loads all of them through zsh-defer. The effect: it “defers execution of a zsh command until zsh has nothing else to do and is waiting for user input”. See zsh-defer README.
The start-time table above shows the cost. sheldon source with my plugins takes 21 ms. Oh My Zsh with only the git plugin takes 32 ms in the same test. For a first setup, Oh My Zsh saves time, because themes and plugins work without config.
Which shell should you use?
Use zsh as your interactive shell if you work in a terminal every day. Write scripts for bash if they must run on other machines. Stay on bash if you open a terminal a few times a month, because Ubuntu needs no setup for it. Run zsh in your current terminal to try it before you change the default shell.
- You work in a terminal every day: use zsh, and enable typo correction and shared history.
- You write scripts for servers that you do not control: write them for bash, and start them with
#!/usr/bin/env bash. - You open a terminal rarely: stay on bash.
Fish is a third option. Its documentation lists syntax highlighting, autosuggestions, and selection lists, and it states “No configuration needed”. Fish scripts differ from bash scripts, and the documentation links a page for bash users called “Fish For Bash Users”.
Fish 4.0.0, released on 27 February 2025, ported the core code from C++ to Rust (release notes).