CONTENTS

Chapter 17. vi Tips and Tricks

17.1 The vi Editor: Why So Much Material?

We're giving a lot of pages to the vi editor. People who use another editor, like Emacs, might wonder why. Here's why.

I've watched people (including myself) learn and use vi for 20 years. It's the standard editor that comes with almost every Unix system these days, but most people have no idea that vi can do so much. People are surprised, over and over, when I show them features that their editor has. Even with its imperfections, vi is a power tool. If you work with files, you probably use it constantly. Knowing how to use it well will save you lots of time and work.

But why not give the same coverage to another editor that lots of people use: GNU Emacs (Section 19.1)? That's because GNU Emacs comes with source code and can be extended by writing LISP code. Its commands have descriptive names that you can understand by reading through a list. vi's commands are usually no more than a few characters long; many of the option names are short and not too descriptive either. Lots of Unix systems don't even have vi source code these days.

I hope that you vi users will learn a lot in this section and that people who don't use vi will at least browse through to see some of vi's less obvious features.

If you're looking for additional text-editing power, you can use vim instead of the plain vanilla vi installed on most systems. All vi commands work with vim, but with added functionality, power, and more standardized behavior accross flavors of Unix. There should be an installation of vim for your Unix.

—JP and SP

17.2 What We Cover

Summary Box

When you type a : (colon) command in vi, you're beginning an ex command. There's more information about ex in a later chapter: Section 20.3, Section 20.4, and Section 20.5.

— EK

17.3 Editing Multiple Files with vi

ex commands enable you to switch between multiple files. The advantage is speed. When you are sharing the system with other users, it takes time to exit and re-enter vi for each file you want to edit. Staying in the same editing session and traveling between files is not only faster for access, but you also save abbreviations and command sequences that you have defined, and you keep yank buffers (Section 17.4) so that you can copy text from one file to another.

When you first invoke vi, you can name more than one file to edit and then use ex commands to travel between the files:

% vi file1 file2

This edits file1 first. After you have finished editing the first file, the ex command :w writes (saves) file1, and :n calls in the next file (file2). You can type :wn both to save the current file changes and to go to the next file. Typing :q! discards changes and closes the current file. Type vi * to edit all the files in a directory, though this will give you an error in some Unix systems. Type CTRL-g or :f to get the name of your current file; :args lists all filenames from the command line and puts brackets around the [current] file.

You can also switch at any time to another file not specified on the command line with the ex command :e. If you want to edit another file within vi, you first need to save your current file (:w), then you can type the following command:

:e  filename 

vi "remembers" two filenames at a time as the current and alternate filenames. These can be referred to by the symbols % (current filename) and # (alternate filename).

# is particularly useful with :e, since it allows you to switch back and forth between two files easily. The command :e# is always "switch to the other one." With different flavors of Unix, the vi command CTRL-^ (control-caret) is a synonym for :e#. This usually seems to work even without pressing the SHIFT key. For instance, if I get a caret by pressing SHIFT-6, I don't need to press CTRL-SHIFT-6 to make vi change files: just CTRL-6 is enough.

If you have not first saved the current file, vi will not allow you to switch files with :e or :n unless you tell it imperatively to do so by adding an exclamation point after the command.

The command:

:e!

is also useful. It discards your edits and returns to the last saved version of the current file.

In contrast to the # symbol, % is useful mainly in shell escapes (Section 17.21) and when writing out the contents of the current buffer to a new file. For example, you could save a second version of the file letter with the command:

:w %.new

instead of:

:w letter.new

— LL and SP

17.4 Edits Between Files

When you give a yank buffer (temporary holding buffer) a one-letter name, you have a convenient way to move text from one file to another. Named buffers are not cleared when a new file is loaded into the vi buffer with the :e command (Section 17.3). Thus, by yanking (copying) or deleting text from one file (into multiple named buffers if necessary), calling in a new file with :e and putting the named buffer into the new file, you can transfer material between files.

The following table illustrates how to transfer text from one file to another. Type the keystrokes exactly as shown to achieve the stated result.

Keystrokes

Action

Results

"f4yy

Yank four lines into buffer f.

With a screen editor you can scroll
the page, move the cursor, delete lines,
insert characters, and more, while seeing
the results of the edits as you make them

:w

Save the file.

"practice" 6 lines 238 characters

:e letter

Enter the file letter with :e. Move cursor to where the copied text will be placed.

Dear Mr.
Henshaw:
I thought that you would
be interested to know that:
Yours truly,

"fp

Place yanked text from named buffer f below the cursor.

Dear Mr.
Henshaw:
I thought that you would
be interested to know that:
With a screen editor you can scroll
the page, move the cursor, delete lines,
insert characters, and more, while seeing
the results of the edits as you make them
Yours truly,

If you yank into a buffer and type the buffer name as an uppercase letter, your new text will be added to the text already in the buffer. For example, you might use "f4yy to yank four lines into the buffer named f. If you then move somewhere else and type "F6yy with an uppercase F, that will add six more lines to the same f buffer — for a total of ten lines. You can yank into the uppercase buffer name over and over. To output all of the yanked text, use the lowercase letter — like "fp. To clear the buffer and start over, use its lowercase name ("fy...) again.

— LL and JP

17.5 Local Settings for vi

In addition to reading the .exrc file (the vi configuration or startup file) in your home directory, many versions of vi will read a file called .exrc in the current directory. This allows you to set options that are appropriate to a particular project.

For example, you might want to have one set of options in a directory used mainly for programming:

set number lisp autoindent sw=4 terse
set tags=/usr/lib/tags

and another set of options in a directory used for text editing:

set wrapmargin=15 ignorecase

