vi tips and tricks: Ten cool commands sure to impress your friends

Become a vi editing wizard

When coming to grips with the vi editor—either for the first time or as a regular user—most people tend to have a grasp of the core command set that allows them to perform those functions they use most regularly: navigating or saving a file; inserting, updating, deleting, or searching for data; or quitting without saving changes.

However, the vi editor is extremely powerful and rich in features and functionality. Even after many years of use, you can still uncover new commands that you didn't realize existed. The commands covered in this article are amongst those less well known, but they can help you to work smarter by short-cutting existing methods you may use or allowing you to do something that you never realized you could do with vi.

Before we start just a recap on the two modes of vi: command and insert. Command mode allows the user to execute commands to modify text, navigate around the file or control your vi session in some way. Insert mode puts anything you type into the current file in your vi session. When you start vi you start in command mode. Once in insert mode you can switch back to command mode by hitting the Escape key. Hitting the Escape key whilst in command mode leaves you in command mode. All of the commands covered in this article should be executed from command mode.

Turn line numbering on and off

The vi editor has a number of options that determine the look and feel of an editing session. To change any session settings in vi, you use the :set command. To display a list of the options and settings, use the :set all command.

One of the options you can set is number , which turns line numbering on and off (see Listing 1).

Listing 1. Before line numbering is turned on

# # Internet host table # ::1 localhost 127.0.0.1 localhost loghost 192.168.0.6 centos5 192.168.0.10 appserv 192.168.0.11 webserv 192.168.0.12 test 192.168.0.5 solaris10 # Added by DHCP ~ ~ ~ :set number

This command instructs vi to display a line number against each record in the file you are currently editing. After putting vi into command mode, you can type :set number and press Enter to turn line numbering on (see Listing 2).

Listing 2. Line numbering turned on

1 # 2 # Internet host table 3 # 4 ::1 localhost 5 127.0.0.1 localhost loghost 6 192.168.0.6 centos5 7 192.168.0.10 appserv 8 192.168.0.11 webserv 9 192.168.0.12 test 10 192.168.0.5 solaris10 # Added by DHCP ~ ~ ~ :set number

You can use the :set nonumber command to turn line numbering off. You can also use shorthand versions of this and the :set number command—namely, :set nu and :set nonu .

Having line numbers displayed can be particularly useful when you need to quickly calculate the number of lines you want to process with a vi function. This is especially true when the number of lines is long and may span several screens, or you know the range of lines you want to process but need to find the start and end line numbers that you'll use in the appropriate vi command.

If you want to display line numbers every time you enter a vi session, add the line set number to the .exrc file in your home directory.

Auto-indentation

When writing code in certain programming languages, indentation is an important part of the style to ensure that the code is more readable. You can set up the vi editor to automatically indent to adhere to a language-specific style when necessary. You use autoindent to turn automatic indenting on or off (see Listing 3).

Listing 3. Turning automatic indentation on

#!/bin/ksh # # for file in /etc/* do if [[ -f ${file} ]] ; then echo "${file} is a file" ~ ~ ~ ~ ~ :set autoindent

From this point on, if you use leading spaces or tabs in a line, subsequent new lines will be indented to the same place. With vi in command mode, type :set autoindent , then press Enter to turn on automatic indenting. Set the level of indentation by setting shiftwidth . For example, to set each indentation to four spaces, use :set shiftwidth=4 (see Listing 4).

Listing 4. Setting the indentation level

#!/bin/ksh # # for file in /etc/* do if [[ -f ${file} ]] ; then echo "${file} is a file" elif [[ -d ${file} ]] ; then echo "${file} is a directory" fi done ~ ~ :set shiftwidth=4

While in command mode, you can use the >> command to add a level of indentation to an existing line or the << command to remove a level. Precede these commands with an integer to add or remove an indentation level across multiple lines. For example, with the cursor at the start of line 6 in Listing 4 and after entering command mode, type 5>> to add an indentation level to the next five lines. Listing 5 shows the result.

Listing 5. Indenting a block of lines

#!/bin/ksh # # for file in /etc/* do if [[ -f ${file} ]] ; then echo "${file} is a file" elif [[ -d ${file} ]] ; then echo "${file} is a directory" fi done ~ ~

You can use the :set noautoindent command to turn automatic indenting off. Shorthand versions of this and the autoindent command are also available—namely, :set ai and :set noai . You can also turn indentation on and set the indentation level in one command by using :set ai sw=4 .

