Searching in vi

Searching in vi

If you are trying to find a particular text, you can get vi to do it for you. You tell vi that you want to enter a search pattern by pressing / (slash). This will bring you down to the bottom line of the screen where you will see your slash. you then can type in what you want to look for. When you press Enter, vi will start searching from your current location down toward the bottom of the file. If you use press ? instead of /, then vi will search from your string toward the top of the file.

If the search is successful, that is, the string is found, you are brought to that point in the text. If you decide that you want to search again, you have three choices. You can press ? or / and input the search string again; press n, which is the first letter of the word “next”; or simply press ? or / with no text following it for vi to continue the search in the applicable direction. If you wanted to find the next string that matches but in the opposite direction, what do you think the command would be? (Hint: the capital form of the “next” command.)

Once you have found what you are looking for, you can edit the text all you want and then continue searching. This is because the search string you entered is kept in a buffer. So, when you press /, ?, n, or N, the system remembers what you were looking for.

You can also include movement commands in these searches. First, you enclose the search pattern with the character used to search (/ or ?), then add the movement command. For example, if you wanted to search backward for the phrase “hard disk” and then move up a line, you would enter ?hard disk?-. If you wanted to search forward for the phrase “operating system” and then move down three lines, you would enter /operating system/+3.

All this time, we have been referring to the text patterns as search strings. As you just saw, you can actually enter phrases. In fact, you can use any regular expression you want when searching for patterns. For example, if you wanted to search for the pattern “Linux,” but only when it appears at the beginning of a line, you would enter /^Linux. If you wanted to search for it at the end of the line, you would enter /Linux$.

You can also do more complicated searches such as /^new [Bb][Oo][Aa][Tt], which will search for the word “new” at the beginning of a line, followed by the word “boat” with each letter in either case.

No good text editor would be complete without the ability to not only search for text but to replace it as well. One way of doing this is to search for a pattern and then edit the text. Obviously, this starts to get annoying after the second or third instance of the pattern you want to replace. Instead, you could combine several of the tools you have learned so far.

For example, lets say that everywhere in the text you wanted to replace “Unix” with “UNIX.” First, do a search on Unix with /Unix, tell vi that you want to change that word with cw, then input UNIX. Now, search for the pattern again with /, and simply press . (dot). (Remember that the dot command repeats your last command.) Now do the search and press the dot command again.

Actually, this technique is good if you have a pattern that you want to replace, but not every time it appears. Instead, you want to replace the pattern selectively. You can just press n (or whatever) to continue the search without carrying out the replacement.

What if you know that you want to replace every instance of a pattern with something else? Are you destined to search and replace all 50 occurrences? Of course not. Silly you. There is another way.

Here I introduce what is referred to as escape or ex-mode, because the commands you enter are the same as in the ex editor. To get to ex-mode, press : (colon). As with searches, you are brought down to the bottom of the screen. This time you see the : (colon). The syntax is

: <scope> <command>

An example of this would be:

:45,100s/Unix/UNIX/

This tells vi the scope is lines 45 through 100. The command is s/Unix/UNIX/, which says you want to substitute (s) the first pattern (Unix) with the second pattern (UNIX). Normally in English, we would say “substitute UNIX for Unix.” However, the order here is in keeping with the UNIX pattern of source first, then destination (or, what it was is first, and what it will become is second, like mv source destination).

Note that this only replaces the first occurrence on each line. To get all occurrences, we must include g for global at the end of each line, like this:

:45,100s/Unix/UNIX/g

A problem arises if you want to modify only some of the occurrences. In this instance, you could add the modifier c for confirm. The command would then look like this:

:45,100s/Unix/UNIX/gc

This causes vi to ask for confirmation before it makes the change.

If you wanted to search and replace on every line in the file, you could specify every line, such as :1,48., assuming there were 48 lines in the file. (By the way, use Ctrl-g to find out what line you are on and how many lines there are in the file.) Instead of checking how many lines there are each time, you can simply use the special character $ to indicate the end of the file. (Yes, $ also means the end of the line, but in this context, it means the end of the file.) So, the scope of the command would look like :1,$.

Once again, the developers of vi made life easy for you. They realized that making changes throughout a file is something that is probably done a lot. They included a special character to mean the entire file: %. Therefore, the command is written as % = 1,$.

Here again, the search patterns can be regular expressions. For example, if we wanted to replace every occurrence of “boat” (in either case) with the word “ship,” the command would look like this:

:%s/[Bb][Oo][Aa][Tt]/ship/g

As with regular expressions in other cases, you can use the asterisk (*) to mean any number of the preceding characters or a period (.) to mean any single character. So, if you wanted to look for the word “boat” (again, in either case), but only when it was at the beginning of a line and only if it were preceded by at least one dash, the command would look like this:

:%s/^–*[Bb][Oo][Aa][Tt]/ship/g

The reason you have two dashes is that the search criteria specified at least one dash. Because the asterisk can be any number, including zero, you must consider the case where it would mean zero. That is, where the word “boat” was at the beginning of a line and there were no spaces. If you didn’t care what the character was as long as there was at least one, you could use the fact that in a search context, a dot means any single character. The command would look like this:

:%s/^..*[Bb][Oo][Aa][Tt]/ship/g

You can can simplify things by using the plus-sign (+) instead of the asterisk. The plus-sign will match 1 or more of the preceding character, whereas the asterisk matches the preceding character 0 or more times. This is a slight but important difference. If you use a question mark (?), it will match 0 or 1 times only. For example, if we had:

:%s/^-?[Bb][Oo][Aa][Tt]/ship/g

We would only match if there was 0 or 1 dash at the start of the line. If there were two or more, it would not match.