Effective editing of multiple files in Vim

:: Jun 22, 2021 :: vim L2 ::

This post will cover a couple of commands which are very useful when you’re editing multiple files at once. For each type of list in Vim, there’s an appropriate command which gives us the possibility to execute commands in bulk. Here they are:

We will also cover two more commands which you’ll use often when editing multiple files:

## The execute and normal commands

The execute command

The exe[cute] command is used to evaluate a string as if it’s a Vim command. You could run a command like this: :execute "echom 'Hello world!'" to echo the string Hello world. In this example, we used internal Vim command for echoing content echom.

This command is a very handy tool, because it lets you create commands out of an arbitrary string.

The normal command

If you try running the command :normal gg you’ll see that your cursor will jump to the first line in your current buffer.

As you might guess already, norm[al] command simply takes a sequence of keys you’ve typed and treats them as a Vim command you would enter in Normal mode.

Of course, this command will pick up your personal mappings if they exist. That brings us to the next question: what happens if a user has remapped gg command to delete one line, instead of jumping to first line of the buffer?

In that case, the example from above will execute the command for deleting the line, or whatever else was specified for the mapping.

But, even if there were a mapping for gg command, which is different than default action of jumping to the first line, you can still use the default mapping of the command. Here’s how:

:normal! gg

This way, Vim will move your cursor to the first line of a buffer, even if there’s already a different mapping for gg sequence of keys. So if you use !, mappings will not be used.

You can also use ranges to execute Normal mode commands for each line in the specified range. For more details on this command, take a look at :help normal.

## argdo vs bufdo

Pay attention here, and make sure to understand the difference between :argdo and :bufdo, because this is unclear even to some advanced Vim users.

As you already learned, Vim has different types of lists for different purposes. Another very important list is the so called “arguments list.” This list holds the files which you specify when you start Vim. We’ll call it arglist from now on.

For example, if you start Vim like this:

$ vim my_file1.txt my_file2.txt

Vim will add to arglist two items (my_file1.txt, my_file2.txt). Once you’re in Vim, you could show the contents of arglist by running :args command.

Now, when you open these two files, Vim creates two buffers, for each of the files. As we mentioned before, you can show the buffers list by running :buffers command.

At this point, both lists, for arguments and buffers, contain items my_file1.txt and my_file2.txt.

If we additionally, from Vim, open another file like :e my_file3.txt.

Vim will create one more buffer for this file. It will also add another item in buffers list. So now, the buffers list will contain three items (for all three files we’ve opened), while arglist will still contain two items (with my_file1.txt and my_file2.txt).

And here we finally come to the point:

In our example, editing files via argdo command would affect only my_file1.txt and my_file2.txt, while bufdo command would affect these two, plus my_file3.txt.

Now let’s take a look as some useful examples of these commands.

Recommendation: This post comes from my book Mastering Vim Quickly: From WTF to OMG in no time which helped thousands of people to drastically improve their Vim skills within an hour! Look it up: ebook + screencasts | paperback

## bufdo examples

When you open a file in Vim, you’ve actually created a Vim buffer, as we already said. For each new file you open, its buffer is added to the internal buffers list.

Let’s say you have opened multiple files, so you have multiple buffers. At one point, you want to execute a command which will affect all the active buffers (in buffers list), you need to use :bufdo command.

For example, it’s the end of your work day, you want to save your work and go home. You’ve already edited multiple buffers, but you didn’t save your changes. Of course you shouldn’t switch from buffer to buffer and run :wq. In order to save changes in all your active buffers and exit Vim, simply run :bufdo wq.

This way, we’ve just told Vim to execute :wq command in each active buffer. Alternatively, you could also run the command :wqa which will write changes in all buffers and quit Vim.

Paste to the end of each buffer

:bufdo exe ":normal Gp" | update

Let’s break it down:

Related tip: Whenever you execute :w, Vim will actually write the buffer to the file, no matter whether the buffer was changed from the last saved state. This means it will update the timestamp of the file to the time when you run :w, even if the contents of the file did not actually change.

On the other hand, when you execute :up[date], the Vim will update the file timestamp ONLY if the file has been changed.

Executing macro over all active buffers

:bufdo execute "normal! @a" | w

As you can see, the structure of the command is the same as in the previous example. This time we run @a command in Normal mode to execute the macro recorded in register a. Also, this time we use w instead of update (for no specific reason).

We have already covered how to manage buffers in chapter Buffers, so we won’t repeat the commands for adding and deleting buffers.

## argdo examples

All of the examples for :bufdo command can be applied to files stored in arglist, by replacing :bufdo with :argdo command.

Before we get to some examples, it’s important to mention that we can, just like with buffers list, add and delete items in arglist.

Here are a few useful commands:

Command Description
:args Show files in your current arglist
:args /path/to/files/*/* Replace old arglist files with new ones
:arga[dd] /path/to/file.txt Add file.txt to your arglist
:argd[elete] /path/to/file.txt Remove files from your arglist
:argdo update Save changes to all arglist files
:argdo undo Undo last operation in each arglist file

Here’s an example: You’re working on a project, and have multiple files in your git branch. You want to replace string Bad with string Good across the entire project.

We can do this in two steps, here’s the first one:

:args `git grep -l Bad`

This command runs a shell command git grep -l Bad, which will provide us the list of files containing string Bad, and place those files in argslist.

Then, we can run:

:argdo %s/Bad/Good/gc | update

and perform the substitution across all files in arglist, and save the changes.

But, what if you add multiple files to the arglist, and you want to replace bad with good in all of them where bad appears, without using external shell commands? If you would run a command like this:

:argdo %s/bad/good/g | update

Vim will say there’s an error when the search operation fails for a file which doesn’t contain the string bad. This will prevent completing the substitution over all files in the arglist. A better way to run this command would be:

:argdo %s/bad/good/ge | update

We have included e flag, which tells Vim to not issue an error if the search for the pattern fails. If you’d like to perform substitution selectively, and skip some of the matches, you could just add the c[onfirm] flag.

One last example will show you how you can use shell commands from Vim.

:argdo exe '%!sort' | w

This command will sort the contents of all files in your arglist. It calls the shell command sort.

## windo command

Let’s keep it short.

The command :windo is similar to :argdo and :bufdo, with one important difference: it only affects the visible buffers. As we already mentioned, Vim can have many buffers open, but if there’s only one window, then only one buffer is shown at the time.

So, if you have a Vim session with multiple windows open, then the commands you run with :windo will be applied only to those buffers which are currently visible in your windows.

Let’s summarize. There’s so much power in these commands. We’ve covered the most important concepts and some real word examples. Take time to understand and learn how you can use these commands, and very quickly you’ll become very effective at editing multiple files.

You're welcome to join my private email list or follow me on Twitter.