If you want to enable automatic indentation and set the indentation level to four spaces every time you start a vi session, add the line set ai sw=4 to the .exrc file in your home directory.

Ignore case on searches

As you would expect, pattern matching on searches in UNIX® is case sensitive. However, if you want vi to ignore case sensitivity, you can use the :set ignorecase command. Turn case sensitivity back on using :set noignorecase . You can also use shorthand versions ( :set ic and :set noic ).

If you want to ignore case sensitivity on searches every time you enter a vi session, you can add the line set ignorecase to the .exrc file in your home directory.

Compound searches

You can search for strings in vi using the / command, specifying the pattern to match either as a literal string or as a regular expression. For example, to search for the word echo in a file, enter command mode, type /echo , and then press Enter. This command would find the first word on line 3 in the file shown in Listing 6.

Listing 6. Compound searches

1 #!/bin/ksh 2 # 3 echo "Starting" 4 file=${1} 5 6 echo ${file} 7 8 if [[ ${file} = 1 ]] ; then 9 ((file=${file}+1)) 10 echo "Adding one gives " \ 11 ${file} 12 fi 13 echo "Ending" 14 exit ~ ~

You can use a simple regular expression to specify that you want to find a line containing one word followed by another. For example, to find the first line containing the string echo followed by zero or more characters followed by the string file , you would use /echo.*file . In the file shown in Listing 6, this command would find the first word on line 6.

However, this command will only find matches where both strings exist on the same line. If you want to search for the first occurrence of a pattern or string where it follows another regardless of whether both patterns or strings exist on the same line, you can use a compound search by specifying both search commands separated by a semi-colon ( ; ). For example, to search for the first occurrence of the string echo where it follows the string {file}+1 , you would use /{file}+1/;/echo/ . In the file shown in Listing 6, this command would find the first word on line 10.

Compound searches are particularly useful when you are searching through code for the existence of a command that specifically follows another—for example, where a function is called after a particular variable is set.

Replaying search patterns

When searching for patterns to replace within a file, you can instruct vi to save any patterns that it matches into a buffer, which can then be replayed in substitutions using a buffer reference number. You do this by enclosing the pattern within \( and \) , which instructs vi to save the pattern into a numbered buffer (1 to 9). You can then reference these buffers in substitutions using the buffer references \1 to \9 .

For example, to search the file in Listing 7 for lines starting with the word Martin and for each occurrence to add the prefix Mr and the suffix Wicks, enter command mode, type the vi command :%s/^\(Martin\)/Mr \1 Wicks/g , and then press Enter.

Listing 7. Replaying search patterns (before)

Martin is an IT consultant. Martin likes snowboarding and mountain biking. Martin has worked on UNIX systems for over 15 years. Martin also worked for many years before that on mainframes. Martin lives in London. ~ ~ ~ ~ :%s/^\(Martin\)/Mr \1 Wicks/g

Here's a breakdown of the command into its components:

:%s - Instructs vi to perform a substitution.

- Instructs vi to perform a substitution. / - Pattern separator.

- Pattern separator. ^\(Martin\) - Look for lines starting with the string Martin , and save the string in buffer 1.

- Look for lines starting with the string , and save the string in buffer 1. / - Pattern separator.

- Pattern separator. Mr \1 Wicks - Substitute the string located with the string Mr , followed by the contents of buffer 1 followed by the string Wicks .

- Substitute the string located with the string , followed by the contents of buffer 1 followed by the string . / - Pattern separator.

- Pattern separator. g - Global change (that is, change every occurrence on every line matched).

You can use the buffer reference in both the search and in the substitution string.

The resulting changes are shown in Listing 8.

Listing 8. Replaying search patterns (after)

Mr Martin Wicks is an IT consultant. Martin likes snowboarding and mountain biking. Martin has worked on UNIX systems for over 15 years. Martin also worked for many years before that on mainframes. Mr Martin Wicks lives in London. ~ ~ ~ ~ :%s/^\(Martin\)/Mr \1 Wicks/g

Bookmarks

