Search and replace with vi -- part 1

Global search and replace on any editor should be called global search and destroy. Executing a badly formed search and replace request will obliterate your text faster than anything except

<font face="Courier">rm *</font>
. Fortunately, under vi you may undo the effect of the last change command, which includes the effects of a search and replace. Undo action is executed by typing
<font face="Courier">u</font>
(for undo) in command mode. This is a crucial command to know when you are editing, especially when you are doing global search and destroy operations. It is also very handy when you are practicing. You can try something out and then use
<font face="Courier">u</font>
to revert the file to its previous state.

The vi editor loads a text file into an editor buffer. When you perform any edit including search and replace operations you are actually editing the characters in the buffer not the actual file. I have the words, line, text, and file throughout the article, but keep in mind that you are actually editing text stored in a memory buffer and not the file itself. The file isn't changed until the editing that you have done is written back out to the file.

To get started with vi, it is helpful to think of vi's search and replace command as a command in five separate parts:

  1. The command or symbol that signifies that the command to be executed is a search and replace command
  2. The range of lines in the file to be processed
  3. The text to be searched for
  4. The text to be used as a replacement
  5. Options for the search and or replace action

Each of these parts of the command has its own rules, and I will take them apart one by one.

First we will take a look at part one, the command to start a search and replace. The vi editor is not itself an editor -- it is a visual wrapper ("vi" for visual) around a single line editor called "ex." No one uses single line editors anymore -- they are too primitive and painful to use -- but they were the forerunners of the full-screen and multiple screen editors on the market. The vi interface creates a full-screen wrapper around the ex single line editor. Many of vi's commands are actually ex commands and are triggered by first entering ex mode. Search and replace is one of these. To enter ex mode, type the colon (:) character while in command mode. All of the flavors and examples of search and replace in this article start with the colon character. Although not covered in this article, commands that start with the colon such as

<font face="Courier">:q!</font>
to quit are ex commands. The difference between ex commands and the vi commands that were added into the vi wrapper is the starting colon. You don't have to worry about this, but historically it explains why so many vi commands start that way.

The vi command for search and replace is

<font face="Courier">substitute</font>
, which can be shortened to the single character
<font face="Courier">s</font>
. The simplest substitute command starts with a colon followed by an
<font face="Courier">s</font>
as in:

<font face="Courier">:s </font>

The colon and

<font face="Courier">s</font>
(
<font face="Courier">:s</font>
) are followed by the search text and the replacement text. A delimiter, which is usually the forward slash (/), must separate these two text strings. The following command will search for "up" and replace it with "right."

<font face="Courier"> :s/up/right/ 
</font>

In the above command we have parts one (the command itself -

<font face="Courier">:s</font>
), three (the search text - up), and four (the replacement text - right). What about part two, the range of lines over which the command will be performed? The default range of lines is the current line only. The command above will search whatever line the cursor is currently on for "up," and replace it with "right" if found.

When a range of lines other than the default current line is to be specified, the range is given after the colon, but before the "s" of the substitute command.

A starting line, a comma, and an ending line identify a range of lines. The following command searches from the first line through the 10th line and executes the search and replace.

<font face="Courier"> :1,10s/up/right/ 
</font>

Note that the

<font face="Courier">s</font>
or
<font face="Courier">substitute</font>
command appears immediately after the address range of lines. There is no space between the ending line number and
<font face="Courier">s</font>
.

If you had to remember all the line numbers in a file, entering ranges of lines could become very tedious. Fortunately, there are several shortcuts that can be used to identify lines.

The current line (where the cursor is located) can be specified as a single dot (.). To search and replace from the first line through the current line use the command:

<font face="Courier"> :1,.s/up/right/ 
</font>

The last line can be specified as a dollar sign ($). To search and replace from the current line through the last line use the command:

<font face="Courier"> :.,$s/up/right/ 
</font>

To search the whole file, use the shorthand for first line and last line to create a command that processes the full file being edited and executes the search and replace.

<font face="Courier"> :1,$s/up/right/ 
</font>

A search and replace through all lines of a file is common enough that a shorthand command was developed to stand for "first through last." The percent sign (%) is equivalent to the address range 1,$. The following command is another way to process searches from the first through the last line, and execute the search and replace.

<font face="Courier"> :%s/up/right/ 
</font>

The beginning or ending line for a range may be given as a positive or negative number of lines offset from the current line. To execute a search and replace on the current line and the next five lines use:

<font face="Courier"> :.,+5s/up/right/ 
</font>

To execute a search and replace from five lines above the current line through the current line use:

<font face="Courier"> :-5,.s/up/right/ 
</font>

To execute a search and replace from five lines above the current line through five lines below the current line use:

<font face="Courier"> :-5,+5s/up/right/ 
</font>

The range of lines must be from lowest to highest. The following command is illegal as the first address is five lines beyond the current line, and the second address is the current line.

<font face="Courier"> :+5,.s/up/right/ 
</font>

Expanding your search and destroy

Now you know how to specify addresses and ranges of lines, but there is a little catch in the actual search and replace. All of the commands described so far will only locate the first instance of "up" and replace it once with "right" in each line in the address range. Listing 1 will be converted to Listing 2 after the following command is executed.

