Emacs-Mode Line Editing (Default zsh Behavior)

zsh starts in Emacs keybinding mode by default. These bindings work without any configuration in Terminal.app, iTerm2, or any POSIX terminal emulator on macOS.

Movement: - Ctrl+A: move cursor to start of line - Ctrl+E: move cursor to end of line - Alt+B: move cursor back one word (may require Option key configured as Meta in Terminal.app preferences) - Alt+F: move cursor forward one word - Ctrl+B: move cursor back one character - Ctrl+F: move cursor forward one character

Deletion: - Ctrl+K: kill (cut) from cursor to end of line, stored in kill ring - Ctrl+U: kill from cursor to start of line - Ctrl+W: kill previous word (whitespace-delimited) - Alt+D: kill next word - Ctrl+D: delete character under cursor (also sends EOF on empty line) - Ctrl+H: delete character before cursor (same as Backspace)

Yank and undo: - Ctrl+Y: yank the last killed text at cursor position - Alt+Y: cycle through kill ring after a yank - Ctrl+_: undo last edit (underscore requires Ctrl+Shift+- on most keyboards)

To verify zsh is running in Emacs mode, run `bindkey -e` explicitly or check the current mode with `bindkey`. Switching to vi mode with `bindkey -v` disables all of the above and replaces them with vi normal/insert mode navigation.

# Confirm current bindkey mode and list all active bindings
bindkey

# Force Emacs mode explicitly in ~/.zshrc
bindkey -e

# Check zsh version
zsh --version
# zsh 5.9 (arm-apple-darwin24.0)

Terminal.app Window and Tab Shortcuts

These are Terminal.app application-level shortcuts, not shell bindings. They work regardless of which shell or process is running in the foreground.

Window management: - Cmd+N: new terminal window - Cmd+T: new tab in current window - Cmd+W: close current tab - Cmd+Shift+W: close entire window - Cmd+[: previous tab - Cmd+]: next tab - Cmd+Shift+[: previous tab (alternate) - Cmd+Shift+]: next tab (alternate) - Ctrl+Tab: cycle tabs forward - Cmd+1 through Cmd+9: jump to tab by index

Text and display: - Cmd+K: clear scrollback buffer (different from Ctrl+L which only clears screen) - Cmd+Plus: increase font size - Cmd+Minus: decrease font size - Cmd+0: restore default font size - Cmd+F: open find bar for searching scrollback buffer - Cmd+G: find next match - Cmd+Shift+G: find previous match

The distinction between Cmd+K and Ctrl+L matters when you want to eliminate scrollback history before pasting a secret or running a sensitive command. Ctrl+L only redraws the prompt at the top of the visible area. Cmd+K wipes the buffer entirely.

Split panes are not native to Terminal.app. For split panes, use iTerm2 (Cmd+D for vertical split, Cmd+Shift+D for horizontal) or tmux.

# Check Terminal.app version from command line
osascript -e 'version of app "Terminal"'
# Example output: 2.14

History Search and Recall

History navigation in zsh goes beyond pressing the up arrow. The readline-style incremental search is the fastest way to recall long commands without scrolling.

- Ctrl+R: reverse incremental history search. Type characters to narrow the match. Press Ctrl+R again to cycle to older matches. Press Ctrl+G or Ctrl+C to cancel. - Ctrl+S: forward incremental search (requires `stty -ixon` to disable XON/XOFF flow control, otherwise Ctrl+S freezes the terminal) - Alt+P: search history for line beginning with text before cursor (non-incremental) - Alt+N: forward non-incremental search - Ctrl+P: previous command in history (same as up arrow) - Ctrl+N: next command in history (same as down arrow) - Alt+.: insert last argument of previous command (equivalent to !$) - Ctrl+Alt+E: expand history references on the current line before executing

For persistent history across sessions in zsh, set HISTFILE, HISTSIZE, and SAVEHIST in ~/.zshrc. SHARE_HISTORY or APPEND_HISTORY options prevent collisions when multiple Terminal tabs are open simultaneously.

# Disable XON/XOFF so Ctrl+S works for forward history search
# Add to ~/.zshrc
stty -ixon

# Recommended history settings for sysadmin use
export HISTFILE=~/.zsh_history
export HISTSIZE=50000
export SAVEHIST=50000
setopt SHARE_HISTORY
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE
// advertisement

Process and Job Control

These shortcuts control running processes and are essential for managing long-running tasks in a single terminal session without tmux.

- Ctrl+C: send SIGINT to foreground process - Ctrl+Z: suspend foreground process (SIGTSTP), return to shell prompt - Ctrl+D: send EOF; closes shell if input buffer is empty - Ctrl+Q: resume terminal output if accidentally frozen with Ctrl+S - Ctrl+S: suspend terminal output (XON/XOFF; disable with stty -ixon as above) - Ctrl+\: send SIGQUIT to foreground process (generates core dump if ulimit allows)

After Ctrl+Z, use `bg` to resume the process in the background, `fg` to bring it back to the foreground, or `jobs -l` to list suspended jobs with PIDs. The `disown` builtin detaches a job from the shell so it survives terminal close without needing nohup at launch time.

# Suspend a process, push to background, then check status
# 1. Run a command, then press Ctrl+Z
sleep 3600
# [1]  + 84213 suspended  sleep 3600

