Terminal Shortcuts Every Developer Should Know
You mistype a flag near the start of a long command and hold the left arrow for four seconds to get there. You retype a path you used two commands ago. You press up thirty times looking for something. All three have a two-keystroke fix that’s been in your shell since 1985, because the line editor your terminal uses is readline, and almost nobody learns it.
Moving Around the Line
| Keys | Action |
|---|---|
Ctrl-a |
Jump to start of line |
Ctrl-e |
Jump to end of line |
Alt-b |
Back one word |
Alt-f |
Forward one word |
Ctrl-xx |
Toggle between current position and start of line |
Ctrl-a and Ctrl-e are the two that pay off immediately. You typed a long docker run and need to prefix it with sudo — Ctrl-a, type, done.
On macOS, Alt-b and Alt-f need “Use Option as Meta key” enabled in your terminal’s settings. Turn it on; word-wise movement is where most of the value is.
Editing Without Backspace
| Keys | Action |
|---|---|
Ctrl-w |
Delete the word before the cursor |
Alt-d |
Delete the word after the cursor |
Ctrl-u |
Delete from cursor to start of line |
Ctrl-k |
Delete from cursor to end of line |
Ctrl-y |
Paste back what you last deleted |
Alt-Backspace |
Delete previous word (stops at punctuation, unlike Ctrl-w) |
Ctrl-u is the one to build a reflex around. Mistyped command, half-written and wrong — Ctrl-u clears the line instantly. Much better than holding backspace or Ctrl-c, and unlike Ctrl-c it saves what you deleted so Ctrl-y brings it back.
That save-and-restore behaviour enables a genuinely useful move:
# You're typing a long command and realise you need to check something first
$ bundle exec rails runner 'Order.where(...).count' ← half typed
Ctrl-u (line clears, text saved)
$ ls db/migrate | tail -3 (do the other thing)
Ctrl-y (your command is back)
Ctrl-w deletes by whitespace, Alt-Backspace deletes by word boundary. On a path like app/services/billing/invoice.rb, Ctrl-w kills the whole thing and Alt-Backspace kills just rb. Knowing which you want saves retyping.
Searching History
This is the section that changes how fast you work.
| Keys | Action |
|---|---|
Ctrl-r |
Reverse search through history |
Ctrl-r again |
Next older match |
Ctrl-s |
Search forward (needs stty -ixon) |
Ctrl-g |
Abort the search, restore the line |
Alt-. |
Insert the last argument of the previous command |
Ctrl-r replaces pressing up thirty times. Type any fragment of a previous command and it finds it.
$ Ctrl-r
(reverse-i-search)`migra': bin/rails db:migrate:status
Press Ctrl-r again to cycle further back. Enter runs it, → or Ctrl-e puts it on the line for editing without running.
Ctrl-s needs one line of config because terminals historically used it for flow control:
# ~/.zshrc or ~/.bashrc
stty -ixon
Alt-. is the sleeper hit. It inserts the last argument of the previous command:
$ mkdir -p app/services/billing
$ cd <Alt-.> → cd app/services/billing
Press it repeatedly to cycle through the last arguments of successively older commands. Once this is in your fingers you stop retyping paths entirely.
History Expansion
| Syntax | Meaning |
|---|---|
!! |
The previous command |
!$ |
Last argument of the previous command |
!^ |
First argument of the previous command |
!* |
All arguments of the previous command |
!rails |
Most recent command starting with “rails” |
!?spec? |
Most recent command containing “spec” |
^old^new |
Rerun previous command with the first “old” replaced by “new” |
sudo !! is the famous one — you ran something that needed root, and this reruns it with sudo.
^old^new is the underused one:
$ bundle exec rspec spec/models/oder_spec.rb
No such file
$ ^oder^order
bundle exec rspec spec/models/order_spec.rb
Typo corrected without retyping the line. For multiple occurrences, !!:gs/old/new/.
A safety note: history expansion happens before the command runs and doesn’t prompt. sudo !! when the previous command was destructive does exactly what you’d fear. Add shopt -s histverify (bash) so expansions are placed on the line for confirmation rather than executed immediately.
Process Control
| Keys | Action |
|---|---|
Ctrl-c |
Interrupt (SIGINT) |
Ctrl-z |
Suspend to background |
fg |
Resume in foreground |
bg |
Resume in background |
Ctrl-d |
EOF — exits the shell or ends stdin |
Ctrl-l |
Clear the screen (keeps scrollback) |
Ctrl-z then fg is the workflow people miss. You’re in a Rails console and need to run one git command — Ctrl-z, run it, fg, and you’re back in the console with all your state intact. No need to exit and rebuild the object graph you’d been assembling.
jobs lists what’s suspended; fg %2 resumes the second one.
Making History Worth Searching
Ctrl-r is only as good as the history behind it, and the defaults are poor.
Example:
# ~/.zshrc
HISTSIZE=100000
SAVEHIST=100000
HISTFILE=~/.zsh_history
setopt HIST_IGNORE_ALL_DUPS # keep only the most recent copy of a command
setopt HIST_IGNORE_SPACE # commands starting with a space aren't recorded
setopt HIST_REDUCE_BLANKS
setopt SHARE_HISTORY # share across concurrent sessions
setopt EXTENDED_HISTORY # record timestamps
setopt INC_APPEND_HISTORY # write immediately, not on exit
# ~/.bashrc equivalent
HISTSIZE=100000
HISTFILESIZE=100000
HISTCONTROL=ignoreboth:erasedups
shopt -s histappend
PROMPT_COMMAND="history -a; history -n"
HIST_IGNORE_SPACE / ignorespace is the security-relevant one — prefix a command with a space and it isn’t written to history. Use it for anything containing a token, and rotate the token anyway.
SHARE_HISTORY / the PROMPT_COMMAND line means a command run in one tab is immediately searchable in another. Without it, history only writes on shell exit and you lose everything from a terminal you closed by mistake.
Pro-Tip: Install
fzfand bind it overCtrl-r. Native reverse search is sequential — you type a fragment and cycle through matches one at a time, with no view of what else matched.fzfturns the same keystroke into a fuzzy-searchable, scrollable list of your entire history with a preview, so you see all forty matching commands and pick the right one. It also gives youCtrl-tfor fuzzy file insertion into the current command andAlt-cfor fuzzy directory navigation. Two lines in your shell config (source <(fzf --zsh)on modern versions) and it’s the largest single improvement available to a terminal workflow — more than any alias file, because it makes the ten thousand commands you’ve already typed actually retrievable.
A Few That Save Real Time
cd - # back to the previous directory
!$ # last argument, as a literal
Alt-# # comment out the current line and push it to history
Ctrl-x Ctrl-e # open the current command in $EDITOR
Ctrl-x Ctrl-e is the escape hatch for a command that’s grown too complex for a single line — it opens in vim or whatever $EDITOR is set to, and running it saves and executes.
Alt-# prefixes the line with # and submits it, which stores it in history without running. Useful when you’ve composed something dangerous and want to look at it again before committing to it.
Conclusion
Four shortcuts cover most of the value: Ctrl-a/Ctrl-e for line ends, Ctrl-u to clear and Ctrl-y to restore, Ctrl-r to search history instead of pressing up, and Alt-. to reuse the last argument. Add Ctrl-z/fg so you never exit a console to run one git command. Then fix your history config — large size, dedup, shared across sessions, timestamped, and ignorespace for anything with a secret in it. Install fzf over Ctrl-r and the history you’ve been accumulating for years becomes something you can actually use.
FAQs
Q1: Do these work in zsh, bash, and fish?
Bash and zsh both use readline-style bindings by default, so nearly all of them work in both. Fish has its own line editor with different defaults, though most of the Ctrl- bindings match.
Q2: Why doesn’t Alt-b work on macOS?
Terminal.app and iTerm2 send Option as an accent modifier by default. Enable “Use Option as Meta key” in the profile settings and word movement starts working.
Q3: Can I use vi keybindings instead?
Yes — set -o vi in bash, bindkey -v in zsh. Worth it if you’re fluent in vim, but the emacs defaults are what’s available on every machine you’ll SSH into.
Q4: How do I see what a key is bound to?
bindkey in zsh lists everything; bind -P in bash. Grep the output for the sequence you’re curious about.
Q5: Is there a downside to a very large history?
Not really — the file is plain text and a hundred thousand lines is a few megabytes. Just be aware it’s unencrypted on disk, which is another reason to use ignorespace for anything sensitive.
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