<font face="Courier">:%s/up/right/
</font>

Listing 1

<font face="Courier">Move the cursor to the right by using the arrows up, up, up
then make the corrections. Move the cursor again up, up until
you reach the end of the line.
<end listing 1>

</font>

Listing 2

<font face="Courier">Move the cursor to the right by using the arrows right, up, up
then make the corrections. Move the cursor again right, up until
you reach the end of the line.
<end listing 2>

</font>

To correct this, we come to the last part of the command -- part five, the options. Options allow you to specify that a search and replace are being performed globally on a line. It is odd to think of a search and replace on a single line as occurring globally on that line, but that is the syntax that vi uses. The global for a line option is

<font face="Courier">g</font>
at the end to the command. The following command correctly converts Listing 1 to Listing 3.

<font face="Courier">:%s/up/right/g
</font>

Listing 3

<font face="Courier">Move the cursor to the right by using the arrows right, right, right
then make the corrections. Move the cursor again right, right until
you reach the end of the line.
<end listing 3>

</font>

Another useful option flag is the confirm flag, a

<font face="Courier">c</font>
at the end of the command. The confirm flag will display the line to be changed with a pointer to the text to be changed and will wait for you to press "y" or "n" to signify that you do or do not wish to go ahead with the substitution. The following command will ask for and wait for your answer on each substitution.

The illustration in Listing 4 assumes that the user has answered "y" to each prompt except the first one. The final result is shown in Listing 5.

<font face="Courier">:%s/up/right/gc
</font>

Listing 4

<font face="Courier">Move the cursor to the right by using the arrows up, up, up
                                                 ^^
Move the cursor to the right by using the arrows up, up, up
                                                     ^^
Move the cursor to the right by using the arrows up, right, up
                                                            ^^
then make the corrections. Move the cursor again up, up until
                                                 ^^
then make the corrections. Move the cursor again right, up until
                                                        ^^
</font>

Listing 5

<font face="Courier">Move the cursor to the right by using the arrows up, right, right
then make the corrections. Move the cursor again right, right until
you reach the end of the line.
</font>

There is another form of line addressing called global addressing. It is similar to the

<font face="Courier">%</font>
(all lines) address, but allows you to limit the search and replace action by specifying certain text that must appear in a line before the search and replace action is applied to it. An example is show below. The syntax shown below would read "for all lines containing `some text', search for `search text' and replace any instances with `replacement text.'"

<font face="Courier">:g/some text/s/search text/replacement text/
</font>

In effect, you are requesting that two strings must be found in a line, but only one of them is to be replaced. This is probably easier to understand with an example. In Listing 6 a file of addresses contains a consistent error. Maryland zip codes have been incorrectly entered as 91042 when they should be 01042. In the sample listing, the address on the last line contains the correct zip. In this example we also make the assumption that the file is too large to edit by hand.

The first apparent solution is to globally search for all instances of 91042 and replace them with 01042. However, there are several California addresses using a correct zip code of 91042. A search and replace that replaced all instances 1042 would now result in California addresses that contain incorrect zip codes of 01042.

Listing 6

<font face="Courier">Mr. A    CA     91042
Miss B   MD     91042
Mr. C    CA     91042
Mrs. D   MD     91042
(other addresses)
Mr. X    CA     91042
Mrs. Y   MD     91042
</font>

Instead, what is needed is a method of running the search and replace for the whole file, but within the whole file, only on lines containing MD as the state.

The following command will search all lines in the file for any line containing MD. When such a line is found, it will apply the substitution rule of changing searching for 91042 and changing any instances found to 01042. The command is also given a final

<font face="Courier">g</font>
option, so the search and replace will be done for all occurrences of 91042 in each line.

<font face="Courier">:g/MD/s/91042/01042/g
</font>

Substitutions only occur on the Maryland lines as in Listing 7.

Listing 7

<font face="Courier">Mr. A    CA     91042
Miss B   MD     01042
Mr. C    CA     91042
Mrs. D   MD     01042
(other addresses)
Mr. X    CA     91042
Mrs. Y   MD     01042
</font>

The limiting text criteria in a global command can also be inverted. An inverted criteria limits the search to all lines that do not contain a certain string. The inverted global command starts with

<font face="Courier">:g!</font>
or
<font face="Courier">:v</font>
as in the following two commands which do the same thing. They both search all lines for lines that do not contain "CA" and then substitute 01042 for 91042.

<font face="Courier">:g!/CA/s/91042/01042/g
:v/CA/s/91042/01042/g
</font>

Substitutions only occur on the lines that do not contain CA resulting in Listing 8. This is the same result as Listing 7 but arrived at from the opposite direction.

Listing 8

<font face="Courier">Mr. A    CA     91042
Miss B   MD     01042
Mr. C    CA     91042
Mrs. D   MD     01042
(other addresses)
Mr. X    CA     91042
Mrs. Y   MD     01042
</font>
1 2 Page
Insider: How the basic tech behind the Internet works
Join the discussion
Be the first to comment on this article. Our Commenting Policies