home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Unix Power ToolsUnix Power ToolsSearch this book

30.14. Shell Command-Line Editing

When Unix started, a lot of people used real teletypes -- with a roll or box of paper instead of a glass screen. So there was no way to recall and edit previous commands. (You could see them on the paper, of course, but to reuse them you had to retype them.) The C shell added history substitution operators (Section 30.2) that were great on teletypes -- and are still surprisingly useful on "glass teletypes" these days. All shells except the oldest Bourne shells still have history substitution, although it's limited in the Korn shells.

Modern shells also have interactive command-line editing. That's the ability to recall previous command lines and edit them using familiar vi or emacs commands. Arrow keys -- along with Backspace and DELETE keys -- generally work, too. So you don't need to know emacs or vi to edit command lines. But -- especially if you're comfortable with Emacs-style commands (meta-this that, control-foo bar) -- you'll find that most shells let you do much more than simply editing command lines. Shells can automatically correct spelling, complete partially-typed filenames (Section 28.6), and much more.

The basic idea of command-line editing is that the previous commands are treated like lines in a plain-text file, with the most recently typed commands at the "end" of the file. By using the editor's "up line" commands (like k in vi or C-p in Emacs), you can bring copies of earlier commands under your cursor, where you can edit them or simply re-execute them without changes. (It's important to understand that you're not editing the original commands; you're editing copies of them. You can recall a previous command as many times as you want to; its original version won't be changed as you edit the copy.) When you've got a command you want to run, you simply press ENTER; your cursor doesn't have to be at the end of the line. You can use CTRL-c (C-c in Emacs jargon) to cancel a command without running it and get a clean shell prompt.

It would be easy for us to fill a chapter with info on command-line editing. (In this book's fourth edition, maybe we should!) Because every shell has its own way to do this, though, we've decided to stick to the basics -- with a few of the bells and whistles tossed in as examples. To really dig into this, check your shell's manpage or its Nutshell Handbook.

Another way to do history editing is with your own editor: use the fc command.

30.14.1. vi Editing Mode

All shells with command-line editing have support for basic vi commands, but it's usually not complete and historically not well documented. For instance, I've used some shells where the . (dot) command wouldn't repeat the previous edit -- and other shells where it would -- but neither shell's manual page mentioned this useful fact. Macros are limited, and you don't define them with the usual map (Section 18.2) command; instead, the shell's built-in key binding command controls which built-in shell editing function is executed when a key is pressed. (The Korn shell doesn't allow any special vi bindings, though at least it has complete documentation.) Still, with all those caveats, you'll probably find that vi editing is pretty comfortable if you already know vi. (If you don't know vi, though, I'd recommend Emacs editing. See the next section.)

At a bare shell prompt, you're effectively in vi text-input mode: the characters you type appear on the command line. If you want to edit, press ESC to go to command mode. Then you can use typical commands like dw to delete a word and ct. to change all characters to the next dot on the line. Commands like a, i, and c take you to text-input mode. You can execute a command line from either command mode or text-input mode: just press ENTER anywhere on the line.

One difference between the shell's vi mode and real vi is that the direction of searches is opposite. In real vi, the motion command k and the search command ? (question mark) both move to previous commands. In shells, k still moves to a previous command, but / (slash) searches for previous commands. By the way, after you specify a search with \, press ENTER to do the search. These differences from real vi can be confusing at first, but with practice they soon become natural.

To choose vi mode, type set -o vi in Bourne-type shells and bindkey -v in tcsh. In bash, you may also use keymap editor, with a variety of different editor settings (Section 30.14.5), to set up the editing mode. To make this the default, store the command in your shell's setup file (Section 3.3) (in bash, you can also edit your Readline inputrc file). You can switch back and forth between the two modes as you work; this is useful because the Emacs mode lets you do things you can't do in vi.

30.14.2. Emacs Editing Mode

If you know the Emacs (Section 19.1) editor, you'll feel right at home in the shells. Although the support isn't complete -- for instance, you can't write eLisp code (and you can't run psychoanalyze-pinhead (Section 19.13) :-)) -- the emacs-mode commands act like a natural extension to traditional, simple shell editing commands. So, even if you don't know emacs, you'll probably be comfortable with emacs mode. Even many browsers nowadays use the traditional emacs mode commands for moving about in the Location field, so you may already know some of these even if you're not aware that you do.

