------------------------- How to Use VI --------------------------
(Notes for Rice Astro majors)

vi and emacs are the two editors of choice for unix machines. Both are quick, versatile and powerful. Emacs has a few more bells and whistles, while vi is available by default on every unix machine I've seen. Because of its universal availability, I chose to learn vi. Parts of the editor are a bit cryptic, but within a day or two you should be pretty much over most of the learning curve. No pain, no gain. And there is gain - you can really fly through most editing jobs very quickly with vi. On Redhat unix systems there is a command called vimtutor that you might try for an interactive lesson. Of course, many web pages exist for help as well.

You'll avoid most of the confusion surrounding vi if you understand that it uses two modes, command mode, where you tell vi such things as where you want to move in the file, mark your location, delete words, find strings, etc., and insert mode, where vi simply puts whatever you type in as text into your file. To go from insert mode to command mode, hit the escape key (if you are already in command mode, you'll get a beep, no damage done). There are several ways to move the other way, i.e., from command to insert mode. The most common of these are: i or I (insert), a or A (append), o or O (open a new line), s or S (substitute), and R (replace). Sometimes you will hear of a third mode, command line, which are the same as the colon commands described below.

Note: vi starts you out in command mode, so type `a' or `i' to start adding text, and return to command mode by hitting escape. Then save your file by typing :wq (ZZ also works for those who can't type).

Basic Commands

CTL is the control key. CR is a carriage return.

Moving Within a File
The h, j, k, and l commands reflect the arrow keys on those old VT100 terminals, and always
work.  Sometimes the arrow keys also do the same things, depending on your keyboard.
Note that symbols such as $, which mean the end of the line (or file in : mode, see below),
and ^ (beginning of line) are not just for moving in the file, but also
retain their meanings when you define segments of the text to do substitutes (see below).
The editor is a lot like a verbal language in that sense.

