Zsh Cheatsheet - .zshrc, Aliases & Completion

After switching the default shell, the speedup does not come from syntax — it comes from smarter completion, prefix-aware history search, and a prompt that doubles as a status display. The sheet is organised around the seven parts of .zshrc people keep editing: base options, variables and arrays, globbing and expansion, history, functions and completion, prompt customisation, and oh-my-zsh with plugins. A few gotchas are called out: zsh arrays index from 1 while bash arrays index from 0, which is the most common source of breakage when porting scripts. setopt AUTO_CD lets you hit enter on a directory name, and pairs with AUTO_PUSHD to turn cd - into a jump list. EXTENDED_GLOB is what makes ** genuinely recursive rather than one level deep. Binding the arrow keys to history-search-backward turns them from a time scrollbar into a prefix search over past commands. On oh-my-zsh, remember it adds measurable startup cost, so cherry-pick plugins on production boxes.

SysOps·37 commands·Last updated 2026-07-21
zshshellTerminaloh-my-zsh

Typical Use Case

For developers who have just moved their default shell to zsh: make completion, history search, aliases and the prompt actually do the work. What zsh adds over bash concentrates in three places — recursive globbing (**), copyable history search (Ctrl+R), and glob qualifiers such as *(om[1]) to pick the newest file. This sheet covers the high-traffic .zshrc options and oh-my-zsh plugin usage; best read once in the first week after switching, then revisit as needed.

Basic Setup 5

chsh -s $(which zsh)
Switch the default shell to zsh
source ~/.zshrc
Reload the config
echo $SHELL
Show the current shell
setopt AUTO_CD
Type a dir name to cd (no cd needed)
setopt CORRECT
Suggest corrections on typo

Variables & Arrays 6

