Command Output in vi

Command Output in vi

It often happens that we want to insert the output of Linux commands into the file we are editing. The sledgehammer approach is to run the command and redirect it to a file, then edit that file. However, if that file containing the commands output already exists, we never have to leave vi. Instead, we can use the :r from ex-mode to read it directly into our current file. But, what if it doesn’t yet exist? For example, I often want the date in text files as a log of when I input things. This can be done with a combination of the :r (for read) from ex-mode and a shell-escape.

A shell-escape is when we start from one program and jump out of it (escape) to a shell. Our original program is still running, but we are now working in a shell that is a child process of that program.

To do a shell-escape, we need to be in ex-mode. Next, press the exclamation mark (!) followed by the command. For example, to see what time it is, type :!date. We then get the date at the bottom of the screen with the message to press any key to continue. Note that this did not change our original text. It just showed us the output of the date command.

To read in a command’s output, we need to include the :r command, as in :r!date. Now, the output of date is read into the file (that is, it is inserted into the file). We could also have the output replace the current line by pressing ! twice, as in !!date. Note that we are actually brought down to the last line on the screen, where there is a single !.

If we want, we can also read in other commands. What is happening is that vi is seeing the output of the command as a file. Remember that :r <file_name> will read a file into the one we are editing. Why not read from the output of a file? With pipes and redirection, both stdin and stdout can be files.

We can also take this one step further. Imagine that we are editing a file containing a long list. We know that many lines are duplicated and we also want the list sorted. We could do :%!sort, which, if we remember from our earlier discussion, is a special symbol meaning all the lines in the file. These are then sent through the command on the other side of the !. Now we can type

:%!uniq

to remove all the duplicate lines.

Remember that this is a shell-escape. From the shell, we can combine multiple commands using pipes. We can do it here as well. So to save time, we could enter

:%!sort | uniq

which would sort all the lines and remove all duplicate lines. If we only wanted to sort a set of lines, we could do it like this

:45,112!sort

which would sort lines 45 through 112. We can take this one step further by either writing lines 45-112 to a new file with :45,112w file_name or reading in a whole file to replace lines 45-112 with :45,112r file_name.