Advanced Keyboard Tricks

4 min read

Core idea

The shell is a keyboard instrument

Bash uses a library called Readline to handle every keystroke on the command line. Arrow keys, backspace, and Enter are the obvious entry points — but Readline also exposes dozens of editing primitives that let you move by word, kill to end of line, transpose characters, search history incrementally, and complete partial names. Skilled command-line users barely touch the arrow keys; they fly across the line with Ctrl- and Alt-prefixed combinations that the mouse simply cannot match for speed.

Three pillars: editing, completion, history

Three families of shortcuts cover almost every keyboard situation. Editing is the in-line manipulation of the current command (Ctrl-A, Ctrl-E, Ctrl-K, Alt-D, transpose, case-flip). Completion is bash inferring what you mean from a fragment plus context (Tab on a partial path, command, variable, or hostname). History is the searchable record of everything you have run (up-arrow, Ctrl-R, !$, !88, history | grep). Each pillar reduces typing for a different reason: editing reduces motion, completion reduces certainty, history reduces re-derivation.

Why it matters

Compound speedups, not single tricks

Each Readline shortcut alone saves a small amount of time. But shell work is repetitive — the same commands, the same paths, the same option flags appear hundreds of times a day. The savings compound. A user fluent in Ctrl-R and brace expansion can rerun yesterday's complex incantation in two keystrokes; a user without those tools retypes it letter by letter or copies it out of a terminal window. The gap widens with experience instead of narrowing.

Touch-typist's principle: never reach for the mouse

The deepest reason these tricks exist is that lifting your hands from the keyboard breaks the flow of thought. The same impulse that drove the original Unix designers to use short command names (ls, cp, rm, mv) drives Readline's design now. If you can edit, complete, and re-run without leaving the home row, the shell becomes a kind of musical instrument — and the speed at which you think in commands rises to match the speed at which you type them.

Key takeaways

Mental model

The three sources of typing leverage

The three sources of typing leverage

Reverse incremental search as a search tree

Reverse incremental search as a search tree

Practical application

  1. Learn five editing shortcuts first. Ctrl-A (start of line), Ctrl-E (end), Ctrl-K (kill to end), Ctrl-U (kill to start), Ctrl-W (kill word backward). These five cover ~80% of the editing you will ever do. Stop using the arrow keys for anything you can do with these.

  2. Make Tab your default disambiguator. When typing a command, path, or variable, type the first few unambiguous characters and press Tab. If bash beeps, press Tab again to see candidates. Two-keystroke completion is faster than reading the directory listing.

  3. Replace history retyping with Ctrl-R. Whenever you reach for the up-arrow more than twice, switch to Ctrl-R and type a fragment of the command you want. Press Ctrl-R repeatedly to walk backward through matches; press Ctrl-J to copy the match to your line for editing instead of executing immediately.

  4. Use !$ and !* to chain commands. After ls -la /etc/nginx/nginx.conf, type vim !$ to edit the same file — !$ expands to the last argument of the previous command. !* expands to all of them. This is faster than any retyping or copy-paste.

  5. Inspect history-expansion results with :p before running. When using !ssh or !?prod? to re-run a remembered command, append :p first (!ssh:p) to print the expansion without executing. Bash then places the printed command at the top of history; press up-arrow and Enter if it looks right.

  6. Raise HISTSIZE and dedupe. Put these in ~/.bashrc: HISTSIZE=10000, HISTFILESIZE=20000, HISTCONTROL=ignoredups:erasedups. A bigger, deduplicated history makes Ctrl-R dramatically more useful.

Example

Rewinding a complex pipeline in three keystrokes

Suppose ten minutes ago you ran:

$ docker logs --since 1h api-server-7d4f9 | grep -E 'ERROR|WARN' | sort | uniq -c | sort -rn

Now an alert fires and you need to run it again. The slow path is to scroll up-arrow through forty intervening commands, find it, and press Enter. The Readline path is:

$ <Ctrl-R>docker logs

Bash jumps to the matching line as you type the seventh character. Press Enter and the full pipeline re-runs. Three keystrokes (Ctrl-R, "docker logs", Enter) replace forty up-arrows.

If the container ID changed, press Ctrl-J instead of Enter — that puts the line on your prompt for editing, where Alt-B and Alt-F walk by word and you replace api-server-7d4f9 with the new ID in a few keystrokes more.

History expansion to chain a check before an action

A common safety pattern in shell work is inspect, then act on the same target:

$ find . -name '*.tmp' -mtime +30
./build/cache/old-1.tmp
./build/cache/old-2.tmp
./vendor/scratch.tmp
$ rm !$

Wait — !$ is the last argument of the previous command, which is +30, not the file list. The correct chaining is to re-run with -delete:

$ !! -delete

!! expands to the previous full command line, to which we append -delete. The shell prints what it will run before executing (because history expansion always echoes the result), so you see the full find ... -delete invocation before pressing Enter. That print-then-execute behavior is the safety check; do not bypass it by aliasing it away.

Continue exploring

Tags