Vim: master the basics

Posted at 3:36pm on Friday March 6th 2009

In depth: There are more text editors for Linux than you can shake a stick at. Every man and his dog seems to have had a go at one. The ones written by dogs usually involve a lot of sniffing around the connectors at the back of the computer and are probably best ignored, but that still leaves quite a lot - emacs, kedit, gedit, kwrite, kate, mousepad, leafpad, fte, joe, jed, nano, pico... the list goes on.

But the one that most Unix and Linux professionals prefer is the grand-uncle of them all, Vi, and its younger brother, Vim. (Vim stands for "Vi improved" and is a 1991 Vi rewrite produced by Bram Moolenar.

The original Vi was written by Bill Joy back in 1976. Vim is the editor used in this tutorial, although the core command sets of Vi and Vim are identical.) One reason for Vim's popularity is that you can edit efficiently with it even if you don't have a graphical desktop - for example, if you've taken the machine down to single-user run level for maintenance, or if it's a server machine that doesn't have a desktop installed.

Now, Vim isn't the easiest editor to learn - it's practically impossible to figure it out without reading at least a little documentation. Nor does it offer instant gratification.

As a Linux trainer, when I get to the section about Vim I can see the fear in students' eyes. One guy told me that whenever he needed to edit a file on Unix he'd copy it to a floppy so he could edit it on Windows rather than learn Vim.

This reluctance to master Vim is a shame, because once you do, you'll be editing text faster than ever before. So bite the bullet, read the tutorial, and become a Vim genius.

Vim is usually run with a file name as argument. It will open the file for editing if it exists and create it if it doesn't. So let's start with

vi gpl.txt

If you want to follow along with the same file, you can get yourself a copy using

wget www.gnu.org/licenses/gpl.txt

One of the reasons Vim and Vi are hard to learn is that they're moded (some would say 'out-moded'!). That is, the effect of the characters you type depends on which mode you're in. For example, in command mode, typing w moves forward one word, but in insert mode, it simply inserts a 'w' into the text. The modes, and the transitions between them, are shown in Figure 1.

Figure 1: Vim's modes and their various functions.

Command mode

When Vim starts up it's in command mode. Most commands are single characters, and some of the most useful are in Table 1, below.

Table 1: Common commands

Cursor Movement

h,j,k,l

Single character cursor movement: left, down, up, right

w

Forward one word

b

Backward one word

0

Beginning of line (digit zero)

$

End of line

(

Beginning of sentence

)

End of sentence

{

Beginning of paragraph

}

End of paragraph

G

End of file

%

Go to matching bracket at same nesting level

Searching

/string

First occurrence of string (searching forward)

?string

First occurrence of string (searching backward)

n

Next occurrence (continue search in same direction)

N

Previous occurrence (continue search in opposite direction)

Entering insert mode

i

Insert text before cursor

a

Insert text after cursor

I

Insert at beginning of line (upper-case 'i')

A

Append to end of line

o

Open a new line below the current one

O

Open a new line above the current one

Editing text

x

Delete single character

d

Delete (and copy to delete buffer)

y

Yank (copy to delete buffer)

p

Put (paste) contents of delete buffer

c

Change

.

Repeat last change (but at the current cursor position)

u

Undo last change

J

Join current line to line below

<

Reduce indentation

>

Increase indentation

The classic cursor-movement commands in Vi and Vim are h, j, k and l, which do single-character moves left, down, up and right. This choice of characters doesn't have much mnemonic value; it was made because these are home keys for touch typists and are easily accessible. (In Vim you can also use the arrow keys, but because you have to move your hand to reach them, they'll slow you down.)

Most cursor movement commands can take a numeric prefix to repeat the command; thus 5j moves down five lines and 3w moves forward three words. With the G command the prefix specifies a line number, so 23G takes you to line 23.

Searching for text with / and ? is generally a quicker way to get around. The text you search for can be a (limited) regular expression, so, for example, /[Ss]oftware will search for Software or software. If you find an instance of the string that wasn't the one you wanted, keep typing n to repeat the search.