Note that you can set certain options in the .exrc file in your home directory (Section 1.15) and unset them (for example, set wrapmargin=0 noignorecase) in a local directory.

Many versions of vi don't read .exrc files in the current directory unless you first set the exrc option in your home directory's .exrc file:

set exrc

This mechanism makes it harder for other people to place, in your working directory, an .exrc file whose commands might jeopardize the security of your system.

You can also define alternate vi environments by saving option settings in a file other than .exrc and reading in that file with the :so command. For example:

:so .progoptions

Local .exrc files are also useful for defining abbreviations ( Section 17.23) and key mappings (Section 18.2). When we write a book or manual, we save all abbreviations to be used in that book in an .exrc file in the directory in which the book is being created.

You can also store settings and startup commands for vi and ex in an environment variable called EXINIT (Section 17.27). If there is a conflict between settings in EXINIT and an .exrc file, EXINIT settings take precedence.

You can keep a group of standard .exrc files in a central directory and link (Section 10.5) to them from various local directories. For instance, from this book's source-file directory, which is full of SGML files, I made a symlink:

% ln -s ~/lib/vi/exrc.sgml .exrc

I prefer symbolic links to hard links in a case like this because they make it easy to see to which central file the local .exrc link points.

— TOR

17.6 Using Buffers to Move or Copy Text

In a vi editing session, your last deletion (d or x) or yank (y) is saved in a buffer. You can access the contents of that buffer and put the saved text back in your file with the put command (p or P). This is a frequent sequence of commands:

5dd   delete 5 lines
       . . . move somewhere else

p     put the 5 deleted lines back in a new
      location, below the current line

Fewer new users are aware that vi stores the last nine (Section 17.7) deletions in numbered buffers. You can access any of these numbered buffers to restore any (or all) of the last nine deletions. (Small deletions, of only parts of lines, are not saved in numbered buffers, however.) Small deletions can be recovered only by using the p or P command immediately after you've made the deletion.

vi also allows you to yank (copy) text to "named" buffers identified by letters. You can fill up to 26 (a-z) buffers with yanked text and restore that text with a put command at any time in your editing session. This is especially important if you want to transfer data between two files, because all buffers except those that are named are lost when you change files. See Section 17.4.

— TOR

17.7 Get Back What You Deleted with Numbered Buffers

Being able to delete large blocks of text in a single bound is all very well and good, but what if you mistakenly delete 53 lines that you need? There's a way to recover any of your past nine deletions, because they're saved in numbered buffers. The last delete is saved in buffer 1, the second-to-last in buffer 2, and so on.

To recover a deletion, type <"> (the double quote character), identify the buffered text by number, then give the put command. To recover your second-to-last deletion from buffer 2, type the following:

"2p

The deletion in buffer 2 is placed on the line below the cursor.

If you're not sure which buffer contains the deletion you want to restore, you don't have to keep typing <">np over and over again. If you use the repeat command (.) with p after u (undo), it automatically increments the buffer number. As a result, you can search through the numbered buffers as follows:

"1pu.u.u etc.

to put the contents of each succeeding buffer in the file one after the other. Each time you type u, the restored text is removed; when you type a dot (.), the contents of the next buffer is restored to your file. Keep typing u and . until you've recovered the text you're looking for.

— TOR

17.8 Using Search Patterns and Global Commands

Besides using line numbers and address symbols (., $, %), ex (including the ex mode of vi, of course) can address lines (Section 20.3) using search patterns (Section 32.1). For example:

:/ pattern/d

Deletes the next line containing pattern.

:/ pattern/+d

Deletes the line below the next line containing pattern. (You could also use +1 instead of + alone.)

:/ pattern1/,/ pattern2/d

Deletes from the next line (after the current line) that contains pattern1 through the next following line that contains pattern2.

:.,/ pattern/m23

Takes text from current line (.) through the next line containing pattern and puts it after line 23.

Note that patterns are delimited by a slash both before and after.

If you make deletions by pattern with vi and ex, there is a difference in the way the two editors operate. Suppose you have in your file named practice the following lines:

With a screen editor you can scroll the
page, move the cursor, delete lines, insert
characters and more, while seeing results
of your edits as you make them.

Key-strokes

Action

Results

d/while

The vi delete-to-pattern command deletes from the cursor up to the word while but leaves the remainder of both lines.

With a screen editor you can scroll the
page, move the cursor, while seeing results
of your edits as you make them.

:.,/while/d

The ex command deletes the entire range of addressed lines; in this case both the current line and the line containing the pattern. All lines are deleted in their entirety.

With a screen editor you can scroll the
of your edits as you make them.

17.8.1 Global Searches

In vi you use a / (slash) to search for patterns of characters in your files. By contrast, ex has a global command, g, that lets you search for a pattern and display all lines containing the pattern when it finds them. The command :g! does the opposite of :g. Use :g! (or its synonym :v) to search for all lines that do not contain pattern.

You can use the global command on all lines in the file, or you can use line addresses to limit a global search to specified lines or to a range of lines.

:g/ pattern/

Finds (moves to) the last occurrence of pattern in the file.

:g/ pattern/p

Finds and displays all lines in the file containing pattern.

:g!/ pattern/nu

Finds and displays all lines in the file that don't contain pattern; also displays line number for each line found.

:60,124g/ pattern/p

Finds and displays any lines between 60 and 124 containing pattern.

g can also be used for global replacements. For example, to search for all lines that begin with WARNING: and change the first word not on those lines to NOT:

:g/^WARNING:/s/\<not\>/NOT/

