/ Tags: DEVELOPER TIPS / Categories: TIPS

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 sudoCtrl-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 fzf and bind it over Ctrl-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. fzf turns 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 you Ctrl-t for fuzzy file insertion into the current command and Alt-c for 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.

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More