Insert mode

To insert text you must enter insert mode. The simplest commands for this are i, which inserts before the current cursor position, and a, which inserts after it. The I and A commands are the grown-up versions of i and a; they insert before or after the current line respectively. The commands o and O open up a blank line below or above the one you're on, then enter insert mode.

Once in insert mode, you can insert anything from one character to many lines, However, you cannot do cursor movement or any other editing operations in insert mode - for that you must return to command mode (though some versions can be persuaded to use the arrow keys for cursor movement, even in insert mode).

There's only one way back from insert mode to command mode: press ESC. Get into the habit of pressing ESC often.

Deleting and changing text

The command x deletes a single character. It takes a repetition prefix, so 6x deletes six characters. The d command is much more powerful, and must be followed by a cursor movement command to specify how much text to delete.

Here's how it works: d followed by a cursor movement deletes the text between where you are now and where that cursor movement command would move you to. So, for example, dw deletes a word, d3w deletes three words, d$ deletes to the end of the line, d} deletes to the end of the paragraph, and dG deletes to the end of the file.

There is a special case of the d command, dd, which deletes the entire line. Repetition prefixes work here, so 3dd deletes three lines.

Hang on a moment... aren't the text search commands (/ and ?) just a form of cursor movement? Can you use those as the target of a d command? It turns out you can. For example, d/PUBLIC would delete forward to (but not including) the word PUBLIC.

As a slightly fancier example, d/^[0-9] will delete forward to the first line that starts with a digit. (When I'm teaching Vim, it's typically at this point when open hostility begins to give way to a grudging appreciation of the power of the command set.)

The c (change) command works much like the d command except that it also puts you into insert mode. Thus you can think of c3w as meaning 'change three words' and so on.

The u command will undo the last change you made. Vim supports multi-level undos - each u rolls back one more change. The original Vi didn't work like that. If you typed two u's, the second one would undo the undo that the first u had just undone!

Bottom-line mode

Bottom-line mode is used for all sorts of things, but the bottom-line commands you'll definitely need are w to write the edited text back out to the original file, and q to quit. From command mode you reach bottom-line mode by typing :, so :wq is the standard way to save your file and quit the editor. If you want to quit the editor without saving to file, use :q. We'll meet some more bottom-line mode commands later.

You know enough now to drive Vim, at least in second gear, but if you'd like to learn how to get into overdrive and do handbrake turns, read on...

Cut and paste operations

Vim supports cut-and-paste operations through the use of the delete buffer. Text you delete using the d command is saved in this buffer and can be pasted back at a new cursor position with the p (put) command. So to move the current paragraph to the end of the file you might try:

Move the start of the current paragraph with {

Delete the paragraph with d}

Move to the end of the file with G

Paste the deleted paragraph back in with p

(You might like to compare these few keystrokes with the number of mouse operations needed to perform the same operation in a mouse-driven editor.)

Copy and paste works much the same except you use the y command instead of d. The y command 'yanks' text - that is, it places it into the delete buffer but doesn't actually delete it. Like d, it works in conjunction with a cursor-moving command, so y2) yanks two sentences and 4yy yanks four lines.

For example, to duplicate the current line, type yyp to yank the line and paste a copy in on the line below.

Getting fancy

If one delete buffer isn't enough for you, you'll be pleased to know there are 26 more, named 'a' through 'z'. The double-quote mark is used to specify a named buffer. For example, "t4yy will yank four lines into the buffer named 't' and "tp will paste in the contents of the buffer 't'. The contents of named buffers survive as long as you stay in the editor, or until you explicitly overwrite them.

Vim can also do 'global' substitutions. Suppose you want to anglicise the GPL licence text by replacing all occurrences of 'license' with 'licence'. There are a couple of approaches to this: the manual approach and the automatic approach. The manual approach might go like this:

Move to the start of the file with 1G

