Keyboard Shortcuts every Command Line Hacker should know about GNU Readline
Shame, then, that even serious command line hackers never bother learning about its capabilities, as they can supercharge your command line productivity.
There’s a large body of command line hackers who, despite their incredible skill and speed, never cottoned on to the fact that line editing – editing text on a prompt, like the one in Python or bash – is a skill that is transferable and worth learning.
Line editing is not a novel concept, nor a new one. On Linux, the most common line reader is GNU readline. It provides a framework for basic completion, movement, editing and prompt history.
In bash it’s used when you press
<TAB> to complete a file name; navigate through your input history with the arrow keys; or edit and type text at the prompt. In fact, it’s so ubiquitous – yet veiled behind the programs it supports – that people credit bash, and not readline, for many of its novel features.
But GNU readline’s a lot more capable than you might think. By default it uses Emacs key bindings, but you may already know that. Yet a surprising number of hackers – even ardent Emacs users – never bothered exploring its capabilities. (There is also, in the interest of full disclosure, a Vi emulation layer.)
Most people plateau when they learn about
Control+r for reverse history isearch. But there are many reasons to learn keep learning:
- Quick Keyboard Macro Recording
Tired of tediously manipulating a complex prompt when you decide you want to pass the prompt output to
Maybe you keep having to type the same repetitive incantation in
python, over and over again?
Maybe you have to write the same
SELECTstatements over and over again in
So why not use a keyboard macro? You can record, play back, and save them for later. And they can – if you want – work across programs that use GNU readline.
- Smarter History Use
There’s more to GNU readline’s history commands than merely navigating up or down the history list. Most are specialized; but they are all worth learning about.
- Faster Movement and Editing
Learn a handful of Emacs key bindings and they’ll pay dividends across a swathe of programs. Whether it’s bash or SQLite’s command line interface, the key bindings remain the same — and some even augment the defaults with specialized commands specific to that program.
Memorize once, use (nearly) everywhere!
Of course, most Emacs hackers don’t bother, because Emacs has its own shell integration, and that does the job 99% of the time.
So why bother learning about GNU readline if you’re an Emacs maven?
Because, when you next find yourself out in the cold – or if you wholly reject the warm embrace of Emacs – you’ll have a set of standardized key bindings that greatly improves your productivity. There’s also a rich set of features you can access with little effort, and they work well in a wide range of disparate command line applications.
So if you’re sick and tired of reaching for the arrow keys just to edit or move around the prompt: Hark! For there are better ways abound.
Basics, Caveats & Useful Defaults
But, let’s get the basics out of the way first.
GNU readline has its own little configuration language, and the global settings for it are usually found in
/etc/inputrc. When it’s done reading the global file, it’ll check for a user-local file in
Like Emacs, readline respects Emacs’s notation for shortcuts:
C- is Control;
C-M- is Control and Alt in unison. Like Emacs, readline is also capable of simple chords:
C-x p and
C-x C-r are both legitimate chords: press and release
Control+x followed by
p, for instance. So far, so Emacs.
GNU readline is not universally used. If you’re not using Linux, your application may not use readline at all. And some programs have their own line editor implementation, like Zsh.
However, in the
.inputrc file you should know that the
M- qualifier does not work. Instead, write
\e for Meta; and
\C for Control. There is also a long-form format, but I find it cumbersome and the parser a bit wonky. And in any event, in a readline keyboard macro – more on keyboard macros later – you have to use this notation anyway, so you may as well standardize on the shorthand notation.
# Correct - binds to C-x q "\C-xq": "Hello, World" # Correct - binds to C-M-p "\e\C-p": "Good Bye!"
Variables are set with
set expand-tilde on
You can also limit settings to certain programs:
$if Bash # ... $else # ... $endif
Determining the name is not easy though without reading the source.
One thing that catches people out about Emacs-style key bindings in readline is its hidden timeout. By default the
keyseq-timeout value is a rather ambitious 500 ms. I find that far too quick, so I change it 1200 ms:
set keyseq-timeout 1200
Most distros seem to leave readline’s color highlighting for completion disabled. I don’t know why, as it’s helpful.
set colored-stats on set colored-completion-prefix on
Readline looks at your
LS_COLORS environment variable to determine what the highlighting should look like. If it’s missing, it’ll use a reasonable default.
Basic Navigation and Editing
For command line editing, knowing a handful of basic keys is all you need to greatly speed up your typing and, on that account, your productivity. If you’re already familiar with Emacs’s movement keys, then you’ll know them all, already.
|Move backward/forward one character|
|Move backward/forward by word|
|Move to the beginning/end of the line|
|Kill word backward/forward|
Delete char forward/backward
|Kill (to clipboard) to end of line/beginning of line|
|Yank from kill ring|
|Cycle through kill ring history|
|Upper, lower, or capitalize forward word|
|Cycle through the undo list|
Although GNU readline maintains a kill ring (clipboard), which works much like Emacs’s kill ring, it’s not shared with your system’s clipboard, nor any other instances of GNU readline. It is, in effect, local to the process you’re running.
GNU readline also has limited support for Emacs’s universal and numeric arguments, so it’s possible to type
M-2 M-d to kill two words forward. You can also supply negative arguments with
M-- M-u uppercases the word preceding point.
Undo is also worth knowing about. It’s different than Emacs’s undo ring, as it’s linear, and is reset when the prompt is.
Knowing how to go to the beginning and end of a line; deleting and moving by word; and a little bit about killing and yanking will go a long way.
Taken together and you have the rudiments of Emacs’s most basic editing suite available in a wide range of command line applications.
Less Useful Key Bindings
|Swap point and mark|
|Character search forward/backward|
|Insert a comment|
The concept of the mark – one half of the dynamic duo, with point being the other one, that makes up the text selection region in Emacs – is also present in GNU readline. However, there is no support for visualizing the selected region: you’re going to have to remember where the mark is! This is like Emacs before
M-x transient-mark-mode became a thing.
That, combined with the editing scope limited to a line of text, means it’s not really such a useful thing to know about.
Quoted insert is useful if you regularly have to insert keyboard escape codes. Like
^[ for ANSI control codes. To insert
You can also comment out the line with
M-# but I find its utility minimal in a line editor where commenting out a line has narrow applications.
C-x C-r. That key is really only useful if you’re experimenting with your
.inputrc file. Invoking the key binding will silently force readline to reload its settings. That means you can experiment and not have to restart your program to test your changes.
This is where, in my opinion, the value of GNU readline really kicks in. Like Emacs, readline has a keyboard macro recorder. It’s nowhere near as advanced as Emacs’s keyboard macro recorder; but you only need the basics.
You can record, play back, and print out keyboard macros.
That is more useful than it seems, and it can save you a lot of typing:
If you’re a frequent user of
fzf– or ezf, if you want the Emacs equivalent – then I’m sure you have ritualized the practice of piping stuff to said tool:
$ apt-cache search emacs
Could be transformed into this with a quick’n’dirty keyboard macro:
$ $(apt-cache search emacs | ezf -f 1)
At that point you’re free to take the command substitution and feed it into a command of your choice — like
apt-getpossibly. Remember, you can leave the point wherever you like: at the beginning or end of the prompt; or maybe at the pipe, so you can quickly change the parameters.
- You can wrap prompts in simple commands, like
dir( ... )or
help( ... ), in Python.
- Keyboard macros can be made global or local and written by hand. No recording required.
- You can create one on-the-fly, and only later bind it to a key if you find it useful enough to keep around.
To truly make use of the macro functionality you must first bind the command
print-last-kbd-macro to a key in your
Here I bind it to
C-x P. But feel free to use another key binding. Note the backslashed
|Start/Stop macro recorder|
|Play last macro|
|Print out macro (Note you must bind the command to a key for this to work)|
To start recording type
C-x (. Unlike Emacs – or indeed unlike many other parts of GNU readline that signal state changes, like prefix arguments – it does not tell you it’s recording, which is quite infuriating. Nevertheless, you can stop recording with
C-x ) and then play back your newly-minted macro with
I would recommend you experiment a bit. There are few things it can’t record. It’s limited to just this line; you cannot macro record (or play back) across multiple prompt invocations. Keep that in mind.
C-x P (or a key binding of your own choosing) spits out the macro string needed to replay the macro. You can put it in your
.inputrc after binding it to a string, but make sure you double quote it if it is not quoted already.
Here’s one that calls out to
ezf), bound to
"\C-xF": "\C-e | fzf)\C-a(\C-a$\C-b\C-a"
If you look closer, you’ll see that it’s just a string with a bunch of key bindings. So you can indeed write your own.
To test it works, type
C-x C-r to reload your
I personally think the keyboard macro functionality is an underrated gem. I can’t imagine too many people know about or use it. Which is a real shame!
bash or another tool of your choosing, it may have some form of TAB-based completion. By default the builtin GNU readline command
complete is bound to
C-i (they’re one and the same control code, in actual fact.) But what most people don’t know is that is just the beginning.
Bash, for instance, extends the completion system and adds a host of specialized completion commands.
|Complete at point|
|List possible completions at point|
|Insert all completions|
The most common – the one everyone knows – is
TAB. When a developer integrates GNU readline into their application, they may decide to support readline’s underlying completion engine. Python does this; that’s why you can do basic completion with
TAB in a Python shell.
There are a couple more that are quite specialized, but worth knowing about.
M-? directs readline to display the completions at point. This is pretty much the same as hitting
TAB multiple times, but with a crucial difference:
M-? won’t autocomplete parts of your query as
M-* is more interesting. It takes all possible completion matches – sourced from
M-? – and inserts them into your prompt. Now you can edit them before you pass them to another program.
If you frequently use
~ – referring to your home directory – you can use
M-~ to complete against it, as though you’d
TAB-completed. Other programs may not implement it, or they handle it differently. Python inserts the path to your home directory, for instance. The variable
expand-tilde partly controls this.
Every GNU readline program implements its own unique history list, but (as you’d expect) they share a common set of key bindings you can use to navigate that list.
|Go to beginning/end of the history list|
|Reverse/Forward isearch history|
|Query search backward/forward through history list|
|Yank (insert) last argument|
|Previous/Next history entry|
I’m sure you’re familiar with the
<down> arrows to move through history.
C-n serve the same purpose.
M-> move to the very beginning and end of the history list. Not that useful, but if regularly cycle through the history then it’s useful to know that you can do so from either end, at will.
The history commands I love most, aside from navigating up or down, are the isearch – incremental search – history commands bound to
C-s. Both search the history, incrementally, as you type. They’re incredibly useful and a hallmark of readline. Lots of people know about
C-r but somehow never tweaked
C-s: it searches forward, and you can mix and match the two to skim through the history matches.
C-sfreezes your terminal, you can blame the superannuated “flow control” feature — it stops the flow of text so you can pause at will and read it. Wonderful feature in the 1980s when your teletype would type faster than you could read the text. Less so today.
stty -ixondisables it.
In addition to incremental history search, there’s
M-n. Type either and you’re asked to input a search string. Pressing
RET jumps to the first match. It has its uses, but if you normally run your shells in Emacs you’ll find it frustrating as
M-n is how you navigate history in
Put this incantation in your
.inputrc to rewire those two keys to also step through the history:
"\ep": previous-history "\en": next-history
M-.. It inserts the last argument of the last command; repeat it and you’ll step through previously-used prompt arguments. I find it useful if I regularly have to regurgitate a common argument:
$ grep *.txt foo.* # Typing M-. now inserts foo.*: $ rg █ # -> rg foo.*
Discovering Key Bindings
Because GNU readline is so extensible, it’s not uncommon for programs to create their own commands and key bindings that apply only to that program. The key bindings I talked about earlier are, broadly speaking, available everywhere — even if they don’t really make sense in the context of that application.
But bash, being a GNU product, has a large range of extended key bindings and commands you should know about.
But in order to discover them you really need to know about the dump commands. They are unbound by default. Here’s what I have bound them to:
"\e\C-f": dump-functions "\e\C-v": dump-variables
C-M-f prints out all known functions;
C-M-v all known variables. Give them a prefix argument (e.g.,
M-1 C-M-f) and readline spits out
.inputrc-compatible code you can paste into your own settings file.
Bash-specific Key Bindings
There are many; so I encourage you to explore yourself. But I’ll list a couple of my favorites.
|Expand environment variable|
|Edit and execute command in Emacs|
|Expand using dynamic abbrev. (Unbound by default; see below.)|
C-M-e takes an environment variable at point and expands it inline. Useful for things like manipulating the order of things in
$PATH, or tweaking the contents of one variable before storing it in another.
The other completers –
M-$ – attempt a specialized, narrower version of the common completer bound to
TAB. They have their uses: when you must insert a variable, for example.
Dynamic Abbrev is an Emacs feature that has made the leap – sort of, anyway – into readline. It looks at the word at point and attempts to expand it using previously-seen history values. So, if you previously typed in a complex word – say a hostname – you can ask readline to recall it anywhere in a prompt:
$ ping secret.setec-astronomy.example.com # Typing C-M-o expands to the full hostname. $ ssh sec█
It is not bound by default, so I recommend
Repeated invocations cycle through the matches. It’s a fantastic command.
Finally I want to mention
C-x C-e, which is really quite cool. When you invoke
C-x C-e, then bash/readline will send the current prompt to your
$EDITOR for further processing.
If you have your Emacs running in client-server mode (
M-x server-start, for instance) and your
$EDITOR set to
emacsclient then you can indeed edit the prompt in Emacs before returning it from whence it came with
Great for more complex edits.
Here’s a snapshot of my
.inputrc. It’s mostly about making things even more Emacs-like, plus a couple of basic macros.
### Mickey's .inputtrc # Change the timeout for key sequences as 500ms is too fast. set keyseq-timeout 1200 # By default, completions are not highlighted in color. set colored-stats on set colored-completion-prefix on ### BASH $if Bash # Wrap a command in $( ... ) "\C-xq": "\C-a\$(\C-e)" # Wrap a command in $( .... | ezf -f 1) "\C-xF": "\C-e | ezf -f 1)\C-a(\C-a$\C-b\C-a" # C-M-o is dabbrev-expand "\e\C-o": dabbrev-expand $endif ### Python $if Python # Wrap prompt in !help( ... ) (for PDB) "\C-xh": "\C-a!help(\C-e)" # Wrap prompt in dir( ... ) "\C-xd": "\C-adir(\C-e)" $endif ### Global # Prints the last recorded macro "\C-xP": print-last-kbd-macro # M-m is back-to-indentation which is what I usually use to go to the # beginning of a line; everywhere else, I bind it like C-a. "\em": beginning-of-line # M-p and M-n should behave like they do in M-x shell in Emacs. "\ep": previous-history "\en": next-history # C-M-f and C-M-v dump functions and variables. "\e\C-f": dump-functions "\e\C-v": dump-variables
GNU readline’s a real gem, and underused. I hope more people – particularly Emacs users, as the keys are so familiar – learn a little bit more about readline and its capabilities. Memorizing a handful of key bindings will supercharge your command line productivity.