Using Readline Functions in Bash

Tutorial – Readline

Article from Issue 232/2020
Author(s):

Readline provides you with a rich set of tools for working with text and moving around quickly and efficiently on the command line.

Try this: Open a terminal window and type the following at the prompt:

ls non-existent_dir

Then move your cursor back to the space between ls and non-existent_dir and press Ctrl+K. Next hit Ctrl+Y and see what happens.

Congratulations: You are now using Bash as it was used 30 years ago. Or, to be precise, you are using Bash's Readline [1] interface.

Originally written by Brian Fox, the creator of Bash, Readline is a library for advanced key-jockeying for text-based interfaces like Emacs and, yes, Bash. It provides a set of utilities for cutting and pasting (called "killing" and "yanking" in Readline parlance) before such things were made common in graphical interfaces.

In the examples above, the K in Ctrl+K stands for "kill" and the keyboard shortcut Ctrl+K kills everything to the right of the cursor until the end of the line. The Y stands for "yank" and it pastes whatever was last killed into what is called the kill ring.

One Ring

The kill ring is where killed strings go. Remember that killing is like cutting, so the strings you kill don't really go away. The kill ring is like the desktop's clipboard, but completely independent from it.

The nice thing about the kill ring is that it has a number of slots, usually about 60, so you can store quite a few strings in it, and you can rotate through them when yanking (pasting) them back.

To understand better how it works, try this – type

one two three

onto an empty line in your terminal.

Move the cursor to the beginning of three and press Alt+D. This will erase three from the line. The difference with Ctrl+K is that it deletes until the end of the current word, not the current line. So, if you move your cursor to the beginning of one and press Alt+D, one will disappear into the kill ring, but two will stay.