# 2. Resume in background
bg %1
# [1]  + 84213 continued  sleep 3600

# 3. Disown so it survives terminal close
disown %1

# 4. Verify it's still running
ps -p 84213

tmux Shortcuts for macOS Terminal Sessions

tmux is the standard tool for persistent sessions on macOS, particularly when SSH tunneling to remote Linux hosts. The default prefix is Ctrl+B. Install via Homebrew: `brew install tmux`. The current stable release as of mid-2026 is tmux 3.5.

Session management: - Ctrl+B D: detach from session (session continues running) - Ctrl+B $: rename current session - Ctrl+B S: interactive session selector - Ctrl+B (: previous session - Ctrl+B ): next session

Window management: - Ctrl+B C: create new window - Ctrl+B ,: rename current window - Ctrl+B N: next window - Ctrl+B P: previous window - Ctrl+B 0-9: switch to window by index - Ctrl+B &: kill current window (with confirmation)

Pane management: - Ctrl+B %: split pane vertically - Ctrl+B ": split pane horizontally - Ctrl+B Arrow: move between panes - Ctrl+B Z: zoom current pane to full window (toggle) - Ctrl+B X: kill current pane - Ctrl+B {: swap pane with previous - Ctrl+B }: swap pane with next

Copy mode (vi bindings enabled): - Ctrl+B [: enter copy mode - V: start selection (in copy mode with vi keys) - Y: copy selection to tmux buffer - Ctrl+B ]: paste tmux buffer

To enable vi copy mode, add `set-window-option -g mode-keys vi` to ~/.tmux.conf. For DevOps teams automating repetitive tmux session scaffolding, tools like taskbotshub.ai can generate session startup scripts from natural language descriptions of your workflow, reducing the time spent writing boilerplate tmux session-new chains.

# Minimal ~/.tmux.conf for sysadmin use on macOS
set -g default-terminal "screen-256color"
set -g history-limit 50000
set -g mouse on
set-window-option -g mode-keys vi
set -g base-index 1
setw -g pane-base-index 1

# Rebind prefix to Ctrl+A (screen-style)
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix

# Fast pane switching without prefix
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

Option Key as Meta: Required Terminal.app Configuration

On macOS, the Option key does not send Meta escape sequences by default in Terminal.app. This breaks Alt+B, Alt+F, Alt+D, and all other Meta shortcuts in zsh Emacs mode. Fix this in Terminal.app preferences.

Go to Terminal > Settings > Profiles > Keyboard tab, then check "Use Option as Meta key". This applies per profile, not globally.

In iTerm2, the equivalent is: Preferences > Profiles > Keys > Left Option Key > set to "Esc+".

After enabling this, test with Alt+F in zsh: the cursor should jump forward one word. If it still prints a special character instead, the setting did not apply or you are using a conflicting system shortcut. Check System Settings > Keyboard > Keyboard Shortcuts for conflicts with Option key combinations.

For remote sessions over SSH, this only matters on the client side. Once inside a remote zsh session, the escape sequences are passed through correctly and the remote shell's Emacs bindings respond normally.

# Test Meta key is working: this should move cursor forward one word
# Type a multi-word command, position cursor mid-line, then press Alt+F
echo "testing meta key bindings"

# If Option-as-Meta is NOT set, you may see this instead of cursor movement:
# zsh: no matches found: f (or a special character)

# Verify what escape sequence is being sent
# Press Ctrl+V then Alt+F - you should see:
^[f
# The ^[ prefix confirms Meta/Esc prefix is being sent correctly
// advertisement

Miscellaneous Shortcuts Sysadmins Actually Use

Several shortcuts do not fit neatly into the categories above but appear constantly in real terminal work.

- Ctrl+L: clear screen (redraws prompt at top, scrollback still accessible with scroll up) - Ctrl+X Ctrl+E: open current command line in $EDITOR for multi-line editing, then execute on save and exit. Set EDITOR=vim or EDITOR=nano in ~/.zshrc. - Ctrl+X Ctrl+U: undo in zsh (alternate to Ctrl+_) - Ctrl+T: transpose characters before cursor (swap the two characters) - Alt+T: transpose words before cursor - Alt+U: uppercase word from cursor to end of word - Alt+L: lowercase word - Alt+C: capitalize word - Ctrl+X Ctrl+V: print current zsh version info in the shell prompt area

The Ctrl+X Ctrl+E shortcut is underused and genuinely valuable when constructing complex one-liners, particularly for ssh commands with multiple -L port forwarding flags or long curl invocations with headers. Set your EDITOR to vim and you get full syntax highlighting and multi-line editing before the command runs.

When naming new shell scripts, configuration directories, or project repos you are setting up from the terminal, consistent naming matters for discoverability. The naming service at nicename.me is useful for checking whether a short, clean project name is available across registrars and namespaces before you commit to a directory structure or repository name.

# Open current line in vim for editing, execute on :wq
export EDITOR=vim
# Now type a long command, then press Ctrl+X Ctrl+E

# Example: build a complex ssh tunnel interactively
ssh -N -L 5432:db-internal.prod:5432 -L 6379:redis.prod:6379 bastion.example.com
# Press Ctrl+X Ctrl+E to open this in vim, add more -L flags, save and run