The Truth About Text — Part III

Last month's column introduced you to the venerable Unix editor vi. This month's column describes several of vi's more advanced capabilities, which set it apart from other editors. By the end of this column you'll know how to perform editing feats that are quite cumbersome without the help of vi.

Last month’s column introduced you to the venerable Unix editor vi. This month’s column describes several of vi’s more advanced capabilities, which set it apart from other editors. By the end of this column you’ll know how to perform editing feats that are quite cumbersome without the help of vi.

Editors Within Editors

As explained in our previous Newbies column, vi is really two types of editors in one. The older ex editor has been incorporated into vi, and any commands beginning with a colon (:) are passed to ex for execution.

What wasn’t fully revealed last month, however, is that the ex editor, though older than the rest of vi, is capable of performing some pretty sophisticated actions. Let’s begin by considering Table One, which summarizes some important ex commands.

Table One: Selected ex Commands

Command

Description

d

Delete specified lines

g/pattern/command

Execute specified command on lines containing pattern

g!/pattern/

Execute specified command on lines not containing pattern

m location

Move lines to specified location

r filename

Insert text from specified file

s/pattern/replacement/

Substitute instances of pattern with replacement

t location

Copy lines to specified location

! command

Execute a shell command

In addition to the commands shown in Table One,ex handles several other important commands that were examined in last month’s column, including the commands to write the current file and exit vi.

Deleting Lines

The previous Newbies column showed how you can delete the current line by issuing vi’s dd command. Using ex commands, you can delete a line other than the current one or delete a range of lines. To delete a single line, issue the command:

:xd

where x is the number of the line that you want to delete. Be sure to type the colon, which signifies that you’re issuing an ex command rather than an ordinary vi command. For example, to delete line 3, issue the following command:

:3d

At this point, you might be asking, “Great idea, but how am I supposed to know what line number I’m looking at?” It’s a very good question. The fact is that it’s much easier to use this and other ex commands if line numbering is enabled. If your configuration of vi does not display line numbers by default, see The set Command sidebar, pg. 24, to learn how to enable line numbering.

Deleting a range of lines is not much more difficult than deleting a single line. To do so, issue the command:

:x,yd

where x is the number of the first line you want to delete, and y is the number of the last line you want to delete. For example, to delete lines 1-25, issue the command:

:1,25d

The symbol $ stands for the last line of the current file. So rather than typing in the line number, you can delete all lines by issuing the command:

:1,$d

The symbol % stands for 1,$, so you can issue this command even more conveniently as:

:%d

The set Command

The ex command set lets you enable and disable options that control the operation of vi. The available options vary somewhat from one version of vi to another. Table Two, pg. 26, summarizes several of the most common and popular options. To view the currently enabled options, issue the command:

:set

As an example, you can enable line numbering by issuing the command:

:set number

Most versions of vi let you abbreviate the name of an option. Thus, you can enable line numbering by issuing the following command, which is equivalent to that given previously:

:set nu

To turn off an option, prefix the name of the option with no . For example, to turn off line numbers, issue the command:

:set nonumber

Some set commands require a parameter; you can recognize these by the presence of an equals sign (=) in Table Two. What kind of command would require a parameter? Well, let’s look at the TAB character as an example. The TAB advances the curser by a given number of spaces. You can use set to set the number of spaces associated with a TAB character to four by issuing the command:

:set tabstop=4

Table Two: Common vi Options

Option

Description

autoindent

In insert mode, causes vi to automatically indent each line to align with the preceding line.

ignorecase

Causes vi to ignore case when searching.

number

Causes vi to display line numbers.

shiftwidth=width

Defines the width of columns used when autoindent is enabled. Also defines the operation of the shift (< and >) commands.

showmatch

Causes vi to briefly flash the matching ( or [ when the cursor moves over a ) or ] .

tabstop=spaces

Defines the number of spaces of indentation associated with a TAB character.

wrapscan

Causes vi to search from the beginning of the file when the end of the file is reached.

The ex commands to copy and move text use a syntax similar to that of the d command. To copy a range of lines to a new location, issue an m command as follows:

:x,ymz

where the range of lines to be copied is x, y, and the copied lines are to be inserted following line z. If you want to copy only a single line, you can specify x without the comma and y. For example, if you wanted to copy lines 1-5 and insert them after line 10, you would issue the following command:

:1,5m10

Similarly, to copy line 5 and insert it after line 10, issue the command:

:5m10

To move lines rather than copy them, use the t command rather than the m command. You may find it helpful to think of the t as standing for transfer. For example, to transfer lines 1-5 to the line following line 10, issue the command:

:1,5t10

Issuing ex Commands

Figure One: Almost all ex commands have the general syntax shown above.

Most ex commands follow the same pattern as the d, m, and t commands. To see this pattern, refer to Figure One. As shown there, the typical ex command has four parts, two of which are optional:

The colon, which signifies that the command is an ex command.

The range of lines that are the subject of the command. If this part is omitted, the command operates on the current line.

The command itself.

The command options, which may be omitted. None of the ex commands described so far require any options.

Substituting Text

One of the most powerful ex commands is the s command, which substitutes text. To replace the string hi with bye on the current line, issue the command:

