Zsh

On this page 7

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.

FileRead byTypical content
~/.zshenvEvery Zsh instance, including scriptsMinimal environment variables only
~/.zprofileLogin shellsPATH changes, login-time setup
~/.zshrcInteractive shellsAliases, options, completion, prompt, plugins
~/.zloginLogin shells, after .zshrcCommands that need the full setup
~/.zlogoutLogin shells on exitCleanup tasks
Zsh startup files and their purpose

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.

FeatureExampleBenefit
Programmable completionTab completes Git branches or package namesContext-aware suggestions with menus
Recursive globbingls **/*.logMatches files in all subdirectories
Glob qualifiersls *(.m-1)Filters by type, size or modification time
Shared historysetopt SHARE_HISTORYCommands appear in all open terminals
Spelling correctionsetopt CORRECTSuggests fixes for mistyped commands
Right-side promptRPROMPT variableShows status such as Git branch or exit code
Characteristic Zsh features

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.

AspectZshBashPOSIX sh
Array indexingStarts at 1Starts at 0No arrays
Unquoted variable splittingNot split by defaultSplit on whitespaceSplit on whitespace
Unmatched globError by defaultPattern passed unchangedPattern passed unchanged
Interactive featuresVery richModerate, extended by readlineMinimal
Default onmacOSMost Linux distributionsDebian /bin/sh is Dash
Best useInteractive daily shellScripts and interactive useMaximally portable scripts
Zsh, Bash and POSIX sh compared

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/bash shebang should be executed by Bash, not sourced into Zsh.
  • Assuming zero-based arrays: Bash habits produce off-by-one errors unless the KSH_ARRAYS option is set.
  • Copying PATH edits into several files: duplicated entries grow with each nested shell; the typeset -U path command removes duplicates.
  • Forgetting to change the login shell: installing Zsh does not activate it; chsh -s with the path listed in /etc/shells does.

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.