You can tell vi to place a bookmark at a point in a file by pressing the M key followed by another alphabetic character that denotes the bookmark reference. Therefore, you have up to 26 bookmarks named a to z. To return to the previous bookmark, press the back tick ( ` ) followed by the bookmark reference alphabetic character.

For example, after pressing the MA keys, you would save the current cursor position into a bookmark named a. Whenever you want to return to that cursor position later in the editing session, you simply press the `A keys. To flip between the current bookmark and the previous one, you can use the double back tick ( `` ) command sequence.

Find, update, find next, repeat

One of the most useful Search/Replace features of the vi editor is the ability to find a string matching a pattern, update it, and then repeat the same search for the next occurrence and optionally repeat the update against it, much like the Find Next/Replace functions found in Microsoft® Word.

You probably already know that you can search for string patterns in vi by entering command mode, typing /search_pattern (where search_pattern is a string or regular expression), and then pressing Enter. Doing so takes you to the first occurrence of a string matching the pattern specified. From here, you can perform whatever operation you want on the located text. For example, pressing the C and W keys followed by more text changes the located string to another word.

To quickly locate the next occurrence of a matching pattern, you press the N key. When the next match is found, you can optionally use the period key ( . ) to repeat the last text operation at this location, such as the change word ( cw ) function used in the previous example. You can then continue to find further matches ( n ) and optionally repeat the text operation ( . ) using these keys in much the same way you would use the Find Next and Replace functions in Word.

Switch case

You can switch the case of the alpha character underneath your cursor in vi the tilde key ( ~ ). Doing so shifts from lowercase to uppercase and vice versa. Holding down the key rolls over each character in the line, flipping the case of any alpha characters the editor comes across. You can enter a numeric character prior to the tilde to denote how many alpha characters you want to change.

Filtering

You probably know that you can execute commands in a shell within vi by typing :!command , where command is the UNIX command that you want to execute—for example, :!pwd to show the present working directory your editing session is in—and then pressing Enter.

However, you can also send a section of your file as the standard input to a UNIX command of your choice and have the same section in your editing buffer replaced by the resulting output. For example, if you wanted to sort the entire file shown in Listing 9 while remaining in the vi session, you would type :1,$!sort to instruct vi to pass lines 1 through the end of the file ( $ ) into the sort command, replacing the specified section with the output, and then press Enter.

Listing 9. Sorting a file inside the vi session (before sort)

5 4 3 2 7 6 5 4 8 9 6 3 1 3 4 ~ ~ :1,$!sort

Listing 10 shows the resulting output from the sort operation.

Listing 10. Sorting a file inside the vi session (after sort)

1 2 3 3 3 4 4 4 5 5 6 6 7 8 9 ~ ~ :1,$!sort

Alternatively, you can prefix the shell command with the number of lines you want it to operate on from the current cursor. To do so type a numeric character specifying the number of lines followed by double exclamation marks ( !! ) followed by the UNIX command.

For example, with the cursor at the start of line 4 in Listing 9, you would type:

4!!awk '{print "New text",$0}'

and press Enter to prefix lines 4 through 7 inclusive with the text New text , as shown in Listing 11.

Listing 11. Prefixing a block of lines with new text

5 4 3 New text 2 New text 7 New text 6 New text 5 4 8 9 6 3 1 3 4 ~ ~ !awk '{print "New text",$0}'

You can string UNIX commands together using the pipe separator ( | ) to create complex and powerful filtering within your vi session. For example, to replace the contents of the file in the editing buffer of your current vi session with the first white space-delimited field of each line, sorted into ascending order and translated to uppercase, you would enter the following line:

:1,$!awk '{print $1}' | sort | tr [:lower:] [:upper:]

Section save

You can save sections of a file you're currently editing by entering :start,endw file , where start is the first line in the current file from which you want to save, end is the last line that you want to save to, w denotes that you want to write to another file (or overwrite an existing file), and file is the name of the file to which you want to save the specified section. You can use the $ notation for the last line to specify to the end of the file and double greater-than symbols ( >> ) after the w to indicate that you want to append to rather than overwrite the file. The example in Listing 12 shows lines 6 to 9, inclusive, being appended to a file called /tmp/newfile.

Listing 12. Saving a section of a file to another, appending rather than overwriting it

1 # 2 # Internet host table 3 # 4 ::1 localhost 5 127.0.0.1 localhost loghost 6 192.168.0.6 centos5 7 192.168.0.10 appserv 8 192.168.0.11 webserv 9 192.168.0.12 test 10 192.168.0.5 solaris10 # Added by DHCP ~ ~ ~ :6,9w >> /tmp/newfile

Conclusion

The vi editor is an extremely powerful tool, and this article provides you with a number of tips and tricks that will hopefully make your file editing more efficient. Remember, there's always more to vi that meets the eye. Happy editing!

Downloadable resources

Related topics