Zsh is an interactive shell and scripting language. Much of what sets it apart is opt-in: options you turn on, glob qualifiers you learn, and a few builtins that bash doesn’t have. This sheet covers where config goes, a starter set of options, history shortcuts, globbing, expansion modifiers, and the differences from bash that catch people out. Everything below is from the official Zsh manual, version 5.9.2. We attempt to test on all versions.
what it is and when to use it
Use zsh as your login shell if you live in a terminal and want stronger globbing, history, and completion than a plain POSIX shell. Scripts meant to run anywhere should still say #!/bin/sh and avoid zsh-only syntax. Zsh’s default mode is not POSIX compatible.
check the version
| Command | What it does |
|---|---|
zsh --version | From any shell |
echo $ZSH_VERSION | From inside zsh |
zsh -f | Start a zsh that skips your startup files, to test whether your config causes a problem |
startup files
zsh reads these in order. Each user file lives in $ZDOTDIR, or in your home directory if ZDOTDIR is unset.
| File | What it does |
|---|---|
.zshenv | Every zsh, including scripts. Keep it small. |
.zprofile | Login shells only. |
.zshrc | Interactive shells. Aliases, options, prompt, and key bindings go here. |
.zlogin | Login shells, after .zshrc. |
.zlogout | When a login shell exits. |
The system files in /etc (/etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin, /etc/zlogout) run before or after each user file. /etc/zshenv always runs first. On some systems those files live in a different directory.
| Command | What it does |
|---|---|
source ~/.zshrc | Reread .zshrc in the current shell |
exec zsh | Replace the current shell with a fresh one |
options
Option names ignore case and underscores, so AUTO_CD, autocd, and auto_cd are the same option. Put no in front to turn one off: setopt no_beep is the same as unsetopt beep.
| Command | What it does |
|---|---|
setopt | List the options that are on |
unsetopt | List the options that are off |
Common choices for ~/.zshrc:
setopt auto_cd setopt auto_pushd setopt pushd_ignore_dups setopt extended_glob setopt hist_ignore_dups setopt hist_ignore_space setopt interactive_comments
| Option | What it does |
|---|---|
auto_cd | Type a directory name on its own to cd into it. |
auto_pushd | Every cd pushes the old directory onto the stack (see dirs -v below). |
pushd_ignore_dups | Don’t push the same directory twice. |
extended_glob | Turn on the ^, ~, and # glob operators. |
hist_ignore_dups | Don’t save a command that repeats the previous one. |
hist_ignore_space | Don’t save a command that starts with a space. Use it for one-off commands with secrets in them. |
interactive_comments | Allow # comments at the prompt. |
history
History needs three variables. If HISTFILE is unset, history is not saved at all.
HISTFILE=~/.zsh_history HISTSIZE=50000 SAVEHIST=50000
| Setting | What it does |
|---|---|
HISTSIZE | How many events the shell keeps in memory. |
SAVEHIST | How many events are saved to the file. |
share_history | Each shell writes commands to the file as you run them and reads in commands from other shells. |
inc_append_history | Write each command to the file as you run it, without importing other shells' commands. |
The manual says share_history, inc_append_history, and inc_append_history_time should be treated as mutually exclusive. Pick one.
| Command | What it does |
|---|---|
history -10 | The last 10 history entries |
History expansion at the prompt:
| Shortcut | What it does |
|---|---|
!! | The previous command. sudo !! reruns it with sudo. |
!$ | The last argument of the previous command. |
!* | All arguments of the previous command. |
!-2 | The command before the previous one. |
!git | The most recent command that starts with git. |
^foo^bar | Rerun the previous command with the first foo replaced by bar. |
Ctrl-R | Search history incrementally, in the emacs keymap (the default unless EDITOR or VISUAL contains vi). |
globbing
| Command | What it does |
|---|---|
print -l **/*.md | Every .md file in this directory and all subdirectories |
Glob qualifiers go in parentheses at the end of a pattern:
| Pattern | What it does |
|---|---|
*(.) | Plain files only. |
*(/) | Directories only. |
*(@) | Symbolic links only. |
*(N) | Expand to nothing instead of failing when there are no matches. |
*(D) | Include dotfiles. |
*(om[1,3]) | The three most recently modified. |
*(Lm+100) | Larger than 100 megabytes. |
*(mh-1) | Modified within the last hour. mw-1 is within the last week. |
| Command | What it does |
|---|---|
ls -l *.log(om[1,3]) | The three newest log files |
print -l **/*(.Lm+100) | Files over 100 MB anywhere under the current directory |
With extended_glob set:
| Pattern | What it does |
|---|---|
^*.md | Everything except .md files. |
*.txt~notes.txt | Every .txt file except notes.txt. |
Without it:
| Pattern | What it does |
|---|---|
(foo|bar).txt | foo.txt or bar.txt. |
log<1-10>.txt | log1.txt through log10.txt. |
expansion and modifiers
Modifiers work on variables, on history references, and on glob results.
f=/var/log/nginx/access.log.gz
echo ${f:h}
echo ${f:t}
echo ${f:r}
echo ${f:e}
/var/log/nginx access.log.gz /var/log/nginx/access.log gz
| Modifier | What it does |
|---|---|
:h | Head, the directory part. |
:t | Tail, the file name. |
:r | Root, without the last extension. |
:e | The last extension only. |
:a | An absolute path, worked out without touching the filesystem. |
:A | Like :a, then resolves symbolic links. |
| Command | What it does |
|---|---|
name=sysop
echo ${(U)name} ${(L)name} ${#name} ${missing:-fallback} | Upper case, lower case, length, and a default value |
arr=(alpha beta gamma)
echo $arr[1] ${#arr}output alpha 3 | Arrays start at 1, not 0 |
directories
| Command | What it does |
|---|---|
dirs -v | Numbered directory stack (fills up when auto_pushd is on) |
cd ~2 | Jump to entry 2 on the stack |
cd - | Go back to the previous directory |
cd 2025 2026 | Two-argument cd: swap one string in the current path for another |
The second form turns /srv/reports/2025/q4 into /srv/reports/2026/q4 and changes to it.
builtins worth knowing
| Command | What it does |
|---|---|
autoload -Uz zmv | Bulk-rename with a pattern: load zmv first |
zmv -n '(*).txt' '$1.md' | Preview the rename with -n |
zmv '(*).txt' '$1.md' | Then run it without -n |
autoload -Uz compinit && compinit | Turn on the completion system |
bindkey -e | Emacs keys on the command line |
bindkey -v | Vi keys on the command line |
whence -v ls | How a command name would be interpreted: alias, function, builtin, or file |
alias -g G='| grep' | Global alias: expands anywhere on the line, not just as the command |
alias -s md=glow | Suffix alias: typing a file name ending in .md opens it with glow |
rehash | Forget remembered command locations after installing something new |
print -l $path | Print each argument on its own line: here, each directory in PATH |
gotchas
- Unquoted
$varis not split on spaces the way it is in bash. That only happens with thesh_word_splitoption set. Arrays are the zsh way to hold a list. - Arrays are 1-indexed unless
ksh_arraysis set. - A glob with no matches is an error (
no matches found), not passed through as literal text. This is thenomatchoption, on by default. Add(N)to a pattern that may match nothing. Quote URLs and other arguments that contain?,*, or[. .zshenvruns for every zsh, including scripts andzsh -c. Anything that prints output or is slow belongs in.zshrc.zmvstops without doing anything if two files would end up with the same name, or if a target already exists and you didn’t give-f. Always runzmv -nfirst anyway.alias -gandalias -sare powerful and easy to forget. A global alias namedGchanges any command line that hasGas a separate word.
related on terminal-life
sources
Checked against the Zsh manual, version 5.9.2 (released July 12, 2026), on zsh.sourceforge.io. Older versions: man zshall on your own machine.
- Manual index: https://zsh.sourceforge.io/Doc/Release/
- Invocation: https://zsh.sourceforge.io/Doc/Release/Invocation.html
- Startup files: https://zsh.sourceforge.io/Doc/Release/Files.html
- Options: https://zsh.sourceforge.io/Doc/Release/Options.html
- Parameters: https://zsh.sourceforge.io/Doc/Release/Parameters.html
- Expansion (history, parameters, globbing): https://zsh.sourceforge.io/Doc/Release/Expansion.html
- Builtins: https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html
- Line editor (bindkey and widgets): https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html
- Completion system (compinit): https://zsh.sourceforge.io/Doc/Release/Completion-System.html
- User contributions (zmv): https://zsh.sourceforge.io/Doc/Release/User-Contributions.html
terminal-life.com/cheatsheets/zsh/ · checked against the Zsh 5.9.2 manual