History Search
| Command | What it does |
|---|---|
| Search history as you type (press again for older matches) | |
| Show the last 16 commands | |
| Show the whole history | |
| Search history for a command | |
| Rerun the last command ( reruns it as root) | |
| Last argument of the previous command | |
| All arguments of the previous command | |
| Rerun the most recent command starting with ssh | |
| Rerun history entry 42 | |
| Insert the previous command's last argument (repeat to go older) | |
| after typing | Only steps through matching commands if you bind prefix search (below) |
History size and behaviour go in :
HISTSIZE=50000
SAVEHIST=50000
setopt SHARE_HISTORY # share history across open terminals
setopt HIST_IGNORE_ALL_DUPS # keep one copy of a repeated command
setopt HIST_IGNORE_SPACE # commands starting with a space are not saved
# Up/Down search commands starting with what you typed
bindkey '^[[A' history-beginning-search-backward
bindkey '^[[B' history-beginning-search-forwardKeyboard Shortcuts
Emacs-style bindings, the default. See the next section for vi mode.
| Key | Action |
|---|---|
| / | Start / end of line |
| / | Forward / back one word |
| Delete the word before the cursor | |
| Delete the whole line | |
| Delete from cursor to end of line | |
| Paste what Ctrl+W/U/K deleted | |
| Clear the screen | |
| Cancel the current line or interrupt the running command | |
| Log out on an empty line; delete char otherwise | |
| Suspend the foreground job ( resumes) | |
| Complete; press again to cycle candidates | |
| Edit the current line in $EDITOR (after ) |
Aliases
| Command | What it does |
|---|---|
| Define an alias for this session | |
| List every defined alias | |
| Show one alias's definition | |
| Remove an alias | |
| Run the real command, ignoring the alias ( also works) | |
| Global alias, expands anywhere in the line: | |
| Suffix alias: typing opens it in less | |
| Typing opens it in VS Code |
Aliases live for one session unless you put them in :
echo "alias ll='ls -lah'" >> ~/.zshrc
source ~/.zshrcCompletion
zsh's completion system knows flags, hostnames, git branches, and PIDs, but only after you turn it on.
| Command | What it does |
|---|---|
| Enable completion (put in ) | |
| Complete; a second Tab lists candidates | |
| (with menu select) | Arrow-key menu over the candidates |
| Turn on the arrow-key menu | |
| Case-insensitive completion | |
| Rebuild the cache when completions go stale | |
| Fix the "compinit: insecure directories" warning |
Keybindings and Vi Mode
| Command | What it does |
|---|---|
| Emacs mode (the default) | |
| Vi mode: Esc for normal mode, back to insert | |
| List current bindings | |
| Restore Ctrl+R in vi mode (not bound there by default) | |
| Make Esc register instantly in vi mode (in ) | |
| See what a specific key runs |
In vi normal mode: / step through history, searches it, opens the line in your editor.
Globbing
zsh globbing does what you would otherwise need for. Recursive works out of the box.
| Pattern | What it matches |
|---|---|
| All .js files, recursively | |
| Regular files only | |
| Directories only | |
| Symlinks only | |
| The most recently modified file | |
| The five most recently modified | |
| Files larger than 10 MB | |
| All .txt except notes.txt (needs extendedglob) | |
| Everything except .md files (needs extendedglob) | |
| Files, and no error when nothing matches |
setopt extendedglob # enables ^negation and the ~exclusion operatorFor heavier searches (by mtime, with -exec), see the find cheat sheet.
Startup Files
Read in this order. macOS Terminal and iTerm open login shells, so and both run; most Linux terminals open non-login shells, so only runs.
| File | When it runs | Put here |
|---|---|---|
| Every zsh, even scripts | Almost nothing; keep it minimal | |
| Login shells, once | PATH exports, | |
| Every interactive shell | Aliases, functions, prompt, keybindings, completion | |
| Login shells, after zshrc | Rarely used | |
| When a login shell exits | Cleanup, rarely used |
After editing any of them: (or open a new tab).
Prompt
| Command | What it does |
|---|---|
| user@host, current directory, then % (# as root) | |
| Just the last path component | |
| Colour a segment ( on, off) | |
| Right-side prompt showing the time | |
| Enable the built-in theme system | |
| List built-in themes ( tries one) |
Common escapes: user, host, directory with , % or #, time, last exit code. Most people skip hand-rolling and install powerlevel10k or starship, which come with git status, exit codes, and language versions built in.
Oh My Zsh
A config framework: themes, completion tweaks, and plugins in one install. Everything lives in after it is set up.
| Command | What it does |
|---|---|
| Enable plugins (edit the line in ) | |
| Pick a theme | |
| Update Oh My Zsh | |
| List available plugins | |
| Apply config changes to the current session |
The git plugin is why most people install it. The aliases you will actually use:
| Alias | Expands to |
|---|---|
| / | / |
| current branch | |
| / | / |
Run to see the full list (150+).
Command Not Found Fixes
means the binary is not on your PATH or is not a binary at all. The pattern is the same for brew, pip, npm, nvm, docker, and code.
| Symptom | Fix |
|---|---|
| (Apple Silicon) | Add to , open a new tab |
| Any tool you know is installed | , then add its directory: in |
| Installed seconds ago, still not found | so zsh refreshes its command cache |
| nvm is a function; its init lines belong in , run the installer again or copy them over | |
| Worked in bash, gone in zsh | Move PATH exports from / into |
| In VS Code: Cmd+Shift+P, "Shell Command: Install 'code' command in PATH" |
One-Liners and Substitution
| Command | What it does |
|---|---|
| Run the second only if the first succeeds | |
| Run both regardless | |
| Run the second only if the first fails | |
| Capture a command's output in a variable | |
| Substitute output inline | |
| Arithmetic | |
| Loop over files on one line |
zsh vs bash
| Difference | zsh | bash |
|---|---|---|
| Unquoted with spaces | Stays one word | Splits into words |
| Array indexing | Starts at 1 () | Starts at 0 () |
| Recursive glob | Built in | Needs |
| Unmatched glob | Error: | Pattern passed through as-is |
| Config file | ||
| Prompt variable | with escapes | with escapes |
| Spelling correction, suffix aliases, glob qualifiers | Yes | No |
Scripts are unaffected: a shebang runs the script in bash whatever your login shell is. Write new scripts for the shell you name in the shebang.
Gotchas
- when a URL contains or : zsh globs the argument. Quote it () or .
- comments do not work at the interactive prompt by default: (Oh My Zsh turns this on for you).
- Pasted multi-line snippets run line by line as they land. Paste into (editor) if you want to review first.
- A new PATH entry in only applies to new shells; fixes the current one.
- zsh writes each terminal's history at exit by default; if you want tabs to see each other's commands live.
- Long remote sessions die with the connection. Run zsh inside tmux on servers so your shell survives a dropped SSH connection.