h - move to the left                            CTL-f - change display forward a page
j - `jump' down a line                          CTL-b - change display back a page
k - move up a line                              CTL-d - change display down half a page
l - move to the right                           CTL-u - change display up half a page
- - same as k                                   CTL-y - shift display down on screen
+ - same as j                                   CTL-e - shift display up on screen
e - move to the end of a word                   z. - recenter display around cursor
w - move forward to the beginning of a word     z- - recenter display so cursor is at top
b - move backward to the beginning of a word    z+ - recenter display aso cursor is at bottom
$ - move to the end of the line                 zCR - recenter display so cursor is at top
0 - move to the beginning of the line           'm - move to the beginning of the line of mark m
^ - move to the beginning of the line           `m - move to the location of mark m
G - move to the end of the file                  ) - move forward 1 sentence
H - move to the top of the display               ( - move back 1 sentence
M - move to the middle of the display            } - move forward 1 paragraph
L - move to the bottom of the display            { - move back 1 paragraph
B - move back to previous blank space           20| - go to 20th character in the line
E - move ahead to next blank space              CTL-L - clear and redraw
B - move back to previous blank space           CR - same as j
CTL-G - print current location in the file       :22 - move to line 22

6w - move forward 6 words
6b - move backward 6 words
8+ - move down 8 lines
Inserting and Replacing Text
o - open a new line above cursor(*)               O - open a new line below cursor(*)
i - insert text ahead of cursor(*)                I - insert text at the beginning of the line(*)
a - append text after the cursor(*)               A - append text at the end of the line(*)
c$ - change to end of the line (*)                d$ - delete to end of the line
C - same as c$ (*)                                D - same as d$
cG - change to end of the file (*)                dG - delete to end of the file
c0 - change to beginning of file (*)              d0 - delete to beginning of file
cc - change line (*)                              dd - delete line
c'm - change from cursor through mark m (*)       d'm - delete from cursor through mark m
3cc - change 3 lines (*)                          3dd - delete 3 lines
8cw - change next 8 words (*)                     8dw - delete next 8 words
R - overwrite current line, starting at cursor(*) r - replace character at cursor 
s - substitute for character at cursor (*)        8s - substitute for next 8 characters (*)        
S - substitute for entire line (*)                J - join two lines together
. - repeats previous edit command                 xp - transpose two characters
easESC - add a plural, and go back to command mode

(*) - leaves you in insert mode until you hit ESC key
Undo [Important enough to have its own section (at least for me!)]

u - undo previous edit In vim you can do unlimited undo's. To undo the undo, type :redo

Simple Searches

/where    - search forward for the string 'where'
?where    - search backward for the string 'where'
n         - search in the same direction for the same string
N         - search in the reverse direction for the same string
fc        - search for the character 'c' in the current line
;         - find the next occurance of the same character in the current line
,         - same as ; but search backwards in the line
            (f ; and , are not that useful in my opinion)
:abbr ph Pat Hartigan - The abbreviate command. Whenever you type 'ph' followed by a space
                        or punctuation in the file, vi will print 'Pat Hartigan'.  Could be
                        useful I guess.  Or annoying. Such definitions can be put into one's .exrc file.
Simple Substitutions

:g/this/s//that - replace first occurance of `this' with `that' in each line
:g/this/s//that/g - replace `this' with `that' throughout file
Much more extensive searching and substitution capability is available, and is a main reason for using the editor. See below for examples.

Storing the File

:w name  - writes file 'name'
:w! name - overwrites file 'name'
:q       - terminates editing session
:q!      - terminates editing session without storing changes
:wq      - writes changes and quits
ZZ       - same as :wq

More Advanced Commands


mk - record current location as mark k (redefines any previous mark k)
'k - return to line of mark k                `k - return to mark k
d'k - delete to line of mark k              d`k - delete to mark k
c'k - change text to line of mark k         c`k - change text to mark k
"ay'd - yank text into buffer a from cursor through line of mark d
CTL-A - increase the number in the file near the cursor by 1
CTL-X - decrease the number in the file near the cursor by 1
Buffers, Cut and Paste

vi has a total of 27 buffers where you can store text. There is one for each letter of the alphabet, and an `unnamed' buffer, which is where any text you delete goes. Any deleted text pushes the contents of the unnamed buffer into buffer `1'. Anything that was in buffer `1' goes into buffer `2', etc. You access the buffers with the " key. For example, "a means 'buffer a'. To yank 4 lines into buffer k, type "k4yy These lines remain undisturbed in buffer k until you put something else into buffer k, or exit vi.

A few tricks with buffers. To append 4 lines to buffer K that already has some text, type "K4YY You can also store whatever is in your buffers into a new file by typing :w (to write out your current file), then :r newfile, and "kp to put the contents of buffer k into newfile. Note that vi forgets the contents of buffer k if you exit using :wq and then vi newfile.
Note "a means `buffer a'
yy - yank current line to unnamed buffer            "j8yy - yank 8 lines into buffer j
19yy - yank next 19 lines to unnamed buffer         "J8YY - append the next 8 lines into buffer j
p - put unnamed buffer contents after cursor                  p - recover previous edit
P - put unnamed buffer contents before cursor               "1p - recover 2nd previous edit
19dd - delete next 19 lines, and put them in unnamed buffer "7p - recover 8th previous edit
"bp - put the contents of buffer p into current file 
:11,14 ya w - yank lines 11 through 14 into buffer w
:94 pu w - put contents of buffer w after line 94
More Complex Searches and Substitutions, Colon Commands

The vi editor is based on a another editor called ex, and it retains the ex commands through the colon prompt. So, for example, :r is an ex command that expects an argument (e.g. ':r filename') that will read the contents of filename into the document you are editing, while r is simply a vi command that replaces a single character. If you want to just edit in the ex editor you can type :ex, and to return to vi while within ex type :vi.

Probably the most important advanced command is :g. It is powerful, very quick, and once you combine it with s and figure out the syntax, you will be flying through editing sessions. The price to pay is you need to spend time to learn its cryptic syntax. Well worth it.

Let's look at some s commands first, then combine g and s.

The syntax here is
: -- indicates that this is an ex command
2,8 -- a location within the document. In this case lines 2 through 8
      using 2,$ would go from line 2 to the end of the file
      .,5 would mean from the position of the cursor through line 5
      .,'k would mean from the position of the cursor through mark k (which you set previously with m k)
s -- substitute. You are about to look for a string and replace that string with something else
/string1/ -- search for the string between the two /'s. In this case the string is 'string1'. If your
          string contains /, you must escape them with a \. So, to search for '/home/user'
          you would type /\/home\/user/. Same thing for other characters like ., $, ^, &, *, that the
          editor would otherwise think have special meanings.
/string2/ -- If you find the first string, replace it with string2.
/g -- Adding the /g means that if string1 exists more than once on a given line, the editor will replace
      all instances of string1 with string2. Otherwise it only replaces the first one it finds. Sometimes
      replacing only the first occurrence of a string is helpful.

:.,'ms/this/that/g  - replace `this' with `that' throughout file from current line thru mark m
:.,$s/this/that/g   - replace `this' with `that' throughout file from current line thru the end
:33,224s/^/hh/       - insert the string 'hh' at the beginning of lines 33 through 224
:3,14s/$/hh/        - append the string 'hh' at the end of lines 3 through 14

Notice in these last two examples that the ^ and $ characters in the search string refer to the position
in the line, i.e., the beginning or end, while if they were immediately behind the colon they would
refer to the line number in the file. So, for example,


: -- ex command
.,$ -- from where the cursor is located to the end of the file
s  -- substitute
/$/ -- look for the end of the line
/./ -- substitue a .

So the command adds . to the end of all lines from the cursor position to the end of the file. Similarly,
Puts the character ^ at the beginning of the line from the cursor position to the beginning of the file, while
:0,.s/\^/ /
replaces every ^ with a space from the beginning of the file, and 
deletes all occurrences of / throughout an entire document
Now we are ready to use the g command. This is used for global substitutions. It is quite similar to the above, except the syntax is just a bit different. Consider

:g/ -- indicates a global search. You will search for what is in between the next set of / /
/ok/ -- search for the string ok
// -- once you have found the string ok, you will now substitute for it. Note that 
      :g/ok/s//hi/g is identical to :g/ok/s/ok/hi/g  so effectively the // is just a shorthand
      because most of the time when you are searching for something you want to change it to
      something else and with // you don't have to type the string again. But not always!
      For example, maybe you want to change 123 to 456, but only on lines that have
      the string ok in them. Then you would type :g/ok/s/123/456/g
/hi/ -- this is the string you are putting in as a replacement for ok. 
/g   -- indicates you want to replace all the ok in the file, even if there are more than one on a given line

Another example

:g/\/home\/me/s//\/r2\/you/g  - replace /home/me with /r2/you throughout the file
Notice that since g is a global search, there are no line number specifications. If you want those you will have to use the : and s commands as described above. Since there are no line specifications, the variables . ^ $ and so on refer to positions within the line, not the line number in the file. The use of & in substitutions The & character has many uses, especially for creating data with columns. For more flexibility here you can use awk. Essentially when you do a search, the result is stored in a variable called &, which you can then reuse in that substitution. For example,
:g/.*/s//mv \/home\/& ..\/&.copy/ does the following

:g   -- the ex global command
/.*/ -- search for the string ., which means any character, followed by *, which means any string. 
        So the search string .* simply means 'everything'.  Hence, the contents of the entire line are stored
        in the variable &. The editor will work line by line, and replace the contents of & with each new line
/s/  -- you will be substituting something every time you find .*, that is, in each line
//   -- shorthand for .* (see above), meaning that you will be substituting for the entire line. The contents of
        the line are now stored in &.
/mv \/home\/& ..\/&.copy/  -- this looks complicated (impress your friends!) but it really isn't. Within the 
        first and last / lies the string you will put in for each line. If the input string is filename, the
        replacement string would be
        mv /home/filename ../filename.copy
As you can see, this will be very useful for making command scripts that you can run within the operating system, within any coding environment that takes scripts like IDL or IRAF. The & variable also works with the s command, so to do exactly the same thing but only from lines 10 to 20 type
:10,20s/.*/mv \/home\/& ..\/&.copy/
Really fancy searches and substitutions On rare occasions it might be useful to not put the contents of the search string into a single variable &, but rather put one part of the search into /1, another into /2, and so on. Suppose you wanted to change a file as below:
try.dat   -->  cp try.dat ../dat/try.15.dat
ok.dat    -->  cp ok.dat ../dat/ok.15.dat
yes.file  -->  cp yes.file ../file/yes.15.file
You really need to split the search string, because you want to use the part in front of the . (try, ok, yes) in multiple ways and the part after the . (dat, file) in a different way. This is done throughout the entire file in the following magical command (well, maybe not so magical by now)
:g/\(.*\.\)\(.*\)/s//cp \1\2 ..\/\2\/\115.\2/
Oooh. Look at all those slashy characters, dots and stars. Even the sysadmin guy looking over your shoulder will be impressed. But it is really just syntax. Break it apart into pairs of \( \) which look like pairs of ( ) except they are escaped by a \. For each line, within the first pair of \( \) is stored the variable \1 which you will call later, within the second one \2 and so on. I don't think there is a limit. So let's break it apart
:g/ -- the global command of ex
\(.*\.\) -- the string .*\. goes into variable \1  What is this? Well, .* is any character followed by any
            string, so just that would be the whole line. But you also need a \. which simply means the character .
            It needs to be escaped with a \ because otherwise it is a wildcard that means any single character, which is what
            the first . meant. So \(.*\.\) picks out "try." and puts it into \1, then "ok." into \1 then "yes." into \1
            as it goes through the lines.
\(.*\)  -- This one is pretty easy, between the \( and \) is .*, which means everything. So once it is done
           putting things into \1, the editor dumps the entire rest of the line into \2. For the three lines in
           our file it means "dat" in \2, then "dat" (again) in \2, then "file" in \2. 
/s/     -- substitute every time you find the \1\2 string combination defined above
//      -- replace the entire contents of what you just found
/cp \1\2 ..\/\2\/\115.\2/  -- with what is between the leading and trailing /. Let's just do the first line. In this
           case \1="try." and \2="dat" (quotes are mine, not part of the variable). So \1\2 ..\/ translates into
           try.dat ../   and /2\/\1 becomes dat/try  and finally 15.\2 becomes 15.dat  So the result is that
           try.dat is replaced by cp try.dat ../dat/try.15.dat
I would not type in all of that for a 3-line file. I would probably do :g/.*/s//cp & ..\/dat\/&/ which gets me close, and then insert the 15 (by moving the cursor with jhlk, typing i15, drop to the next line with jh, type . to redo the edit, same for third line), then go to the last dat and cw file . But for a file hundreds of lines long you don't want to be editing one by one.

Global searches followed by print, move, or delete

You don't have to substitute once you've found something with :g -- you can also delete the line. Simply replace the s with a d and you are done. So to delete all lines in a file that contain the string "ok" you would type

To delete blank lines type :g/^$/d
In the preceding example, to print out the lines with "ok" in them to see how many they are (like a unix grep),
This command flips the order of lines in a file (first line to last and vice-versa) using the m command from ex. It works by moving the current line to the top of the file, and when it has finished going through the entire file, the order of the lines has flipped.
File Manipulation

:e newfile   - begin editing 'newfile' without exiting vi (keeps buffer contents)
:r newfile   - read contents of `newfile' into current file
vi file*     - sequentially edit file1, file2, file3, etc.
:n           - edit the next file in the sequence
:!pwd        - do the Unix command 'pwd' and return to vi 
Special Characters, Macros, Using History

What to do when you want to insert CTL-C into a file? Precede it within insert mode with CTL-V. This procedure also works for adding carriage returns. Hence, a CTL-V CR will appear as a ^M in the file. This is also a way to put the escape character in a file.

Suppose you want to delete two words, jump down 3 lines, move back a word, and then do this process over and over within a file. Define these commands to an unused vi key (e.g. v) by typing
:map v 2dw3jb
then keep the v key depressed until you are done. To insert the string 'hi', a CR, the string 'there', then delete the second to last word in the next line and skip 2 lines, define (don't type brackets - [ESC] means type the escape key)
:map v ihi[CTL-V][CR]there[CTL-V][ESC]$2bdw2j

Want to run a whole bunch of vi commands on a bunch of files from a script? Store the commands you want in a file. When creating the command file with vi, remember that to store characters like ESC and CTL you need to type CTL-V first. Once the commands are in a file, edit your main document and :r macrofile (usually a single line), and then type "add which will delete the macro from your document and store it in buffer a. To execute the macro type @macrofile or 5@macrofile if you want to do it 5 times. For multiple files or other edits you can use sed, which has basically the same syntax as vi.

Another really nice way to run macros is to use the record feature of vi. Suppose you have something really complicated you want to do line by line. Position your cursor at the beginning of the line and type 'qa', do all your vi commands to get the line the way you want it, then position the cursor at the beginning of the next line and type 'q'. This process records all of your edits into buffer a. So simply type '@a' to do the same thing to the next line, or, say, 50@a to do it for the next 50 lines.

Finally, vi keeps a history of all your commands. To see them, type 'q:'. Then use the arrow keys to move up and down, and edit the previous commands as you wish using normal vi keystrokes. Hit return and the modified command will be executed.

vi Options

These are read from your .exrc file if you have one. For example, if you want vi by default to not distinguish between capital and small letters in a search, put the line
set ignorecase
in your .exrc file. You can always override the defaults once in vi by typing
:set ignorecase

Examples of some potentially useful settings. The inverse of `number' is `nonumber', of `nowrapscan' is 'wrapscan', etc.

:set all          - view all current settings
:set nowrapscan   - do not wrap around file when looking for a string
:set number       - add line numbers to left of file
:set noautoindent - do not automatically indent the file
:set report=0     - always report at bottom when any number of lines are yanked
:set ignorecase   - treat capital and small letters the same when searching

Crash Recovery

Try `vi -r filename' to recover the changes made if the computer crashes while you are editing.


Not sure if anyone still uses telnet, but for historical reasons... Sometimes vi has a hard time recognizing the size of your window in a telnet session. A symptom is that files larger than your window size behave oddly when loading, and you don't see the entire file. To fix this, at the Unix prompt try 'resize', which should define the size of your terminal, and reopen the file with vi

If this doesn't work, you can try to reset your TERM variable from the Unix prompt. When telnetting in from a PC the first thing I do is type 'setenv TERM xterms' which seems to make everything work right. Another possibility is to use 'setenv TERM vt100', though I found that vi doesn't deal with lines > 80 chars or refresh the screen right in insert mode (you need to type a lot of 'z.' to redraw the screen) for this setting. At the Unix prompt you can fix minor annoyances like the delete key not being set right by typing 'stty ERASE {key}' where {key} is what you want for an erase key. You might also need to type 'stty echoe' to have your screen actually remove the characters you erased. Within vi you can always limit the size of your vi window to 15 lines if desired by typing 'z15'.

Back to Hartigan's Home Page
Patrick Hartigan