Previous Section  < Day Day Up >  Next Section

2.4. vi Editing Mode

Like emacs-mode, vi-mode essentially creates a one-line editing window into the history list. vi-mode is popular because vi is the most standard UNIX editor. But the function for which vi was designed, writing C programs, has different editing requirements from those of command interpreters. As a result, although it is possible to do complex things in vi with relatively few keystrokes, the relatively simple things you need to do in bash sometimes take too many keystrokes.

Like vi, vi-mode has two modes of its own: input and control mode. The former is for typing commands (as in normal bash use); the latter is for moving around the command line and the history list. When you are in input mode, you can type commands in and hit RETURN to run them. In addition, you have minimal editing capabilities via control characters, which are summarized in Table 2-7

Table 2-7. Editing commands in vi input mode

Command

Description

DEL

Delete previous character

CTRL-W

Erase previous word (i.e., erase until a blank)

CTRL-V

Quote the next character

ESC

Enter control mode (see below)


Note that at least some of these—depending on which version of UNIX you have—are the same as the editing commands provided by UNIX through its terminal interface.[5] vi-mode will use your "erase" character as the "delete previous character" key; usually it is set to DEL or CTRL-H (BACKSPACE). CTRL-V works the same way as in emacs-mode; it causes the next character to appear in the command line as is and lose its special meaning.

[5] In particular, versions of UNIX derived from 4.x BSD have all of these commands built in.

Under normal circumstances, you just stay in input mode. But if you want to go back and make changes to your command line, or if you want to recall previous commands, you need to go into control mode. To do this, hit ESC.

2.4.1. Simple Control Mode Commands

A full range of vi editing commands are available to you in control mode. The simplest of these move you around the command line and are summarized in Table 2-8. vi-mode contains two "word" concepts. The simplest is any sequence of non-blank characters; we'll call this a non-blank word. The other is any sequence of only alphanumeric characters (letters and digits) plus the underscore (_), or any sequence of only non-alphanumeric characters; we'll just call this a word.[6]

[6] Neither of these definitions is the same as the definition of a word in emacs-mode.

Table 2-8. Basic vi control mode commands

Command

Description

h

Move left one character

l

Move right one character

w

Move right one word

b

Move left one word

W

Move to beginning of next non-blank word

B

Move to beginning of preceding non-blank word

e

Move to end of current word

E

Move to end of current non-blank word

0

Move to beginning of line

^

Move to first non-blank character in line

$

Move to end of line


All of these commands except the last three can be preceded by a number that acts as a repeat count. Whenever you type a number for the repeat count, the number replaces the command prompt for the duration of the repeat command. If your keyboard has cursor motion keys ("arrow" keys), you can use the left and right arrows to move between characters instead of the h and l keys. Repeat counts will work with the cursor keys as well.

The last two will be familiar to users of UNIX utilities (such as grep) that use regular expressions, as well as to vi users.

Time for a few examples. Let's say you type in this line and, before you hit RETURN, decide you want to change it:

$ fgrep -l Duchess < ~cam/book/alice_in_wonderland[]

As shown, your cursor is beyond the last character of the line. First, type ESC to enter control mode; your cursor will move back one space so that it is on the d. Then if you type h, your cursor will move back to the n. If you type 3h from the n, you will end up at the r.

Now we will see the difference between the two "word" concepts. Go back to the end of the line by typing $. If you type b, the word in question is alice_in_wonderland, and the cursor will end up on the a:

$ fgrep -l Duchess < ~cam/book/[a]lice_in_wonderland