NAME="value"
Define a variable (no spaces around =)
arr=(a b c)
Define an array (zsh indexes from 1)
echo $arr[1]
Access the first array element
echo ${arr[@]}
Access all array elements
${#arr[@]}
Get array length
typeset -A dict dict[key]=value
Define an associative array (hash)

Globbing & Expansion 6

**/*.js
Recursively match .js in subdirs
*.{js,ts}
Brace expansion, multiple suffixes
*(.)
Match regular files only (glob qualifier)
*(/)
Match directories only
*(om[1])
The most recently modified file
=ls
= expands to the command's full path

Functions & Completion 4

function name() { ... }
Define a function
alias ll="ls -la"
Set an alias
autoload -Uz compinit && compinit
Initialize the completion system
compdef _git g=git
Reuse git completion for alias g

Prompt Customization 5

PROMPT='%n@%m %~ %# '
user@host path prompt
%n / %m / %M
username / short host / full host
%~ / %d
current dir (~) / absolute path
RPROMPT="%T"
Right prompt shows current time
%T / %D / %W
24h time / date / month-day-year

oh-my-zsh & Plugins 5

plugins=(git zsh-autosuggestions zsh-syntax-highlighting)
Enable a plugin list
ZSH_THEME="robbyrussell"
Set a theme (random to rotate)
zsh-autosuggestions
Autosuggest completions (accept with ->)
zsh-syntax-highlighting
Syntax highlighting (red = error)
extract file.tar.gz
extract plugin auto-detects archive format

Parameter matrix

ParameterEffectExample
setopt AUTO_CDTyping a directory name enters it, no need to write cdsetopt AUTO_CD
setopt CORRECTPrompt to correct a misspelled commandsetopt CORRECT
HISTSIZE / SAVEHISTHistory counts for memory and file; both must be set to take effectHISTSIZE=10000; SAVEHIST=10000
setopt SHARE_HISTORYMultiple terminals share history in real timesetopt SHARE_HISTORY
Ctrl+RReverse incremental search of command historyType a git keyword after Ctrl+R
!!Run the previous command; sudo !! is the classic combosudo !!
!$The last argument of the previous commandmkdir -p /var/log/app; cd !$
**/*.jsRecursively match .js files at any levells **/*.js
*.{js,ts}Brace expansion, matching multiple extensions at oncels *.{js,ts}
*(om[1])glob qualifier: pick the most recently modified filels -t *(om[1])
compdef _git gReuse git completion for the alias gcompdef _git g=git
%~ / %n / %TPrompt placeholders: current directory / username / timePROMPT='%n@%m %~ %# '

Common pitfalls

SymptomAfter switching a script from bash to zsh, array lookups all come back empty and ${arr[0]} does not yield the first element.

Causezsh arrays are 1-indexed by default, whereas bash starts at 0.

FixUse ${arr[1]}, or run emulate sh / setopt KSH_ARRAYS to make the behavior closer to bash.

SymptomAfter defining an alias in .zshrc, continuing to use that alias on the same line has no effect.

CauseAn alias only takes effect for commands after it within the same read.

FixPut the alias on its own line, or switch to a function function name() { ... }.

SymptomAfter installing zsh-syntax-highlighting, syntax highlighting does not work or errors out.

CauseThe plugin load order is wrong; the highlighter must be loaded last in the plugins array.

FixWrite plugins=(git zsh-autosuggestions zsh-syntax-highlighting) and put the highlighter at the end.

SymptomAfter switching to zsh you cannot get an interactive shell and have to fall back to sh.

Cause~/.zshrc has a syntax error that breaks parsing at startup.

FixUse zsh -n ~/.zshrc for a syntax check (parse only, do not execute), locate the bad line, and comment it out first.

SymptomThe gray suggestion from zsh-autosuggestions is not accepted with the arrow keys.

CauseThe default accept key is → (right arrow); pressing End or space does nothing.

FixPress → to accept the whole suggestion; accepting only one word needs extra configuration.

SymptomA command found with Ctrl+R turns out to have wrong arguments, but only that one entry is in history.

CauseThe variants of that command were not saved in history.

FixIncrease the history size: HISTSIZE=10000 SAVEHIST=10000, and use history -i | tail to see timestamped history.

Troubleshooting

  1. 1Confirm the current shell and whether the config is in effect

    echo $SHELL && zsh -n ~/.zshrc

    The first shows the login shell; the second only does a syntax check (-n) and locates parse errors in .zshrc.

  2. 2Reload immediately after changing the config

    source ~/.zshrc

    Run it once after every edit of .zshrc; you can save effort with alias reload-cfg="source ~/.zshrc".

  3. 3Recursively find .js files at any level

    ls **/*.js

    ** is zsh recursion wildcard; bash needs globstar enabled first (shopt -s globstar).

  4. 4Get the most recently modified log file

    ls -t *(om[1])

    (om[1]) is a glob qualifier; o sorts by modification time and takes the first; bash needs find + printf.

  5. 5Search history for a command containing a keyword and run it

    git rebase

    Type the prefix then press Ctrl+R to search upward and Enter to run; with SHARE_HISTORY multiple terminals share in real time.

Command Examples

Recall a matching command from history

git rebase

Type git rebase in the terminal and press Ctrl+R to search upward for history containing that prefix; the arrow keys or Enter run it directly. With setopt SHARE_HISTORY, history is shared across terminals in real time.

Reuse the last argument of the previous command

mkdir -p /var/log/myapp cd !$

!$ expands to the last argument of the previous command, !^ is the first, !! is the whole command. sudo !! is the classic combo — run a dangerous command, then rerun it with the same arguments prefixed by sudo.

Recursively match files at any depth

ls **/*.ts ls *(om[1]).log

** recursively matches all levels (bash needs globstar enabled); *(om[1]) is a glob qualifier that takes the most recently modified .log file. bash needs find with -printf to do the same.

Output

src/index.ts
src/api/client.ts
src/api/auth/login.ts

Reload the config immediately

source ~/.zshrc

Run it once after every edit; writing alias reload-cfg="source ~/.zshrc" is more convenient. Note that an alias does not apply to its own line within the same file.

Common Pitfalls

  • zsh arrays are 1-indexed, so ${arr[0]} is empty — the most common porting trap from bash.
  • An alias only takes effect on subsequent lines; defining and using it on the same line does nothing.
  • Plugin order matters: zsh-syntax-highlighting must be loaded last in the plugins array.
  • ${arr[@]} expands differently in zsh than bash, so quote it explicitly in portable scripts.
  • Verify ~/.zshrc parses before switching your login shell — one syntax error can leave you without a working interactive prompt.

Tips

  • Install oh-my-zsh: sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
  • zsh-autosuggestions needs a separate install (git clone into $ZSH_CUSTOM/plugins).
  • setopt AUTO_CD lets you cd by typing a dir name.
  • Bind up/down arrows to prefix history search via bindkey.
  • zsh arrays start at 1 (bash starts at 0) - mind this when porting scripts.
  • ZDOTDIR changes the config dir, handy for isolated configs.

FAQ

Do zsh arrays start at index 0 or 1?

zsh indexes arrays from 1, while bash starts at 0. This is one of the most common porting traps: in zsh ${arr[0]} is an empty element, not the first one, and the correct form is ${arr[1]}. To make zsh behave more bash-like, use emulate sh or setopt KSH_ARRAYS.

Beyond Ctrl+R, what history tricks does zsh offer?

Ctrl+R does reverse incremental search, and pressing it again walks further back. Beyond that: !! repeats the previous command, making sudo !! the classic pairing; !$ expands to the last argument of the previous command, so after mkdir -p /var/log/app, cd !$ lands you there; !^ grabs the first argument. And setopt SHARE_HISTORY shares history live across terminal windows.

Where is zsh globbing stronger than bash?

Three main things. ** recurses into subdirectories, which bash needs globstar for. Brace expansion *.{js,ts} matches several extensions in one pattern instead of listing them. And glob qualifiers let you filter: *(.) regular files only, *(/) directories only, *(om[1]) the single most recently modified one. In bash you would need find for equivalent filtering.

How do I set up syntax highlighting and autosuggestions in zsh?

Two plugins do it: zsh-syntax-highlighting highlights command syntax live (invalid commands turn red) and zsh-autosuggestions shows a grey inline suggestion from your history that you accept with the right arrow. Add plugins=(git zsh-autosuggestions zsh-syntax-highlighting) to .zshrc and enable the completion system with compinit. Make sure the highlighter is loaded last in the plugins array.

What do %~, %n and %T mean in the zsh prompt?

%n is the username, %m the short hostname, %M the full hostname. %~ is the current path with the home directory abbreviated to ~, while %d prints the absolute path. %T is the time in 24-hour form, %D the year-month-day, %W the month-day-year. The classic PROMPT='%n@%m %~ %# ' shows user@host path plus a prompt char, where %# renders as $ for a normal user and # for root.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us