:s/hi/bye/

If you want to perform this substitution over a range of lines, simply specify the desired range. For example, to perform the substitution over lines 1-100, issue the command:

:1,100s/hi/bye/

The s command supports two especially useful options. By default, the s command moves to the next line whenever it replaces text. Thus, a second or subsequent occurrence of the pattern in the same line is ignored. To specify that the command should look for multiple occurrences of the pattern on each line, use the g option. For example, the following command will replace all occurrences of hi with bye throughout the current file:

:%s/hi/bye/g

Another useful option is c, which causes vi to prompt for confirmation before performing a substitution.

The full power of ex becomes apparent when you learn that regular expressions can appear in the pattern and replacement text of the s command. Table Three summarizes some important regular expression characters that we looked at in the first part of this series.

Table Three: Important Regular Expression Characters

Character

Meaning

.

Matches any single character.

*

Specifies that the preceding expression is optional and need not be matched. Moreover, the expression can be matched indefinitely many times.

\x

Specifies that the character x is understood as an ordinary character, even if it is a metacharacter.

[list]

Specifies a list of characters, any one of which can be used in matching. For example, the expression [0123456789] matches any digit.

[range]

Specifies a range of characters, any one of which can be used in matching. For example, the expression [0-9] matches any digit.

[rangerange]

Used to specify multiple ranges of characters, any one of which can be used in matching. For example, the expression [a-z0-9] matches any lowercase letter or digit.

\<pattern

Matches the specified pattern when it occurs as the beginning of a word

\>pattern

Matches the specified pattern when it occurs as the end of a word

^

Matches the beginning of the line

$

Matches the end of the line

To see what’s possible by using regular expressions, consider the following command:

:%s/^red/blue/

This command replaces the text red with blue, but does so only when red is the first word on the line. As an example of a still more sophisticated command, you might consider the following example:

:%s/=[0-9][0-9]*/number/g

This command searches for lines containing an equals sign followed by one or more digits. When it finds such a line, it replaces the series of digits with the text number. This kind of raw power is the reason that so many serious hackers swear by vi (or ex).

Still More ex Commands

While the s command is truly powerful (See the Using Hold Buffers sidebar, pg. 30), ex has other, equally impressive tricks. For example, consider the g command (not to be confused with the g option we spoke about elsewhere in the text), which is summarized in Table One. You can use it to, for example, quickly delete all lines containing the text comment:

:1,$g/comment/d

Following the g with a ! would reverse the sense of the command, causing it to delete lines not containing the text command.

Here’s another vi trick — use the r command to insert the contents of a specified file into the current file. For example, issue the command:

:r test.txt

to insert the contents of the file test.txt following the current line.

As a final trick, we present the ! command, which lets you execute a shell command. For example, to list the files in the current directory, issue the command:

:!ls

If you want to issue several shell commands, start a subshell by issuing the following command:

:!sh

You can consult the references given at the end of last month’s column to learn about other vi and ex commands.

Practice Baby, Practice…

Okay, you’ve now had the cook’s tour of vi. If you aspire to become a chef, rather than a mere tourist, all that remains is for you to practice using vi. If you take the time to do so, you’ll likely find that vi grows on you, eventually becoming an indispensable part of your computing toolkit. Again, to learn even more about vi, remember to consult the sources given in the conclusion of last month’s column. Until next month, have fun playing around with these editors.

Using Hold Buffers

If you like to cut and paste, you’ll be glad to know that the s command supports hold buffers, which let you store parts of the matched text and use them in the replacement text. This is an extraordinarily powerful capability. To designate a part of the pattern for which the matched text is to be saved, enclose that part of the pattern between the characters \( and \) . To insert the stored text into the replacement text, specify the text \1 for the first or only stored text, \2 as the second stored text, etc. Up to nine parts of the pattern can be stored and retrieved this way.

As an example, consider the following command that helps you change past tense verbs (ending in ed) to present tense verbs (ending in s) when they occur at the beginning of a line:

:%s/^\(.*\)ed/\1s/

Okay, that’s a significantly hairy command, no doubt about it. However, if you take it a piece at a time, it’s not that difficult to understand. The command works like this:

The : signifies that the command is an ex command.

The % signifies that the command is to be applied to every line.

The s signifies that the command performs text substitution.

The / marks the beginning of the search pattern.

The ^ signifies that, to be matched, the pattern must be found at the beginning of a line.

The \( signifies the beginning of a part of the pattern for which the matching text is to be stored in a hold buffer.

The .* is the text to be matched, which can consist of one or more characters of any type.

The \) signifies the end of a part of the pattern for which the matching text is to be stored.

The ed is part of the pattern and must be matched along with the .* .

The / marks the end of the search pattern and the beginning of the replacement text.

The \1 signifies that the contents of the first hold buffer are to be inserted into the replacement text.

The s is text with which to replace the matched text.

The / marks the end of the replacement text.

Try your hand at using the hold buffer facility. You’ll likely amaze yourself at the editing tasks you can perform by using it.

Bill McCarty is associate professor at Azusa Pacific University’s School of Business & Management. He can be reached at bmccarty@apu.edu.