If you type b again, the next word is the slash (it's a "sequence" of non-alphanumeric characters), so the cursor ends up over it:

$ fgrep -l Duchess < ~cam/book[/]alice_in_wonderland

However, if you typed B instead of b, the non-blank word would be the entire pathname, and the cursor would end up at the beginning of it—over the tilde:

$ fgrep -l Duchess < [~]cam/book/alice_in_wonderland

You would have had to type b four times—or just 4b—to get the same effect, since there are four "words" in the part of the pathname to the left of /alice_in_wonderland: book, slash, cam, and the leading tilde.

At this point, w and W do the opposite: typing w gets you over the c, since the tilde is a "word," while typing W brings you to the end of the line. But whereas w and W take you to the beginning of the next word, e and E take you to the end of the current word. Thus, if you type w with the cursor on the tilde, you get to:

$ fgrep -l Duchess < ~[c]am/book/alice_in_wonderland

Then typing e gets you to:

$ fgrep -l Duchess < ~ca[m]/book/alice_in_wonderland

And typing an additional w gets you to:

$ fgrep -l Duchess < ~cam[/]book/alice_in_wonderland

On the other hand, E gets you to the end of the current non-blank word—in this case, the end of the line. (If you find these commands non-mnemonic, you're right. The only way to assimilate them is through lots of practice.)

2.4.2. Entering and Changing Text

Now that you know how to enter control mode and move around on the command line, you need to know how to get back into input mode so you can make changes and type in additional commands. A number of commands take you from control mode into input mode; they are listed in Table 2-9. All of them enter input mode a bit differently.

Table 2-9. Commands for entering vi input mode

Command

Description

i

Text inserted before current character (insert)

a

Text inserted after current character (append)

I

Text inserted at beginning of line

A

Text inserted at end of line

R

Text overwrites existing text


Most likely, you will use either i or a consistently, and you may use R occasionally. I and A are abbreviations for 0i and $a respectively. To illustrate the difference between i, a, and R, say we start out with our example line:

$ fgrep -l Duchess < ~cam/book[/]alice_in_wonderland

If you type i followed by end, you will get:

$ fgrep -l Duchess < ~cam/bookend[/]alice_in_wonderland

That is, the cursor will always appear to be under the / before alice_in_wonderland. But if you type a instead of i, you will notice the cursor move one space to the right. Then if you type miss_, you will get:

$ fgrep -l Duchess < ~cam/book/miss_[a]lice_in_wonderland

That is, the cursor will always be just after the last character you typed, until you type ESC to end your input. Finally, if you go back to the first a in alice_in_wonderland, type R instead, and then type through_the_looking_glass, you will see:

$ fgrep -l Duchess < ~cam/book/through_the_looking_glas[s]

In other words, you will be replacing (hence R) instead of inserting text.

Why capital R instead of lowercase r? The latter is a slightly different command, which replaces only one character and does not enter input mode. With r, the next single character overwrites the character under the cursor. So if we start with the original command line and type r followed by a semicolon, we get:

$ fgrep -l Duchess < ~cam/book[;]alice_in_wonderland

If you precede r with a number N, it will allow you to replace the next N existing characters on the line—but still not enter input mode. Lowercase r is effective for fixing erroneous option letters, I/O redirection characters, punctuation, and so on.

2.4.3. Deletion Commands

Now that you know how to enter commands and move around the line, you need to know how to delete. The basic deletion command in vi-mode is d followed by one other letter. This letter determines what the unit and direction of deletion is, and it corresponds to a motion command, as listed previously in Table 2-8.

Table 2-10 shows some commonly used examples.

Table 2-10. Some vi-mode deletion commands

Command

Description

dh

Delete one character backwards

dl

Delete one character forwards

db

Delete one word backwards

dw

Delete one word forwards

dB

Delete one non-blank word backwards

dW

Delete one non-blank word forwards

d$

Delete to end of line

d0

Delete to beginning of line


These commands have a few variations and abbreviations. If you use a c instead of d, you will enter input mode after it does the deletion. You can supply a numeric repeat count either before or after the d (or c). Table 2-11 lists the available abbreviations.

Table 2-11. Abbreviations for vi-mode delete commands

Command

Description

D

Equivalent to d$ (delete to end of line)

dd

Equivalent to 0d$ (delete entire line)

C

Equivalent to c$ (delete to end of line, enter input mode)

cc

Equivalent to 0c$ (delete entire line, enter input mode)

X

Equivalent to dl (delete character backwards)

x

Equivalent to dh (delete character forwards)


Most people tend to use D to delete to end of line, dd to delete an entire line, and x (as "backspace") to delete single characters. If you aren't a hardcore vi user, you may find it difficult to make sure the more esoteric deletion commands are at your fingertips.

Every good editor provides "un-delete" commands as well as delete commands, and vi-mode is no exception. vi-mode maintains a delete buffer that stores all of the modifications to text on the current line only (note that this is different from the full vi editor). The command u undoes previous text modifications. If you type u, it will undo the last change. Typing it again will undo the change before that. When there are no more undo's, bash will beep. A related command is . (dot), which repeats the last text modification command.

There is also a way to save text in the delete buffer without having to delete it in the first place: just type in a delete command but use y ("yank") instead of d. This does not modify anything, but it allows you to retrieve the yanked text as many times as you like later on. The commands to retrieve yanked text are p, which inserts the text on the current line to the right of the cursor, and P, which inserts it to the left of the cursor. The y, p, and P commands are powerful but far better suited to "real vi" tasks like making global changes to documents or programs than to shell commands, so we doubt you'll use them very often.

2.4.4. Moving Around in the History List

The next group of vi control mode commands we cover allows you to move around in and search your command history. This is the all-important functionality that lets you go back and fix an erroneous command without retyping the entire line. These commands are summarized in Table 2-12.

Table 2-12. vi control mode commands for searching the command history

Command

Description

k or -

Move backward one line

j or +

Move forward one line

G

Move to line given by repeat count

/string

Search backward for string

?string

Search forward for string

n

Repeat search in same direction as previous

N

Repeat search in opposite direction of previous


The first two can also be accomplished with the up and down cursor movement keys if your keyboard has them. The first three can be preceded by repeat counts (e.g., 3k or 3- moves back three lines in the command history).

If you aren't familiar with vi and its cultural history, you may be wondering at the wisdom of choosing such seemingly poor mnemonics as h, j, k, and l for backward character, forward line, backward line, and forward character, respectively. Well, there actually is a rationale for the choices—other than that they are all together on the standard keyboard. Bill Joy originally developed vi to run on Lear-Siegler ADM-3a terminals, which were the first popular models with addressable cursors (meaning that a program could send an ADM-3a command to move the cursor to a specified location on the screen). The ADM-3a's h, j, k, and l keys had little arrows on them, so Joy decided to use those keys for appropriate commands in vi. Another (partial) rationale for the command choices is that CTRL-H is the traditional backspace key, and CTRL-J denotes linefeed.

Perhaps + and - are better mnemonics than j and k, but the latter have the advantage of being more easily accessible to touch typists. In either case, these are the most basic commands for moving around the history list. To see how they work, let's use the same examples from the emacs-mode section earlier.

You enter the example command (RETURN works in both input and control modes, as does LINEFEED or CTRL-J):

$ fgrep -l Duchess < ~cam/book/alice_in_wonderland

but you get an error message saying that your option letter was wrong. You want to change it to -s without having to retype the entire command. Assuming you are in control mode (you may have to type ESC to put yourself in control mode), you type k or - to get the command back. Your cursor will be at the beginning of the line:

$ [f]grep -l Duchess < ~cam/book/alice_in_wonderland

Type w to get to the -, then l to get to the l. Now you can replace it by typing rs; press RETURN to run the command.

Now let's say you get another error message, and you finally decide to look at the manual page for the fgrep command. You remember having done this a while ago today, so rather than typing in the entire man command, you search for the last one you used. To do this, type ESC to enter control mode (if you are already in control mode, this will have no effect), then type / followed by man or ma. To be on the safe side, you can also type ^ma; the ^ means match only lines that begin with ma.[7]

[7] Fans of vi and search utilities like grep should note that caret (^) for beginning-of-line is the only context operator vi-mode provides for search strings.

But typing /^ma doesn't give you what you want: instead, the shell gives you:

$ make myprogram

To search for "man" again, you can type n, which does another backward search using the last search string. Typing / again without an argument and hitting RETURN will accomplish the same thing.

The G command retrieves the command whose number is the same as the numeric prefix argument you supply. G depends on the command numbering scheme described in Chapter 3 Section 3.4.2.3. Without a prefix argument, it goes to command number 1. This may be useful to former C shell users who still want to use command numbers.

2.4.5. Character-Finding Commands

There are some additional motion commands in vi-mode, although they are less useful than the ones we saw earlier in the chapter. These commands allow you to move to the position of a particular character in the line. They are summarized in Table 2-13, in which x denotes any character.

All of these commands can be preceded by a repeat count.

Table 2-13. vi-mode character-finding commands

Command

Description

fx

Move right to next occurrence of x

Fx

Move left to previous occurrence of x

tx

Move right to next occurrence of x, then back one space

Tx

Move left to previous occurrence of x, then forward one space

;

Redo last character-finding command

,

Redo last character-finding command in opposite direction


Starting with the previous example: let's say you want to change Duchess to Duckess. Make sure that you're at the end of the line (or, in any case, to the left of the h in Duchess); then, if you type Fh, your cursor will move to the h:

$ fgrep -l Duc[h]ess < ~cam/book/alice_in_wonderland

At this point, you could type r to replace the h with k. But let's say you wanted to change Duchess to Dutchess. You would need to move one space to the right of the u. Of course, you could just type l. But, given that you're somewhere to the right of Duchess, the fastest way to move to the c would be to type Tu instead of Fu followed by l.

As an example of how the repeat count can be used with character-finding commands, let's say you want to change the filename from alice_in_wonderland to alice. In this case, assuming your cursor is still on the D, you need to get to one character beyond the second slash. To do this, you can type 2fa. Your cursor will then be on the a in alice_in_wonderland.

The character-finding commands also have associated delete commands. Read the command definitions in the previous table and mentally substitute "delete" for move. You'll get what happens when you precede the given character-finding command with a d. The deletion includes the character given as argument. For example, assume that your cursor is under the a in alice_in_wonderland:

$ fgrep -l Duchess < ~cam/book/[a]lice_in_wonderland

If you want to change alice_in_wonderland to natalie_in_wonderland, one possibility is to type dfc. This means "delete right to next occurrence of c," i.e., delete "alic". Then you can type i (to enter input mode) and then "natali" to complete the change.

One final command rounds out the vi control mode commands for getting around on the current line: you can use the pipe character (|) to move to a specific column, whose number is given by a numeric prefix argument. Column counts start at 1; count only your input, not the space taken up by the prompt string. The default repeat count is 1, of course, which means that typing | by itself is equivalent to 0 (see Table 2-8).

2.4.6. Textual Completion

Although the character-finding commands and | are not particularly useful, vi-mode provides one additional feature that we think you will use quite often: textual completion. This feature is not part of the real vi editor, and it was undoubtedly inspired by similar features in emacs and, originally, in the TOPS-20 operating system for DEC mainframes.

The rationale behind textual completion is simple: you should have to type only as much of a filename, user name, function, etc. as is necessary. Backslash (\) is the command that tells bash to do completion in vi-mode. If you type in a word, hit ESC to enter control mode, and then type \, one of four things will happen; they are the same as for TAB in emacs-mode:

  1. If there is nothing whose name begins with the word, the shell will beep and nothing further will happen.

  2. If there is a command name in the search path, a function name, or a filename that the string uniquely matches, the shell will type the rest of it, followed by a space in case you want to type in more command arguments. Command name completion is only attempted when the word is in a command position (e.g., at the start of a line).

  3. If there is a directory that the string uniquely matches, the shell will complete the filename, followed by a slash.

  4. If there is more than one way to complete the name, the shell will complete out to the longest common prefix among the available choices. Commands in the search path and functions take precedence over filenames.

A related command is *. It behaves similarly to ESC-\, but if there is more than one completion possibility (number four in the previous list), it lists all of them and allows you to type further. Thus, it resembles the * shell wildcard character.

Less useful is the command =, which does the same kind of expansion as *, but in a different way. Instead of expanding the names onto the command line, it prints them, then gives you your shell prompt back and retypes whatever was on your command line before you typed =. For example, if the files in your directory include tweedledee.c and tweedledum.c, and you type tweedl followed by ESC and then =, you will see this:

$ cc tweedl

tweedledee.c tweedledum.c

It is also possible to expand other environment entities, as we saw in emacs-mode. If the text being expanded is preceded by a dollar sign ($), the shell will attempt to expand the name to that of a shell variable. If the text is preceded by a tilde (~), expansion to a username is attempted; if preceded by an at sign (@), a hostname.

2.4.7. Miscellaneous Commands

Several miscellaneous commands round out vi-mode; some of them are quite esoteric. They are listed in Table 2-14.

Table 2-14. Miscellaneous vi-mode commands

Command

Description

~

Invert (twiddle) case of current character(s)

-

Append last word of previous command, enter input mode

CTRL-L

Clear the screen and redraw the current line on it; good for when your screen becomes garbled

#

Prepend # (comment character) to the line and send it to the history list; useful for saving a command to be executed later without having to retype it[8]


[8] The line is also "executed" by the shell. However, # is the shell's comment character, so the shell ignores it.

The first of these can be preceded by a repeat count. A repeat count of n preceding the ~ changes the case of the next n characters. The cursor will advance accordingly.

A repeat count preceding _ causes the nth word in the previous command to be inserted in the current line; without the count, the last word is used. Omitting the repeat count is useful because a filename is usually the last thing on a UNIX command line, and because users often run several commands in a row on the same file. With this feature, you can type all of the commands (except the first) followed by ESC-_, and the shell will insert the filename.

    Previous Section  < Day Day Up >  Next Section