Finally, move to the beginning of two and press Alt+D to pull it into the kill ring. Now the ring contains two, one, and three in that order: two, the last element you killed, is at the top of the kill ring, then one is underneath two, and three, the first element you killed, is at the bottom. (I know: the metaphor of a ring with a top and a bottom doesn't seem to make much sense right now, but bear with me.)

Hold down Ctrl and press Y as before and two, the top element, pops up on your line. Do the same thing again and two will pop up again. But this time, hold down your Alt key and press Y. Suddenly two becomes one.

You have just taken two off the top of the kill ring, pushed it to the bottom, and the next element has risen to the top instead. In fact, you can press Alt+Y several times, and you will cycle through all the elements in the kill ring. When you reach the last element, you will cycle back to what used to be the first item again. The ring metaphor makes more sense now, right?

Apart from killing and yanking, Readline also supplies functions that allow you to move around on a line. You can of course use the Home key to move to the beginning of a line instead of having to press Ctrl+A, or End instead of using Ctrl+E to move to the end of a line. But in any case, the everyday actions of moving to the beginning or the end of a line, even deleting a character, are all Readline functions mapped to special keys on your keyboard.

Talking of deleting, try this: Say you wanted to kill the FileZilla application but typed firefox by mistake:

killall -s KILL firefox

You could press backspace five times or you could hold down the Alt key and press 5. Your prompt changes to argument mode, showing the argument you entered (5) as you can see in Figure 1.

Figure 1: Entering a numeric argument for a Readline function.

Now hit Backspace, and Bash will delete five characters, leaving:

killall -s KILL fi

This, by the way, works for regular characters, too. Need to type out eight h's for some reason? Press Alt+8, then H, and… Voila! You just saved yourself five keystrokes.

If you need an argument with double, triple, or more digits (62 h's! 345 backspaces!), press Alt plus a digit while in argument mode (Figure 1). Say you need exactly 853 h's. Press Alt+8, then Alt+5, then Alt+3, and finally H (Figure 2).

Figure 2: Use Readline arguments to get the exact number of characters you need.

Realms and Scripture

Apart from killing separate words or until the end of the line, you can also establish "regions" and copy or kill the characters within those. You do this by setting a "mark" on the line, moving the cursor somewhere else (the position of the cursor is known as the "point"), and then copying or killing the characters between the mark and the point, pushing them to the kill ring.

There is a problem though: The functions of copying or killing regions are not assigned to any key combination by default, so you'll have to implement them yourself.

Do this:

cat /etc/inputrc

What you are seeing is the system-wide configuration file for Bash's Readline defaults. On my machine, it looks like what you can see in Listing 1. In /etc/inputrc, you can see how certain keyboard combinations are assigned to Readline actions:

Listing 1

/etc/inputrc Example File

# do not bell on tab-completion
#set bell-style none
set meta-flag on
set input-meta on
set convert-meta off
set output-meta on
$if mode=emacs
# for linux console and RH/Debian xterm
"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": beginning-of-history
"\e[6~": end-of-history
"\e[7~": beginning-of-line
"\e[3~": delete-char
"\e[2~": quoted-insert
"\e[5C": forward-word
"\e[5D": backward-word
"\e\e[C": forward-word
"\e\e[D": backward-word
"\e[1;5C": forward-word
"\e[1;5D": backward-word
# for rxvt
"\e[8~": end-of-line
# for non RH/Debian xterm, can't hurt for RH/DEbian xterm
"\eOH": beginning-of-line
"\eOF": end-of-line
# for freebsd console
"\e[H": beginning-of-line
"\e[F": end-of-line
$endif

Here's how to interpret this notation:

  • \e followed by a key code is used to link a key code to an action. You can find the key code of a key with Bash's showkey command as explained below.
  • \e followed by the name of another key means press Esc, let go, and then hit the other key. \eo, for example, would mean hit Esc and then O.
  • As we have seen, you can also create keyboard shortcuts by combining Ctrl and Alt with other keys. \C- followed by another key means "hold down the Ctrl key while you hit the other key." For example, /C-k means "hold down Ctrl and hit K" – this will usually erase all the characters from the cursor's position until the end of the line. Note that you can have several of these combos one after another to carry out different tasks, like \C-x\C-r, which reloads the list of macros into Bash after you have modified them.
  • \C- followed by the name of more than one key means hold down the Ctrl key while you hit the first key listed. Then, let go of Ctrl and hit the second key listed. For example, \C-xx means "hold down Ctrl and hit X, let go of Ctrl, and hit X again."
  • \M-key means use the Alt key instead of Ctrl. It works the same as Ctrl, and an example would be M-3 to enter 3 as an argument for a Readline function.

To create a new key combination for yourself, you don't have to edit /etc/inputrc, since you can create your own file in your home directory. Let's do that so we have a keyboard shortcut to copy and cut regions to the kill ring.

Fire up your favorite text editor and create a file called .inputrc in your home directory (see Listing 2).

Listing 2

.inputrc

01 $include /etc/inputrc
02
03 "\C-xc": copy-region-as-kill
04 "\C-xx": kill-region

Line 1 pulls in /etc/inputrc so that you get all the benefits from the system-wide defaults. Line 3 defines a new keyboard shortcut. In this case, you are associating the Ctrl+X and then C combination with Readline's copy-region-as-kill function. Line 4 is the same thing, except you are associating the Ctrl+X and then X combination with the kill-region function. For the record, there is a complete list of Bash's Readline functions in the online documentation [2].

Let's try it out. Save .inputrc, exit your editor and press Ctrl+X and Ctrl+R so the changes are applied for the current shell. If there is anything wrong with your script, Readline will show an error.

When everything is okay, type a long line into your terminal:

wget "https://somedomain.com/somefile" && echo
  "Downloaded successfully"

Move your cursor to the s of somefile and press Ctrl+@. This sets the mark on the s. Now move your cursor to the double quotes after somefile and press Ctrl+X and then C, the keyboard combination you set up in .inputrc to copy the region to the kill ring.

It may seem that nothing has happened, but now move the cursor to after Downloaded and press Ctrl+Y to yank the latest item on the kill ring into the line. Hey presto!:

wget "https://somedomain.com/somefile" && echo
  "Downloaded somefile successfully"

If instead of using the keyboard shortcut for copying, you had held down Ctrl and pressed X and then let go of Ctrl and pressed X again, you would've cut the region between the mark and the point.

Apart from associating shortcuts with Readline's predefined functions, you can also create your own functions and keyboard shortcuts for them.

Open .inputrc in your text editor again and add the following line:

"\el": "ls\015"

This creates a keyboard combination (press Esc, let go, and then press L) that lists the content of the current directory. The \015 part of the command is the code for Carriage Return (Enter) in ASCII.

You can find more key codes using the shell's showkey -a command (Figure 3).

Figure 3: Use showkey -a to find out the codes for non-character keys on your keyboard.

For example, suppose you wanted to list the contents of the current directory by pressing Alt+Right Arrow. Run showkey -a and hold down Alt and press the right arrow to discover the key code (Listing 3).

Listing 3

Getting Key Codes

01 $ sudo showkey -a
02 Press any keys - Ctrl-D will terminate this program
03 ^[[1;3C 27 0033 0x1b
04         91 0133 0x5b
05 ...

What you're interested in is the first line showkey spits out after pressing Alt+ Arrow Right. In Listing 3, that would be line 3. Focus on the bit that says ^[[1;3C.

Press Ctrl+D to exit showkey and open .inputrc in your text editor.

Change the line that says

"\el": "ls\015"

to

"\e[1;3C": "ls\015"

Save the file and reload the Readline macros (Ctrl+X, Ctrl+R).

Now hold down Alt, press the Right Arrow key on your keyboard, and the current directory will list.

History

The Readline set of functions also covers Bash's history. Every time you press the Up Arrow to navigate to a command you have used earlier, you are invoking a Readline function. But there is more to finding entries than just pressing the Up Arrow over and over.

Try this: Start on an empty line and press Ctrl+R. Bash will enter incremental search mode. This means you can start typing in some of the letters of the command you are looking for and Bash will search upwards through the history and show you matches as you type (Figure 4).

Figure 4: The Readline search functions show entries in the Bash history as you type.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News