To move to the beginning of a line, use C-a (that's CTRL-a in Emacs-speak); C-e moves to the end of a line. C-f moves forward a character, and C-b moves backward one character (without deleting). C-n moves forward to the next command, and C-p moves backward to the previous line. Your keyboard's arrow keys probably also work. Your shell has at least one search command; try C-r to start a search and press ENTER to run the search.

Your keyboard's usual delete key (Backspace or DEL) deletes one character backward, and C-d deletes one character forward. C-k deletes ("kills") to the end of the line, and C-w deletes ("wipes") backward to the start of the line.

To choose emacs mode, type set -o emacs in Bourne-type shells and bindkey -e in tcsh. In bash, use one of the keymap editor commands, such as keymap emacs. To make this the default, store the command in your shell's setup file (Section 3.3) (in bash, you can also edit your Readline inputrc file). You can switch back and forth between emacs and vi modes as you work.

30.14.3. tcsh Editing

The bindkey command is used to bind keys to built-in editor functions. With no arguments, it gives a list of all key bindings, like this:

tcsh> bindkey
Standard key bindings
"^@"           ->  set-mark-command
"^A"           ->  beginning-of-line
"^B"           ->  backward-char
        ...
"¡"  to "^y"    ->  self-insert-command
Alternative key bindings
Multi-character bindings
"^[[A"         -> up-history
"^[[B"         -> down-history
        ...
"^X^D"         -> list-choices-raw
Arrow key bindings
down           -> down-history
up             -> up-history
left           -> backward-char
right          -> forward-char

In this list, ^ (caret) starts control characters, so ^A means CTRL-a. ^[ is an escape character (which is also generated when you press a function key, like F1 or up-arrow, on most keyboards). Thus, ^[[A is the sequence ESC left-bracket A (which is also sent by the up-arrow key on VT100-type keyboards). The "alternative key bindings" are used in vi command mode, which wasn't enabled in the example above, because I made it while using emacs bindings.

There's a list of some editor functions in the tcsh manual page, but that list only includes "interesting" functions. To get a complete list, including "boring" functions like backward-char (to move backward one character), type the command bindkey -l (lowercase letter L); that lists all the editor functions and describes each one briefly:

tcsh> bindkey -l
backward-char
          Move back a character
backward-delete-char
          Delete the character behind cursor
     ...

You'll probably want to redirect the output of those bindkey commands into a file -- or pipe it to a pager such as less (Section 12.3) that lets you page through and do searches. (You could probably merge the output of bindkey and bindkey -l into one list with perl (Section 41.1) or awk (Section 20.10) and an associative array, but I haven't tried.)

To bind a key to an editor function, use bindkey with two arguments: the key to bind and the function name. The key can be the literal key you want to type, but that can be messy when you're trying to put the definition in a shell setup file (which traditionally doesn't have nonprintable characters in it) or when you're trying to bind an arrow key or some other key. So you can represent a control character with two characters: a literal caret (^) followed by the letter -- for example, ^A. You can use standard backslash escape sequences, such as \t for a TAB character, but remember to quote (Section 27.13) special characters. And the special option -k lets you name an arrow key: for instance, bindkey -k left for the left arrow.

Here's an example of one of my favorite tcsh editor functions: magic-space. By default, it isn't bound to a key, but it's meant to be bound to the space key. The function expands any history substitutions (Section 30.8) in the command line, then lets you continue editing. In this example, I start by executing an ls command. Then I bind the space key. After that, I start a new command line. I type find and a space, but nothing happens yet because there are no history references. Then I type !ls:$, which is the history substitution for the last argument of the previous ls command; when I press the space key, that argument is expanded to /usr/local/bin, and I can type the rest of the command line:

tcsh> ls /usr/local/bin
acroread               netscape  rsh-add      ssh
ex                     nex       rsh-agent    ssh-add
lcdctl                 nsgmls    rsh-askpass  ssh-add1
    ...
tcsh> bindkey " " magic-space
tcsh> find !ls:$ SPACE
tcsh> find /usr/local/bin -perm  ...

You also can bind a key to a Unix command by using bindkey with its -c option. This is different from simply executing a command at a shell prompt. When a Unix command is bound to a key, the shell will run that command without disturbing the command line you're editing! When the bound command finishes running, the command line you were editing is redisplayed as it was. For example, the binding below makes CTRL-x l run the command ls -lt | less:

bindkey -c ^Xl 'ls -lt | less'

There's much, much more. The tcsh(1) manpage is too brief to teach this well (for me, at least). I recommend the O'Reilly book Using csh & tcsh; it doesn't cover all of the newest tcsh, but it does a complete job on the command-line editor.

30.14.4. ksh Editing

This section covers the public domain Korn shell, pdksh. The original Korn shell is similar.

The bind command binds keys to built-in Emacs editor functions. (You can't re-bind in vi mode.) With no arguments, it gives a list of all key bindings, like this:

$ bind
^A = beginning-of-line
^B = backward-char
   ...
^[b = backward-word
^[c = capitalize-word
   ...
^XC = forward-char
^XD = backward-char

In that list, ^ (caret) starts control characters, so ^A means CTRL-a. And ^[ is an escape character (which is also generated when you press a function key, like F1 or up-arrow, on most keyboards) -- so ^[b is the sequence ESC b.

There's a complete list of editor functions in the ksh manual page. You can also get a list from the command bind -l (lowercase letter L):

$ bind -l
abort
beginning-of-history
complete-command
     ...

To bind a key to an editor function, use bind with the string to bind, an equal sign (=), then the binding. The key can be the literal key you want to type, but that can be messy when you're trying to put the definition in a shell setup file (which traditionally doesn't have nonprintable characters in it) or when you're trying to bind an arrow key or some other key. So you can represent a control character with two characters: a literal caret (^) followed by the letter -- for example, ^A. The other special prefix supported is the two-character sequence ^[ (caret left-square-bracket), which stands for the ESC or Meta key. And remember to quote (Section 27.12) any special characters. So, if you want to make CTRL-r be the traditional Unix rprnt (Section 28.2) operation (to reprint the command line), and make META-r search the history (which is bound to CTRL-r by default in pdksh), you could use these two bindings:

bind '^R'=redraw
bind '^[r'=search-history

30.14.5. bash Editing

The most permanent place to customize bash editing is in the Readline inputrc file. But you also can add temporary bindings from the command line with the bind command. These bindings work only in the current shell, until the shell exits. The bind syntax is the same as the inputrc file, but you have to put quotes (Section 27.12) around the whole binding -- so watch out for quoting conflicts. For example, to make CTRL-o output a redirection (Section 43.1) command and pathname:

bash$ bind 'Control-o: ">> /usr/local/project/log"'

To get a list of all key bindings, use bind -P in Version 2 or bind -v in original bash. In the next example, for instance, you can see that CTRL-m (the ENTER key) and CTRL-j (the LINEFEED key) both accept the command line. Quite a few characters (CTRL-a, CTRL-b, etc.) simply insert themselves into the command line when you type them. If you need a literal control character, you may be able to type CTRL-v and then the character.

less Section 12.3

bash$ bind -P | less
abort is not bound to any keys
accept-line can be found on "\C-j", "\C-m".
   ...
backward-delete-char can be found on "\C-h", "\C-?".
   ...
self-insert can be found on "\C-a", "\C-b", "\C-c", "\C-e", "\C-f", ...

There are two bind options good for use with inputrc type files. To write all the current key bindings out to a file named inputrc.new, type bind -p > inputrc.new in bash2; use the -d option in original bash. (You can overwrite your default .inputrc file this way, too, if you want.) To read an inputrc file into the current shell (if you've just edited it, for instance), use bind -f and give the filename as an argument.

Finally, the bind option -m keymap chooses the keymap that subsequent bindings apply to. The keymap names that bash2 understands are emacs, emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, vi-command, and vi-insert. (vi is the same as vi-command, and emacs is the same as emacs-standard.)



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.