cheat-sheets

Zsh

Zsh is a Bourne-style shell with powerful globbing, parameter expansion and completion, and is the default login shell on macOS. This sheet covers what differs from Bash; most Bash syntax (if, for, $(...), redirection, here-docs) works unchanged. Pick between shells with Shell Comparison.

Documentation: Zsh Manual Project: Zsh

Checked against Zsh 5.9. Examples were run unless marked (unverified).

Startup Files #

FILEREAD BY
~/.zshenvEvery zsh, including scripts (keep it tiny: environment only)
~/.zprofileLogin shells, before .zshrc
~/.zshrcInteractive shells (aliases, prompt, completion, key bindings)
~/.zloginLogin shells, after .zshrc
~/.zlogoutLogin shells on exit
ZDOTDIRDirectory zsh looks in for these files instead of $HOME (set it in /etc/zshenv or ~/.zshenv)

zsh -f starts without any startup files, which is handy for testing. #!/usr/bin/env zsh is the shebang for scripts.

Arrays and Associative Arrays #

Zsh arrays start at 1 and $a expands to all elements without needing [@].

COMMANDDESCRIPTION
a=(x y z)Create an array
$a[1], $a[-1]First and last element (1-based)
$a[2,3]Slice, inclusive of both ends
${#a}Number of elements
${a[(i)y]}Index of the first element equal to y
${a[(r)y]}The first element matching y
a+=(w)Append
${a:#y}Remove elements matching pattern y
${(o)a}, ${(O)a}Sorted ascending, descending
${(u)a}Unique elements
typeset -U pathKeep path free of duplicates (path is the array form of PATH)
typeset -A m; m=(k v k2 v2)Associative array from key/value pairs
${(k)m}, ${(v)m}, ${(kv)m}Keys, values, both
${m[k]}Look up a key

Parameter Expansion Flags #

Flags go in parentheses straight after ${.

FLAGDESCRIPTION
${(U)s}, ${(L)s}Upper / lower case
${s:u}, ${s:l}Same, modifier form
${(j:,:)a}Join array with ,
${(s:,:)s}Split string on , into an array
${(f)"$(cmd)"}Split command output into lines (an array)
${(q)s}Quote for reuse in the shell
${(l:8::0:)n}, ${(r:8::.:)n}Left / right pad to width 8 with 0 / .
${(t)v}Type of a variable (scalar, array, association)
${s:0:2}Substring (offset, length)
${s[1,3]}, ${s[-2,-1]}Substring by position, negative counts from the end
${s/l/L}, ${s//l/L}Replace first / all (same as Bash)

History-style modifiers work on variables too, which replaces dirname, basename and realpath for most uses:

COMMANDDESCRIPTION
${x:h}Head (dirname)
${x:t}Tail (basename)
${x:r}Remove the last extension
${x:e}Only the extension
${x:A}Absolute path with symlinks resolved

Globbing #

Needs setopt extended_glob for the ^, ~ and (#i) forms. Unlike Bash, a pattern that matches nothing is an error (no matches found), not the literal text; add (N) to get an empty result instead.

PATTERNMATCHES
**/*.mdRecursive, no globstar option needed
*(.), *(/)Regular files only, directories only
*(@), *(x)Symlinks, executables
*(.om[1])Newest regular file (om = sort by modification time, [1] = first)
*(.on[1])First regular file by name
*(.L0)Empty files (L is size; L+1 is larger than 1 byte)
*(N)Null glob: no error when nothing matches
^*.txtEverything except *.txt
*~*.log* except names matching *.log
(#i)readme*Case-insensitive match

zmv #

zmv renames with patterns. Load it first with autoload -Uz zmv.

COMMANDDESCRIPTION
zmv -n '(*).md' '$1.txt'Dry run: print the mv commands only
zmv '(*).md' '$1.txt'Rename every .md to .txt (renames files in place)
zmv -W '*.md' '*.txt'Same with wildcards on both sides

Options #

Set with setopt NAME, unset with unsetopt NAME (case and underscores are ignored). setopt alone lists the options that are on.

OPTIONEFFECT
autocdType a directory name to cd into it
auto_pushd, pushd_ignore_dupsMake cd push onto the directory stack, without duplicates
cdable_varscd NAME treats NAME as a variable holding a path
extended_globEnable the extra glob operators above
glob_dotsGlobs match dotfiles
nullglobUnmatched globs expand to nothing (global version of (N))
noclobberRefuse > over existing files
correctOffer spelling corrections for commands
interactive_commentsAllow # comments at the prompt
prompt_substExpand $vars and $(...) in the prompt string
hist_ignore_all_dupsDrop older duplicates from history
hist_ignore_spaceDo not record commands that start with a space
share_historyShare history between running shells
inc_append_historyWrite each command to the history file as it runs
hist_verifyShow a history expansion (!!) for editing instead of running it

History #

zsh
HISTFILE=~/.zsh_history
HISTSIZE=50000      # lines kept in memory
SAVEHIST=50000      # lines saved to HISTFILE
setopt share_history hist_ignore_all_dups hist_ignore_space

Both HISTSIZE and SAVEHIST matter: zsh starts with SAVEHIST=0, so history is not written to disk until you set it (the file is also only written if HISTFILE is set).

COMMANDDESCRIPTION
!!Previous command
!$Last argument of the previous command
fc -l 1List history from entry 1
history 1List the full history (the number is the first entry shown)
Ctrl+RIncremental search backwards

Completion #

zsh
autoload -Uz compinit && compinit
zstyle ':completion:*' menu select

compinit is required for completion to work. Tools usually print their own completions, e.g. source <(kubectl completion zsh); see Kubectl.

COMMANDDESCRIPTION
zstyle ':completion:*' menu selectArrow-key selectable completion menu
zstyle -LList the zstyles set in this shell
compinit -uSkip the insecure-directory check (use only when you know why it complains)
fpathArray of directories searched for completion functions and autoloaded functions

Prompt #

print -P expands prompt escapes, which is the quickest way to try a prompt.

ESCAPEEXPANDS TO
%n, %mUser name, host name
%~Current directory, ~ for home
%#% for a normal user, # for root
%TTime, 24-hour HH:MM
%F{red}…%fStart / end of foreground colour
%?Exit code of the last command
zsh
setopt prompt_subst
PROMPT='%F{green}%n@%m%f %F{blue}%~%f %# '

For a ready-made cross-shell prompt see Starship Prompt.

Key Bindings #

COMMANDDESCRIPTION
bindkey -eEmacs key bindings (the default for most setups)
bindkey -vVi key bindings
bindkey '^R' history-incremental-search-backwardBind a key to a widget
bindkeyList all bindings
autoload -Uz add-zsh-hookLoad the hook helper
add-zsh-hook precmd FUNCTIONRun FUNCTION before each prompt (also preexec, chpwd)

Aliases #

COMMANDDESCRIPTION
alias ll='ls -l'Normal alias (command position only)
alias -g L='| less'Global alias, expands anywhere on the line
alias -s md=catSuffix alias: typing file.md runs cat file.md
whence -w NAMEWhat NAME is (alias, builtin, command, function)

Differences from Bash #

ZSHBASH
Arrays are 1-basedArrays are 0-based
$a is the whole array$a is only the first element; use "${a[@]}"
Unquoted $var is not word-splitUnquoted $var is split and globbed
A non-matching glob is an errorA non-matching glob stays literal
print is the preferred builtinecho and printf
setopt / unsetoptshopt and set -o
$path and $fpath arrays tied to PATH and FPATHColon-separated strings only
$pipestatus$PIPESTATUS
zsh -fbash --noprofile --norc

Because of the splitting difference, a script written for Bash does not behave the same under Zsh without care. Use #!/usr/bin/env bash for portable scripts and lint them with ShellCheck and shfmt.

See Also #

Search all sheets

Tip: / or Ctrl/Cmd+K to open, Esc to close.