---
title: Lists, Vim, and You
teaser: How to search and replace from your shell and editor.
tags: vim,shell
author: Teo Ljungberg
published_on: 2016-04-20
---

Vim truly excels at editing text, but some common text-editing tasks are more
daunting than others to do from our favorite text editor. Today, we are going to
cover the various ways you can search and replace bits of text, in your shell
and beyond to Vim.

## From the shell

From [our dotfiles] you can find a script named [replace], which takes three
arguments - the word you want to replace, its replacement, and the pattern for
where you want to replace within. Easy, right?

```bash
% replace ugly funny-looking **/*.txt
```

If you do not want to install yet another script to your dotfiles, you can use
`sed(1)`, which is used under the hood by [replace].

```bash
% git grep -l ugly | xargs sed -i "" "s/ugly/funny-looking/g"
```

For an extended overview on `sed(1)` and replacing text in your shell, I suggest
reading [sed 102: Replace In-Place].

[our dotfiles]: https://github.com/thoughtbot/dotfiles
[replace]: https://github.com/thoughtbot/dotfiles/blob/master/bin/replace
[sed 102: Replace In-Place]: https://thoughtbot.com/blog/sed-102-replace-in-place

## Using Vim, from the shell

As you might be aware, Vim lets you search and replace by running:
`:s/ugly/funny-looking`. The `:s` there comes from `sed(1)` which we used above.
We are going to use the `:%s` version to search and replace across a buffer,
which is what `%` means in Vim-land. So, how can we leverage that do
search-and-replace over a set of files? Enter `:argdo`.

`:argdo` lets us run a given set of commands over our arguments list. What is
the arguments list you might ask?

Take any directory, much like this one:

```bash
% ls
a.txt  b.txt  c.txt
% vim +args *.txt
```

We are greeted with the response:

    [a.txt] b.txt c.txt

That means that our arguments list is populated with the three files listed
above.

Now, with that new-found knowledge - let us see how we can search and replace
over that set of files.

```bash
% vim $(grep -l ugly -r *) +"argdo %s/ugly/funny-looking/g | update"
```

That command is a bit of a mouthful, but do not fret. We will break it down bit
by bit.

- `$(grep -l ugly)`: returns all files that contain the word "ugly".
- `+"argdo %s/ugly/funny-looking/g"`: will run `%s/ugly/funny-looking` across
  all files in our arguments list.
- `... | update`: will save each file after the `:%s` command has finished
  running.

## From inside of Vim

With the release of [Vim 7.4.858] we got two new commands: `:cdo` and `:cfdo`.
They work similarly to `:argdo`, but they operate over the quickfix list instead
of the arguments list.

The quickfix list is populated when you run commands such as `:grep`. Which is
great, because that is how I find my way around text documents.

First of all, we need a list of files to modify. From inside of Vim:

```vim
:grep ugly -r **/*.txt
```

That will populate our quickfix list with a list of files. You can use `:copen` or
`:clist` to look at the files manually.

Now, for the fun part! We can use the same command as we did when replacing
using `argdo` from the shell, but using `:cfdo` instead of `argdo`.

We are opting to use `:cfdo` instead of `:cdo` for the fact that `:cdo` will
run a set of commands over each element in the quickfix list, while, `:cfdo`
will run it over each _file_ in the quickfix list. Therefore, we will not run the
same command twice.

```vim
:cfdo %s/ugly/funny-looking/g | update
```

Voilà, we have now replaced all occurrences of "ugly" with "funny-looking" from
the inside our favorite text editor.

[Vim 7.4.858]: https://github.com/vim/vim/commit/aa23b379421aa214e6543b06c974594a25799b09

## Conclusion

With this newfound knowledge, we can now search and replace with ease using
Vim's various lists. The lists can be used for other tasks, as versatile as they
are. I'm going to leave it as an exercise to the reader to find other use cases
for them.