Search for the first occurrence of 'license' with /license

Change it to 'licence' using cwlicence<ESC>

Find the next occurrence with n

If you want to change this one as well, type .. This command repeats the last change you made (in this case, the cw command) at the current cursor position

Continue working through the file using n and . to repeat the search and the change.

If you just want to replace 'license' with 'licence' everywhere in the file without reviewing the individual changes, you can use Vim's bottom-line substitute command. In this example it would look like this:

:1,$s/license/licence/g

Let's dissect this piece by piece:

The : puts us into bottom line mode.

The notation 1,$ specifies a range of line numbers. 1 is the first line and $ means the last line. If I had wanted to perform the substitution only on the first 50 lines I could have used '1,50' here.

The notation s/license/licence is a simple example of a substitution command

The g on the end means 'global'; that is, if the specified string appears more than once in a line, replace all occurrences within the line.

Vim will report the number of substitutions it made, and the number of lines affected.

Options

There are dozens of settable options that can be used to customise Vi and Vim. The ones I've found most useful are listed in Table 2, but there are many more. To set options you use the bottom-line command set. Some options are boolean - they are either on or off.

Table 2: Vim options

Option

Abbreviation

Type

What it does

number

nu

bool

Turns on line numbering

showmode

smd

bool

Causes Vim to indicate which mode it's in, using a label on the bottom line

cindent

cin

bool

Turns on C-style program indentation

ignorecase

ic

bool

Ignore case in text searches

magic

-

bool

Allow regexes in text searches

wrapmargin

wm

number

The number of character positions, relative to the RH edge of the screen, at which Vim will insert line breaks when in insert mode

autoindent

ai

bool

Turns on auto-indentation

shiftwidth

sw

number

The number of character positions used for indentation

matchpairs

mps

string

A list of character pairs used for the 'showmatch' feature and for the % command. The default setting is matchpairs=(:),{:},[:]

wrapscan

ws

bool

Causes text searches to wrap around from the bottom of the file to the top

For example, to set the auto-indent option you would type :set autoindent (the : is to get into bottom-line mode, remember?). To unset this option you would type :set noautoindent. Many options have short names, too. For example, auto-indent can be abbreviated to 'ai', so :set ai and :set noai work as well.

Other options have string or numeric values. The shiftwidth option, for instance, specifies the number of character positions used for indenting. It is, obviously, a numeric value, and you'd set it using something like :set shiftwidth=4.

You can define 'permanent' option settings (ones that will be read each time Vim starts up) by placing them in the file ~/.vimrc. For example, a line in this file of the form set sw=2 ai will start Vim in autoindent mode with a shift width of 2. (Note that there is no : in front of the set command in the .vimrc file.)

Filtering

Of all Vim's features, one of my favourites is its ability to process the text buffer (or any part of it) through any external Linux filter.

Filtering uses the ! command which, like the d command, requires a cursor movement following. The text between the current cursor position and where that cursor-movement command would move you to is passed through an external command of your choice, and the output of that command is pasted back in to the text buffer in place of the original content.

When you use the ! command, the cursor will be positioned onto the bottom line to give you somewhere to type in the command to be used as the filter. I'll talk about filters in detail some other time, but a couple of examples should give you the idea.

The filter cat, with the -n option, will prepend line numbers to its input. So the command sequence 1G!Gcat -n will:

Move to the start of the file (1G)

Select the entire file for filtering (!G)

Specify cat -n as the filter. (This part of the command will be echoed on the bottom line.)

Note that inserting line numbers in this way actually adds them to the edit buffer. This is not like turning on line numbering with the :set number option which simply tells Vim to display line numbers on the screen.

As another example, the command sequence 1G!Gwc will run wc on the entire file, which will report the character, word and line counts. Once you've read them, use the u command to undo the change, replacing the output from wc with the original text.

Our third example of filtering uses the doubled form of the ! command, !!, to operate on complete lines, and uses grep and a suitable regular expression to filter out blank lines. Here's the command: 10!!grep -v '^$'. And here's how it works:

The regular expression ^$ matches "beginning of line" followed immediately by "end of line" - that is, it matches blank lines

Features for programmers

There are lots of features in Vi and Vim that will appeal to programmers. First off, there's that auto-indentation mode, activated with the option :set ai. With auto-indentation enabled, as you enter text, Vim automatically indents each new line to match that of the line above. If necessary, you can increase the indent with ^T and decrease it with ^D.

To adjust the indentation of existing lines, use the commands > and <. These work like the d command in that they require a cursor movement following, so >} will indent up to the end of the paragraph (that is, to the next blank line). However, these commands are mostly used in their 'doubled' form - for example, 4>> will indent four lines.

By default, the indent is eight character positions, which is probably more than you'll want. You can change the amount of the indent (Vim calls it the 'shiftwidth') with a command such as :set sw=4.

Another programmer-friendly feature is the way Vim handles the various types of brackets. If the cursor is sitting on any type of bracket such as (, ), {, }, [ or ], typing % will move the cursor to the matching bracket at the same nesting level.

Of course, you can use % as a cursor movement following a d, c, or y command, so if you're editing source code of a C-style language that uses curly brackets to delimit code blocks, you can delete a block very easily by placing the cursor on the opening curly bracket and typing d%.

Also, if you set the showmatch option using :set showmatch, Vi will briefly jump the cursor to the matching opening bracket each time you insert a closing bracket such as ), } or ]. The original Vi does not treat angle brackets - < and > - in this way, and neither does Vim by default; however, you can make it do this by appending <:> to the matchpairs option string using the command :set matchpairs+=<:> which might be useful if you're editing html or xml content.

Then there's colour-syntax highlighting, which I personally don't much like though I know many programmers find it useful. The original Vi didn't support this as the terminals of the day could only display in one colour (usually green). In Vim, you enable it with the bottom-line command :syntax enable. Vim will automatically detect the file type and load the appropriate syntax highlighting.

Vim on a non-graphical terminal. Not a mouse in sight!

We've only just begun...

There are many more Vim commands and options that I simply don't have space for here. If you're reading this as an existing Vi expert and I've omitted your favourite feature, please forgive me! Including everything that Vi users might deem worthy of a mention just isn't possible in a mere four pages, and this is intended to be a gentle introduction.

There's extensive documentation on Vim at www.vim.org/htmldoc/ and a PDF version of Steve Oualline's book on Vim at www.truth.sk/vim/vimbook-OPL.pdf. Mind you, even I'd have to admit that you'd need to be pretty dedicated to read a 572-page book to learn how to use an editor...

A little history

The design of Vi makes a lot more sense if viewed in the context of the hardware that was around in 1976 when Bill Joy wrote it. A typical terminal at that time was the Lear-Siegler ADM3A - a CRT-based machine (go to www.vintage-computer.com/otheritems.shtml to see a picture of one).

The ADM3A had a full QWERTY keyboard but it didn't have the function or arrow keys that you'd find on a modern keyboard. The modal design of Vi reflects these limitations - all cursor movement and editing operations can be performed without using function or arrow keys.

The ADM3A was one of the first terminals to have an addressable cursor (that is, you could send it special sequences of ASCII characters to determine where on the screen the next character would be displayed). We take this for granted now but it was a novel feature at the time and it triggered a flurry of activity as people started writing true 'screen editors' to replace the line-by-line editors that had gone before.

These terminals were connected to their computers via serial ports, either directly or through dial-up connections through modems that offered data rates of 300 or (if you were lucky) 1,200bps. With some screen editors, if you deleted a line then inserted a couple of words, you could probably brew a flagon of mead in the time it took the editor to repaint the screen.

One of Vi's strengths was the efficiency of its screen updates, coupled with the ability to drive a large range of terminals through the use of the termcap database. Current versions of Vi and Vim still use the TERM environment variable to determine the terminal type.