Zsh, the Z shell, is a Unix command-line interpreter that reads commands typed in a terminal or written in shell scripts and executes them, much like Bash. It is largely compatible with the Bourne shell family while adding programmable completion, extended globbing, spelling correction, shared history and highly customizable prompts.
In the Linux shell and terminal topic, Zsh is the main interactive alternative to Bash. Paul Falstad wrote the first version in 1990, and it is distributed under a permissive MIT-style license. It has been the default login shell on macOS since 2019, and many Linux users install it for daily interactive work while keeping Bash or POSIX sh for portable scripts.
How Zsh starts and loads its configuration
When Zsh starts, it reads a fixed sequence of startup files depending on whether the shell is a login shell and whether it is interactive. Understanding this order prevents the most common configuration confusion, such as an environment variable missing in one terminal but present in another.
The table lists the user-level files in the order Zsh reads them; system-wide equivalents usually live under /etc or /etc/zsh.
| File | Read by | Typical content |
|---|---|---|
~/.zshenv | Every Zsh instance, including scripts | Minimal environment variables only |
~/.zprofile | Login shells | PATH changes, login-time setup |
~/.zshrc | Interactive shells | Aliases, options, completion, prompt, plugins |
~/.zlogin | Login shells, after .zshrc | Commands that need the full setup |
~/.zlogout | Login shells on exit | Cleanup tasks |
The location of these files can be moved with the ZDOTDIR environment variable, which many users point to a directory such as ~/.config/zsh to keep the home directory tidy.
Features that set Zsh apart
Zsh behavior is controlled by shell options set with setopt and unsetopt, and by loadable modules. Several capabilities that define the Zsh experience are listed below.
| Feature | Example | Benefit |
|---|---|---|
| Programmable completion | Tab completes Git branches or package names | Context-aware suggestions with menus |
| Recursive globbing | ls **/*.log | Matches files in all subdirectories |
| Glob qualifiers | ls *(.m-1) | Filters by type, size or modification time |
| Shared history | setopt SHARE_HISTORY | Commands appear in all open terminals |
| Spelling correction | setopt CORRECT | Suggests fixes for mistyped commands |
| Right-side prompt | RPROMPT variable | Shows status such as Git branch or exit code |
Completion is enabled with autoload -Uz compinit followed by compinit. The completion system ships definitions for thousands of commands, and it caches them in a dump file to keep startup fast.
Zsh compared with Bash and other shells
Zsh and Bash share most syntax for variables, loops, functions and pipelines, so simple scripts often run unchanged. Differences appear in details that can silently change script behavior.
| Aspect | Zsh | Bash | POSIX sh |
|---|---|---|---|
| Array indexing | Starts at 1 | Starts at 0 | No arrays |
| Unquoted variable splitting | Not split by default | Split on whitespace | Split on whitespace |
| Unmatched glob | Error by default | Pattern passed unchanged | Pattern passed unchanged |
| Interactive features | Very rich | Moderate, extended by readline | Minimal |
| Default on | macOS | Most Linux distributions | Debian /bin/sh is Dash |
| Best use | Interactive daily shell | Scripts and interactive use | Maximally portable scripts |
Fish is another popular interactive shell, but unlike Zsh it is deliberately not POSIX compatible, so existing Bash snippets often need rewriting. Zsh offers a middle path: rich interactivity with familiar syntax.
Frameworks, plugins and performance
Much of the Zsh ecosystem consists of frameworks and plugin managers such as Oh My Zsh, Prezto, zinit and antidote, together with prompt themes such as Powerlevel10k and Starship. Popular plugins add autosuggestions from history and syntax highlighting of the command line as it is typed.
Each plugin adds startup work. A heavily loaded configuration can take several hundred milliseconds to open a new prompt, which becomes noticeable in tmux sessions and editors that spawn shells often. The built-in profiler, enabled with zmodload zsh/zprof, shows which functions consume startup time; lazy loading and a smaller plugin set usually restore startup to well under 100 ms.
Common mistakes when adopting Zsh
- Putting everything in `.zshenv`: it runs for every script, so heavy setup slows all Zsh invocations and can break non-interactive tools.
- Running Bash scripts with Zsh: a script with a
#!/bin/bashshebang should be executed by Bash, not sourced into Zsh. - Assuming zero-based arrays: Bash habits produce off-by-one errors unless the
KSH_ARRAYSoption is set. - Copying PATH edits into several files: duplicated entries grow with each nested shell; the
typeset -U pathcommand removes duplicates. - Forgetting to change the login shell: installing Zsh does not activate it;
chsh -swith the path listed in/etc/shellsdoes.
Understanding it simply
Zsh is a program that listens to typed commands and carries them out, the same job Bash does on most Linux systems. It is the text-based way of telling a computer what to do.
Its strength is comfort. Zsh guesses what is being typed, completes long names with one key, fixes small typing errors and remembers earlier commands across windows. It is like a skilled assistant who already knows which folder or file is meant before the sentence is finished.
Applied in practice
A DevOps engineer spends most of the day in terminals connected to Kubernetes clusters and Git repositories. After switching the interactive shell from Bash to Zsh with chsh -s /usr/bin/zsh, the engineer enables completion, shared history and a prompt that shows the current Git branch and Kubernetes context on the right side.
Completion for kubectl and git now offers pod names and branches after a Tab press, and recursive globbing replaces several find commands. When new terminals start to feel sluggish, zprof reveals that one plugin takes 400 ms; replacing it with a lazily loaded alternative brings startup back to about 60 ms. Deployment scripts stay unchanged, because they keep their #!/bin/bash shebang and still run under Bash.