— LL, from Learning the vi Editor (O'Reilly, 1998)

17.9 Confirming Substitutions in vi

It makes sense to be overly careful when using a search-and-replace command. It sometimes happens that what you get is not what you expected. You can undo any search-and-replace command by entering u, provided that the command was intended for the most recent edit you made. But you don't always catch undesired changes until it is too late to undo them. Another way to protect your edited file is to save the file with :w before performing a global replacement. Then at least you can quit the file without saving your edits and go back to where you were before the change was made. You can also read back in the previous version of the buffer with :e! (Section 17.3).

It's wise to be cautious and know exactly what is going to be changed in your file. If you'd like to see what the search turns up and confirm each replacement before it is made, add the c option (for confirm) at the end of the substitute command:

:1,30s/his/the/gc

The item to be substituted is highlighted so that placement of the cursor on the first character is marked by a series of carets (^^^^).

copyists at his school
            ^^^_

If you want to make the replacement, you must enter y (for yes) and press RETURN. If you don't want to make a change, simply press RETURN.

The combination of the vi commands, n (repeat last search) and dot (.) (repeat last command), is also an extraordinarily useful and quick way to page through a file and make repetitive changes that you may not want to make globally. So, for example, if your editor has told you that you're using which when you should be using that, you can spot-check every occurrence of which, changing only those that are incorrect.

This often turns out to be faster than using a global substitution with confirmation. It also lets you see other lines near the text you're checking, which is hard to do with :s///c in original vi. vi clones have improved the situation. For instance, in vim, :s///c runs in fullscreen mode; it also lets you type CTRL-y and CTRL-e to scroll the screen up or down to see context before you approve or deny each substitution.

—DD, TOR, from Learning the vi Editor (O'Reilly, 1998)

17.10 Keep Your Original File, Write to a New File

You can use :w to save an entire buffer (the copy of the file you are editing) under a new filename.

Suppose you have a file practice, containing 600 lines. You open the file and make extensive edits. You want to quit but save both the old version of practice and your new edits for comparison. To save the edited buffer in a file called check_me, give the command:

:w check_me

Your old version, in the file practice, remains unchanged (provided that you didn't previously use :w). You can now quit the old version by typing :q.

— LL, from Learning the vi Editor (O'Reilly, 1998)

17.11 Saving Part of a File

While editing, you will sometimes want to save just part of your file as a separate, new file. For example, you might have entered formatting codes and text that you want to use as a header for several files.

You can combine ex line addressing (Section 20.3) with the write command, w, to save part of a file. For example, if you are in the file practice and want to save part of practice as the file newfile, you could enter:

:230,$w newfile

Saves from line 230 to end-of-file in newfile.

:.,600w newfile

Saves from the current line to line 600 in newfile.

After newfile has been created, you'll need w! instead of w.

— LL, from Learning the vi Editor (O'Reilly, 1998)

17.12 Appending to an Existing File

You can use the Unix redirect and append operator (>>) with w to append all or part of the buffer's contents to an existing file. For example, if you entered:

:1,10w  newfile 

and then:

$ Section 20.3

:340,$w >> newfile 

newfile would contain lines 1-10 and line 340 to the end of the buffer.

—TOR, from Learning the vi Editor (O'Reilly, 1998)

17.13 Moving Blocks of Text by Patterns

You can move blocks of text delimited by patterns (Section 17.8). For example, assume you have a 150-page reference manual. All reference pages are organized into three paragraphs with the same three headings: SYNTAX, DESCRIPTION, and PARAMETERS. A sample of one reference page follows:

 .Rh 0 "Get status of named file" "STAT"
 .Rh "SYNTAX"
 .nf
 integer*4 stat, retval
 integer*4 status(11)
 character*123 filename
 ...
 retval = stat (filename, status)
 .fi
 .Rh "DESCRIPTION"
 Writes the fields of a system data structure into the
 status array.
 These fields contain (among other
 things) information about the file's location, access
 privileges, owner, and time of last modification.
 .Rh "PARAMETERS"
 .IP "\fBfilename\fR" 15n
 A character string variable or constant containing
 the Unix pathname for the file whose status you want
 to retrieve.
 You can give the ...

Suppose that it is decided to move the SYNTAX paragraph below the DESCRIPTION paragraph. Using pattern matching, you can move blocks of text on all 150 pages with one command!

:g/SYNTAX/,/DESCRIPTION/-1 mo /PARAMETERS/-1

This command operates on the block of text between the line containing the word SYNTAX and the line just before the word DESCRIPTION (/DESCRIPTION/-1). The block is moved (using mo) to the line just before PARAMETERS (/PARAMETERS/-1). Note that ex can only place text below the line specified. To tell ex to place text above a line, you first have to move up a line with -1 and then place your text below. In a case like this, one command literally saves hours of work. (This is a real-life example — we once used a pattern match like this to rearrange a reference manual containing hundreds of pages.)

Block definition by patterns can be used equally well with other ex commands. For example, if you wanted to delete all DESCRIPTION paragraphs in the reference chapter, you could enter:

:g/DESCRIPTION/,/PARAMETERS/-1d

This very powerful kind of change is implicit in ex's line addressing syntax (Section 20.3), but it is not readily apparent even to experienced users. For this reason, whenever you are faced with a complex, repetitive editing task, take the time to analyze the problem and find out if you can apply pattern-matching tools to do the job.

—TOR, from Learning the vi Editor (O'Reilly, 1998)

17.14 Useful Global Commands (with Pattern Matches)

The best way to learn pattern matching is by example, so here's a short list of pattern-matching examples with explanations. (Section 32.21 has a list of these patterns.) Study the syntax carefully so you understand the principles at work. You should then be able to adapt these examples to your own situation.

  1. Change all occurrences of the word help (or Help) to HELP:

    % Section 20.3

    :%s/[Hh]elp/HELP/g

    or:

    :%s/[Hh]elp/\U&/g

    The \U changes the pattern that follows to all uppercase. The pattern that follows is the repeated search pattern, which is either help or Help.

  2. Replace one or more spaces following a colon (:) or a period (.) with two spaces (here a space is marked by a ·):

    :%s/\([:.]\)··*/\1··/g

    Either of the two characters within brackets can be matched. This character is saved into a hold buffer, using \( and \) (Section 34.11) and restored on the right-hand side by the \1. Note that most metacharacters lose their special meanings inside brackets — so the dot does not need to be escaped with a backslash (\).

  3. Delete all blank lines:

    g Section 20.4

    :g/^$/d

    What you are actually matching here is the beginning of the line (^), followed by the end of the line ($), with nothing in between.

  4. Delete all blank lines, plus any lines that contain only whitespace:

    :g/^[ ·tab ]*$/d 

    (In the previous line, a TAB character is shown as tab.) A line may appear to be blank, but may in fact contain spaces or tabs. The previous numbered example will not delete such a line. This example, like the previous one, searches for the beginning and end of the line. But instead of having nothing in between, the pattern tries to find any number of spaces or tabs. If no spaces or tabs are matched, the line is blank. To delete lines that contain whitespace but that aren't blank, you would have to match lines with at least one space or tab:

    :g/^[ ·tab ][ ·tab ]*$/d 
  5. This example and the next both refer to a line in a troff-formatted document like this A-level (top-level) heading macro call:

    .Ah "Budget Projections" "for 2001-2002"

    To match the first quoted argument of all section header (.Ah) macros and replace each line with this argument:

    :%s/^\.Ah "\([^"]*\)" .*/\1/

    this example macro call would be changed to simply:

    Budget Projections

    The substitution assumes that the .Ah macro can have more than one argument surrounded by quotes. You want to match everything between quotes, but only up to the first closing quote. As Section 32.18 explains, using ".*" would be wrong because it would match all arguments on the line. What you do is match a series of characters that aren't quotes, [^"]*. The pattern "[^"]*" matches a quote, followed by any number of nonquote characters, followed by a quote. Enclose the first argument in \( and \) so that it can be replayed using \1.

  6. Same as previous, except preserve the original lines by copying them:

    :g/^\.Ah/t$ | s/\.Ah "\([^"]*\)" .*/\1/

    In ex, the vertical bar (|) is a command separator that works like a semicolon (;) (Section 28.16) on a Unix command line. The first part, :g/^\.Ah/t$, matches all lines that begin with a .Ah macro, uses the t command to copy these lines, and places the copies after the last line ($) of the file. The second part is the same as in the previous example, except that the substitutions are performed on copies at the end of the file. The original lines are unchanged.

—TOR and DG , from Learning the vi Editor (O'Reilly, 1998)

17.15 Counting Occurrences; Stopping Search Wraps

Want to see how many times you used the word very in a file? There are a couple of easy ways.

First, tell vi to stop searching when you get to the end of the file. Type the command :set nowrapscan or put it in your .exrc file (Section 17.30).

  1. Move to the top of the file with the 1G command. Search for the first very with the command /very (HINT: using the word-limiting regular expression /\<very\> (Section 32.12) instead will keep you from matching words like every). To find the next very, type the n (next) command.

    When vi says Address search hit BOTTOM without matching pattern, you've found all of the words.

  2. Use the command:

    :g/very/p

    The matching lines will scroll down your screen.

To find the line numbers, too, type :set number before your searches.

— JP

17.16 Capitalizing Every Word on a Line

Are you typing the title of an article or something else that needs an uppercase letter at the start of every word? Do you need to capitalize some text that isn't? It can be tedious to press the SHIFT key as you enter the text or to use ~ (tilde) and w commands to change the text. The following command capitalizes the first character of every word.

:s/\<./\u&/g

(You might be wondering why we didn't use :s/\<[a-z]/\u&/g to match lowercase letters. The <. actually matches the first character of every word, but the \u will only affect letters. So, unless you only want to capitalize certain letters, <. is enough.)

The previous example does only the current line. You can add a range of lines after the colon. For example, to edit all lines in the file, type the following:

:%s/\<./\u&/g

To do the current line and the next five, use this:

:.,+5s/\<./\u&/g

To make the first character of each word uppercase (with \u) and the rest lowercase (with \L), try:

\(...\)...\1 Section 32.21

:s/\<\(.\)\([A-Za-z]*\)\>/\u\1\L\2/g

The previous command doesn't convert the back ends of words with hyphens (like CD-ROM) or apostrophes (like O'Reilly) to lowercase. That's because [A-Za-z]*\> only matches words whose second through last characters are all letters. You can add a hyphen or an apostrophe to make that expression match more words, if you'd like.

Those commands can be a pain to type. If you use one of them a lot, try putting it in a keymap (Section 18.2).

— JP

17.17 Per-File Setups in Separate Files

Do you need to set certain editor options for certain files — but not use the same setup for every file you edit? Make a special setup file with the same name and an underscore ( _ ) or an extension like .vi, .ex, or .so at the end. For instance, a file named report could have a corresponding setup file named report_ or report.so. (You don't have to use an underscore at the end of the filename. It's convenient, though, because it's not a shell special character (Section 27.17).)

The setup file has the same format as a .exrc file (Section 17.5). To make the editor read it, map (Section 18.2) a function key like F1 (or any other key sequence):

source Section 20.4, ^[Section 18.6

map #1 :source %_^[

When you start vi, tap that key to read the setup file. (The percent sign stands for the current filename (Section 17.3).)

If you want to use the same setup file for several files in a directory, you might want to make hard links (Section 10.4) between them. That will save disk space. It also means that if you decide to change a setup option, you can edit one of the links to the setup file, and the others will have the same change.

— JP

17.18 Filtering Text Through a Unix Command

When you're editing in vi, you can send a block of text as standard input to a Unix command. The output from this command replaces the block of text in the buffer.

In vi, you can filter text through a Unix command by typing an exclamation mark (!) followed by any of vi's movement keystrokes that indicate a block of text and then by the Unix command line to be executed. For example:

!) command 

will pass the next sentence through command.

There are a couple of unusual features about how vi acts when you use this structure:

As another example, assume you have a portion of text in a message that you'd like to convert to all uppercase letters. ex has operators to convert case (Section 17.16), but it's also easy to convert case with the tr (Section 21.11) command. In this example, the second sentence is the block of text that will be filtered to the command:

One sentence before.
With a screen editor you can scroll the page
move the cursor, delete lines, insert characters,
and more, while seeing the results of your edits
as you make them.
One sentence after.

Keystrokes

Action

Results

!)

An exclamation mark appears on the last line to prompt you for the Unix command.

One sentence after.
~
~
~
!_
tr '[a-z]' '[A-Z]'

Enter the Unix command, and press RETURN. The input is replaced by the output.

One sentence before.
WITH A SCREEN EDITOR YOU CAN SCROLL THE PAGE
MOVE THE CURSOR, DELETE LINES, INSERT CHARACTERS,
AND MORE, WHILE seeING THE RESULTS OF YOUR EDITS
AS YOU MAKE THEM.
One sentence after.

To repeat the previous command, the syntax is as follows:

! object !

It is sometimes useful to send sections of a coded document to nroff to be replaced by formatted output. Remember that the "original" input is replaced by the output. Fortunately, if there is a mistake, such as an error message being sent instead of the expected output, you can undo the command and restore the lines.

Sometimes a filter-through on old, buggy versions of vi can completely scramble and trash your text. Things can be so bad that the u (undo) command won't work. If you've been burned this way before, you'll want to write your buffer (with :w) before filter-throughs. This doesn't seem to be a problem with modern versions, but be aware of it.

— TOR

17.19 vi File Recovery Versus Networked Filesystems

Have you ever used the vi -r command to recover a file? It lets you get a file back that you were editing when the system crashed or something else killed your editor before you could save. The system may send you an email message something like this:

Date: Thu, 19 Nov 1999 09:59:00 EST
To: jerry

A copy of an editor buffer of your file "afile"
was saved when the system went down.
This buffer can be retrieved using the "recover" command of the editor.
An easy way to do this is to give the command "vi -r afile".
This works for "edit" and "ex" also.

figs/bomb.gif Section 17.20

Your files are saved under a directory named something like /usr/preserve. Follow the instructions and you'll get back your file, more or less the way it was when you lost it.

If your computers have networked filesystems, such as NFS, there's a wrinkle in the way that vi -r works. It may only work right on the specific computer where you were editing a file. For example, if you're editing the file foo on the host named artemis and it crashes, you may not be able to log on to another host and do vi -r foo to recover that file. That's because, on many hosts, temporary files (like editor buffers) are stored on a local filesystem instead of on the networked (shared) filesystems. On this kind of system, you may need to log on to artemis to recover your lost editor buffer.

If you don't remember which computer you were using when the file was lost, check the "Received:" lines in the email message header;[1] they'll often show from which machine the message originally came. Also, if you don't remember what files are saved on a machine, you can usually get a list of your saved files by typing vi -r without a filename:

% vi -r
/var/preserve/jerry:
On Wed Jul 17 at 08:02 saved 15 lines of file "/u/jerry/Mail/drafts/1"
On Sun Aug 25 at 18:42 saved 157 lines of file "doit"
/tmp:
No files saved.

Don't wait too long. Many Unix systems remove these saved editor buffers every month, week, or sooner.

— JP

17.20 Be Careful with vi -r Recovered Buffers

Usually, when you're editing a file with vi, if you type the command ZZ, it saves your file. But if you recover a file with vi -r (Section 17.19), typing ZZ may not save your edits!

That might be a good thing. When you recover a buffer, you need to decide whether the recovered buffer is really what you want. Maybe you've made other changes to the file since then. Maybe something went wrong as the buffer was being saved (say, the system crashed). You shouldn't just save without checking first.

You can use the :w! command to write the recovered version after you're sure that you want it. Use the :q! command if you don't want the recovered version.

Another good choice is to write the recovered buffer using a different filename, then compare the recovered buffer to the original file. For example, here I recover a draft MH email message and immediately write it to a file named recovered-9 in my tmp directory. Then I use a shell escape (Section 17.21) to run diff (Section 11.1) and compare the draft file on disk (/home/jerry/Mail/drafts/9) with the copy of the recovered buffer that I just wrote (/home/jerry/tmp/recovered-9); the vi current filename % and alternate filename # shortcuts (Section 17.3) are handy here. Oops: diff shows that the recovered version has replaced the last three lines of the message on disk, in the recovered version, with more than 2,000 lines of junk!

less Section 12.3

% vi -r /home/jerry/Mail/drafts/9 
     ...recovered file appears...
:w ~/tmp/recovered-9 
/home/jerry/tmp/recovered-9: 55 lines, 168767 characters.
:!diff % # | less 
!diff /home/jerry/Mail/drafts/9 /home/jerry/tmp/recovered-9 | less
5c5
< Subject: Re: Two more Unix Power Tools questions
---
> Subject: Next UPT (was: Re: Two more Unix Power Tools questions)
146,148c146,2182
< Yes, you mentioned it once.  Thanks for pointing that out, Greg.
< I think the next job is to review all the articles in that chapter
< to be sure which items should be included -- just the articles, or
---
> Yes, you^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
> ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
> ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
     ...zillions of lines of junk...

At this point, the best thing to do is to quit vi immediately (with :q!). Then fix up the original file by copying and pasting the good text from the copy of the recovered buffer that I just wrote. (You might want to rerun diff, outside of vi, to remind yourself which parts of the recovered file you want to transfer to the original file.) Starting a new vi session with the filenames of both the original file and the (mostly trashed) recovered buffer, as Section 17.4 explains, can make the recovery job easier.

— JP

17.21 Shell Escapes: Running One UnixCommand While Using Another

Some Unix commands (usually interactive commands like vi) let you run another Unix command temporarily. To do that, you type a special command character — usually an exclamation point (!) — then type the Unix command line you want to run. In this article, I'll show examples for the vi editor. To see if this works on another utility, check its documentation or just try typing !Unixcommand when the utility is waiting for you to type a command.

You can run any Unix command without quitting vi. That's handy, for example, if you want to read your mail or look at some other file . . . , then go back to the file you were editing without losing your place. It's called a "shell escape." (By the way, there's a another way to do this, job control (Section 23.3), that works on most Unix systems. Job control is often more convenient and flexible than shell escapes.)

Let's say you're editing the file named foo and you need to run grep to get someone's phone number from your phone file. The steps are as follows:

  1. Be sure you're in command mode (press the ESC key if you aren't sure).

  2. If you want to run a command that needs the file you're editing, remember to write out your vi buffer with the :w command. (So you probably wouldn't need to write anything before the following grep command.) Type :! followed by the Unix command, then press RETURN. For example:

    :!grep tim ~/phone
  3. The grep program will run. When it finishes, vi will say:

    [Hit return to continue]
  4. After you press RETURN, you'll be right back where you were.

Other examples:

:!less afile

Page through afile on your screen.

:!rcsdiff %

Give this file to the rcsdiff (Section 11.3) program to see what you've changed since the file was checked out of the archive. vi replaces % with the name of the file you're editing now (Section 17.3).

:!mail

Read your mail. Be careful about this if you were already running the mail program and you used the command ~v to edit a message with vi from inside the mail program. This shell escape starts a subshell (Section 24.4); it will not take you back to the same mail session before you started editing!

:sh

Start a completely new shell. (If you are using a shell with job control, you'll almost always want to use job control to suspend vi temporarily instead (Section 23.6). Press CTRL-z, or use the ex command :suspend.)

Basically, anything you can do at a shell prompt, you can do with a shell escape. You'll be in a subshell though, not your original login shell. So commands like cd won't affect the program where you started the subshell or any other shell. On the bright side, changing directories or resetting anything in your environment won't affect vi or the shell where you started vi. Terminating the program you're running in the subshell will bring you right back where you were.

— JP

17.22 vi Compound Searches

You probably know that you can search for a word or phrase with the vi / (slash) command:

/treasure

If you have a file that uses the same word over and over again, you might want to find one particular place that the word is used. You can repeat the search with the n command until you find the place you want. That can take time and effort, though.

For example, suppose you want to find the word "treasure" in the sentence that has words like "Los Alamos residents . . . treasure," but you can't remember exactly how the sentence is written. You could use wildcards in your regular expression:

/Los Alamos.*treasure

but then the phrases "Los Alamos" and "treasure" have to be on the same line of the file you're searching — and they won't always be. Also, you want your cursor on the word treasure, but that search would put the cursor on Los instead.

"Hmmm," you say, "How about two separate searches, like this?"

/Los Alamos
/treasure

The problem there is that the file might have the phrase "Los Alamos" all throughout it; you might have to type n over and over until you get to the sentence with treasure.

Here's the easy way: a compound search. Say your cursor is on line 1 of the following file:

Before the second World War, there was a treasured boys' school in
what was to become the city of Los Alamos, New Mexico. The school at
Los Alamos changed the lives and made a lifelong impression on most boys
who attended. One of the boys who attended the Los Alamos school went on
to propose that remote set of mesas as a site for the U.S. Government's
   ...
Since the war ended, most of the boys' school ranch buildings have been torn
down or replaced. But there's one building that Los Alamos residents still
use and treasure. It's The Lodge, a log building on the edge of what's now
   ...

Type the command:

/Los Alamos/;/treasure/

That means "find the first occurrence of treasure just after Los Alamos." Starting at the top of the previous example, that search will skip past all the treasure and Los Alamos words until it finds the word treasure on the last line shown. (It's probably smarter to type just /Alamos/;/treasure/ in case Los Alamos is split across two lines of the file.)

Another example: a C programmer wants to find the printf function call just after the line where i is incremented by two (i += 2). She could type:

/i += 2/;/printf/

You can't repeat a compound search by typing n. The easiest way is to define the search as a key map Section 18.2):

^M Section 18.6

:map #3 /Los Alamos/;/treasure/^M

and repeat the search with (in this case) your F3 function key.

— JP

17.23 vi Word Abbreviation

You can define abbreviations that vi will automatically expand into the full text whenever it's typed during text-input mode. To define an abbreviation, use the ex command:

:ab  abbr phrase 

abbr is an abbreviation for the specified phrase. The sequence of characters that make up the abbreviation will be expanded during text-input mode only if you type it as a full word; abbr will not be expanded within a word. [I abbreviate Covnex to Convex, my company's name, because I have dyslexic fingers. -- TC]

Suppose you want to enter text that contains a frequently occuring phrase, such as a difficult product or company name. The command:

:ab ns the Nutshell Handbook

abbreviates the Nutshell Handbook to the initials ns. Now whenever you type ns as a separate word during text-input mode, ns expands to the full text.

Abbreviations expand as soon as you press a nonalphanumeric character (e.g., punctuation), a carriage return, or ESC (returning to command mode).[2] When you are choosing abbreviations, choose combinations of characters that don't ordinarily occur while you are typing text. If you create an abbreviation that ends up expanding in places where you don't want it to, you can disable the abbreviation by typing:

:unab  abbr 

To list your currently defined abbreviations, type:

:ab

The characters that compose your abbreviation cannot appear at the end of your phrase. For example, if you issue the command:

:ab PG This movie is rated PG

you'll get the message No tail recursion, and the abbreviation won't be set. The message means that you have tried to define something that will expand itself repeatedly, creating an infinite loop. If you issue the command:

:ab PG the PG rating system

you may or may not produce an infinite loop, but in either case you won't get a warning message. For example, when the previous command was tested on a System V version of Unix, the expansion worked. On a Berkeley version, though, the abbreviation expanded repeatedly, like this:

the the the the the ...

until a memory error occurred and vi quit. We recommend that you avoid repeating your abbreviation as part of the defined phrase.

—DD and DG, from Learning the vi Editor (O'Reilly, 1998)

17.24 Using vi Abbreviations as Commands (Cut and Paste Between vi's)

The vi command ab (Section 17.23) is for abbreviating words. But it's also good for abbreviating ex-mode commands that you type over and over. In fact, for ex-mode commands (commands that start with a colon (:)), abbreviations can be better than keymaps (Section 18.2). That's because you can choose almost any command name; you don't have to worry about conflicts with existing vi commands.

Here's an example. If you have a windowing terminal or more than one terminal, you might have vi sessions running in more than one place. Your system might have a way to transfer text between windows, but it can be easier to use files in /tmp — especially for handling lots of text. (If your text is confidential and your umask (Section 49.4) isn't set to make new files unreadable by other users, try using a more private directory.) Here are some abbreviations from my .exrc (Section 17.30) file:

figs/www.gif Go to http://examples.oreilly.com/upt3 for more information on: exrc

ab aW w! /tmp/jerry.temp.a
ab aR r /tmp/jerry.temp.a
ab bW w! /tmp/jerry.temp.b
ab bR r /tmp/jerry.temp.b
   ...

I use those abbreviations this way. To write the current and next 45 lines to temporary file a, I type this command in one vi session:

:.,+45 aW

To read those saved lines into another vi session, I use:

:aR

You can do the same thing in a single vi session by using named buffers (Section 17.4), but temporary files are the only method that works between two separate vi sessions.

— JP

17.25 Fixing Typos with vi Abbreviations

Abbreviations (Section 17.23) are a handy way to fix common typos. Try a few abbreviations like this:

ab teh the
ab taht that

in your .exrc (Section 17.5) file.

Any time you find yourself transposing letters or saying, "Darn, I always misspell that word," add an abbreviation to .exrc. (Of course, you do have to worry about performance if the file gets too big.)

You may be able to enforce conventions this way. For example, command names should be surrounded by <command> tags, so creating a list of abbreviations like this:

ab vi <command>vi</command>

saves us from having to type lots of SGML codes.

(Depending on your version of vi, this abbreviation may be recursive (Section 17.23) because the vi is sandwiched between other nonalphanumeric characters. nvi repeated the <command>) quite a few times and quit, but vim did what we wanted.)

—TOR and JP

17.26 vi Line Commands Versus Character Commands

[Quite a few vi users understand how to build vi commands with the (number)(command)(text object) model. But not too many people understand the difference between line commands and character commands. This article explains that and gives some examples. — JP]

The _ (underscore) command is very similar to the ^ (caret) command in that it moves to the first nonblank character of the current line. The key difference is that _ is a line command while ^ is a character command. This is important for all functions that read an "address" — for example, d, y, and c.

In fact, delete, yank, and so on all call a common internal routine in vi to get an "address." If the address is of a particular character, vi does a character-mode delete or yank or whatever. If it is a line address, vi does a line-mode operation. The "address" command may be any of the regular positioning commands (e.g., W, b, $, or /pattern/) or the original character repeated (as in dd or yy).

Some examples are found in Table 17-1.

Table 17-1. Examples of vi character and line commands

Keystrokes

Results

dd

Deletes the current line.

d'a

Deletes all lines between the current line and the line containing mark a, inclusive.

d'a

Deletes all characters between the current character and the character at mark a. This works much like an Emacs W in that the two endpoints are considered to be between two characters. Note that a character-oriented delete may delete newlines.

c/accord/

Changes all characters (not lines!) between the current character up to but not including the a in accord. (However, see the following Note.)

c?accord?

Changes all characters between the current character and the accord, including the word accord.

yj

Yanks two lines: the current line and the one below.

yH

Yanks all the lines from the top of the screen to the current line, inclusive.

<G

Unindents or "dedents" the lines between the current line and the last line, inclusive. (The variable shiftwidth determines the amount of dedenting.) Note that this command turns character addresses into line addresses (so does >).

!}fmt

Runs the lines between the current line and the end of the paragraph through the program fmt (Section 17.28).

 

If you have wrapscan set, a search like c?accord? may wrap from the beginning of the file to the end. This can cause unexpected results and is one reason why I have set nows in my .exrc. Unfortunately, turning off wrapscan breaks tags in many versions of vi.

vi combines the repeat count on the command character with the repeat count on the motion command, so that 2y2j yanks five lines. Interestingly, 2y2_ yanks 4 lines (so does 2y2y) since the _ command moves down (repeat count minus 1) lines. Beware, however, of using repeat counts on all of the motion commands; they're not all implemented in the same way. 4$ moves to the end of the third line below the current; 4 merely moves to the first nonblank character of the current line. | (vertical bar) is a synonym for 0 (zero); given a repeat count, it goes that many characters to the right of the beginning of the line (as if you had typed | (rept-1) l). (Exercise for the reader: why can't you give a repeat count to 0?)

Uppercase letters do different things depending on the command. The exact actions may not always seem sensible, but typically they affect the "current line": D acts like d$; C acts like c$; Y acts like yy. The list must merely be memorized, or you can use a good vi reference guide.

— CT

17.27 Out of Temporary Space? Use Another Directory

vi keeps its temporary copy of the file you're editing in a temporary-file directory — usually /tmp, /usr/tmp, or /var/tmp. If you're editing a big file or if the temporary filesystem runs out of space, vi may not be able to make your temporary file. When that happens, you can use vi's set directory command to set the pathname of a different temporary directory. (If this happens a lot though, you should talk to the system administrator and see if the standard area can be cleaned up or made bigger.)

First, you'll need the absolute pathname (Section 3.7) of a directory on a filesystem with enough room. Use an existing directory, or make a new one.

The vi command is set directory. For example:

set directory=/usr1/jim/vitemp

You have to type that command before giving vi a filename to edit — after that, vi has made the temporary file, and you'll be too late. But if you type that command while using vi and then use the :e command (Section 17.3), all files from then on will use the new temporary directory (in the versions I tried, at least).

To set the directory temporarily, it's probably easiest to add that command to the EXINIT environment variable:

setenv EXINIT 'set directory=/usr1/jim/vitemp'

If you already have a .exrc file (Section 17.5), setting EXINIT will make vi ignore your .exrc file. To make the temporary set directory work, too, use a command with a vertical bar (|), like this:

setenv EXINIT 'source /usr1/jim/.exrc|set directory=/usr1/jim/vitemp'

— JP

17.28 Neatening Lines

Have you made edits that left some of your lines too short or long? The fmt (Section 21.2) utility can clean that up. Here's an example. Let's say you're editing a file (email message, whatever) in vi and the lines aren't even. They look like this:

This file is a mess
with some short lines
and some lines that are too long — like this one, which goes on and on for quite 
a while and etc.

Let's see what 'fmt' does with it.

You put your cursor on the first line and type (in command mode):

5!! Section 17.18

5!!fmt

which means "filter (Section 17.18) 5 lines through fmt." Then the lines will look like this:

This file is a mess with some short lines and some lines that are too
long — like this one, which goes on and on for quite a while and etc.

Let's see what 'fmt' does with it.

This is handiest for formatting paragraphs. Put your cursor on the first line of the paragraph and type (in command mode):

!}fmt

If you don't have any text in your file that needs to be kept as is, you can neaten the whole file at once by typing:

% Section 20.3

:%!fmt

There are a few different versions of fmt, some fancier than others. Most of the articles in Chapter 21 about editing-related tools can be handy too. For example, recomment reformats program comment blocks. cut (Section 21.14) can remove columns, fields, or shorten lines; tr (Section 21.11) can do other transformations. To neaten columns, try filtering through with the setup in Section 21.17. In general, if the utility will read its standard input and write converted text to its standard output, you can use the utility as a vi filter.

— JP

17.29 Finding Your Place with Undo

Often, you're editing one part of a file and need to go to another point to look at something. How do you get back?

You can mark your place with the m command. In command mode, type m followed by any letter. (We'll use x in the example.) Here are the commands to do the job:

m x

Marks current position with x (x can be any letter).

' x

Moves cursor to first character of line marked by x.

` x

Moves cursor to character marked by x.

``

Returns to exact position of previous mark or context after a move.

''

Returns to the beginning of the line of the previous mark or context.

I often find it just as easy to type u to undo my last edit. That pops me right back to the place where I was editing. Then I type u again to restore the edit. Watch out for the new multilevel undo feature in vi clones: typing u twice will undo two edits! (I still use m if I want to mark more than one place.)

— TOR

17.30 Setting Up vi with the .exrc File

You can store commands and settings to be executed any time you start the vi or ex editors (Section 17.2) in .exrc in your home directory. You can modify the .exrc file with the vi editor, just as you can any other text file.

If you don't yet have an .exrc file, simply use vi to create one. Enter into this file the set, ab (Section 17.23), and map (Section 18.2) commands that you want to have in effect whenever you use vi or ex. A sample .exrc file looks like this:

set nowrapscan wrapmargin=7
set sections=SeAhBhChDh nomesg
map q :w^M:n^M
" To swap two words, put cursor at start of first word and type v:
map v dwElp
ab ORA O'Reilly & Associates, Inc.

The ^M characters are RETURNs. Make them by pressing CTRL-v, then RETURN (Section 18.6). Lines that start with a double quote (") are comments. Since the file is actually read by ex before it enters vi, commands in .exrc should not have a preceding colon (:).

In addition to reading the .exrc file in your home directory, vi will read the .exrc file in the current directory. This allows you to set options that are appropriate to a particular project (Section 17.5).

If your .exrc file doesn't seem to be working, watch carefully for error messages just as vi starts, before it clears your screen. If you can't read them quickly enough, start ex instead of vi. The q! command quits ex:

% ex
No tail recursion
:q!

—TOR

[1]  Many email programs hide these header lines from you. You might need to set a "show all header fields" option first.

[2]  An abbreviation won't expand when you type an underscore ( _ ); it's treated as part of the abbreviation.

CONTENTS