Practical Vim< /a> vit Practical Vim< /a> Note that the styling for a Visual selection is the... development I thought I knew Vim, but Practical" name="description"/>
Tải bản đầy đủ - 0 (trang)
Tip 96. Find and Replace Across Multiple Files

Tip 96. Find and Replace Across Multiple Files

Tải bản đầy đủ - 0trang

Chapter 14. Substitution

• 234

Vim” but not when it appears in the phrase “Pragmatic Bookshelf.” This pattern will do the trick:

/Pragmatic\ze Vim

This uses the \ze item to exclude the word “Vim” from the match (see Tip 77,

on page 192). Then we can run this substitute command:


Next we just need to figure out how to execute that command across the entire


Execute a Substitute Command on All Files in the Current Project

In Tip 37, on page 80, we learned that the :argdo command allows us to run

an Ex command in a set of files. We can use this technique to run the substitute command for all files in a project. But first, we must populate the

argument list with all of those files by running this:

:args **/*.txt

We should do one more thing before we continue. Run this:

:set hidden

This setting enables us to navigate away from a modified file without first

saving it. Refer to Tip 37, on page 80, for a more detailed discussion.

Now we can execute our substitution command in all of these files by running


:argdo %s//Practical/g

E486: Pattern not found: Pragmatic\ze Vim

This gets the job done, but Vim reports a “Pattern not found” error in the

output. Remember, some of the files that we’re working with contain the

phrase “Pragmatic Bookshelf” but not “Pragmatic Vim.” So when the substitute

command is executed in these files, Vim throws an error.

The error message is just noise. Vim carries on regardless, executing the

substitute command for each of the remaining files. It’s not critical, but it

can be distracting, especially if we’re dealing with a larger list of files. Supplying the e flag to the substitute command will suppress these error messages.

So we could instead run this:

:argdo %s//Practical/ge

And this way presents fewer distractions.

report erratum • discuss

Find and Replace Across Multiple Files

• 235

Let’s pause for a moment and think about what we’ve done. Running :args

**/*.txt loads all files from the current project into the argument list. Then

when we run :argdo %s//Practical/ge, Vim proceeds to execute the substitute

command in every one of those files. Our example contains only a handful of

buffers, but in a real-world situation, the list could number in the hundreds

or thousands. Using this technique, we can be sure that we’ll hit our targets,

but we also run the substitute command unnecessarily many times over. It’s

a scatter-shot approach.

Let’s see if we can tighten our focus.

Build a List of Files Containing Our Target Pattern

How about if we break the problem into two steps? First we’ll perform a

project-wide search for our target pattern. Then we’ll run the substitute

command on the files that returned a positive match.

To perform a project-wide search, we’ll reach for the :vimgrep command (see

Chapter 18, Search Project-Wide with grep, vimgrep, and Others, on page 269).

Since this uses Vim’s built-in search engine, we can reuse the exact same

pattern. Try running this:

/Pragmatic\ze Vim

:vimgrep /// **/*.txt

Pressing / inserts the last search pattern. Again, we use the **/*.txt wildcard to tell vimgrep to look inside all files contained in the current directory.

Each match returned by vimgrep is recorded in the quickfix list (see Chapter

17, Compile Code and Navigate Errors with the Quickfix List, on page 259). We

can browse the results by running :copen, which opens the quickfix window.

But instead of stepping through the results, one at a time, we want to run

the substitute command on every file that appears in the quickfix list.

It would be convenient if Vim included a :quickfixdo command, but there is no

such thing. So instead we’ll use this small snippet of Vim script:


command! -nargs=0 -bar Qargs execute 'args' QuickfixFilenames()

function! QuickfixFilenames()

let buffer_numbers = {}

for quickfix_item in getqflist()

let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])


return join(map(values(buffer_numbers), 'fnameescape(v:val)'))


report erratum • discuss

Chapter 14. Substitution

• 236

You can either add this code to your vimrc file or install it as a plugin.1

Now we can run :Qargs, and it will populate the argument list with each of the

files named in the quickfix list. If we run the substitute command in each of

the files in the argument list now, we can be sure that it only includes the

files that match our pattern. We can leave off the e flag, since we don’t anticipate the substitute command raising any errors.

Here’s the full sequence of commands:

/Pragmatic\ze Vim

:vimgrep /// **/*.txt


:argdo %s//Practical/g

:argdo update

The :update command saves the file, but only if it has been changed (see

:h update ). Note that the last three commands could be combined into one,

like this:

:Qargs | argdo %s//Practical/g | update

The | character has a different meaning on Vim’s command line than shell

users might expect. Whereas in Unix, the pipe character passes standard

output from one command into the standard input of the next command

(creating a “pipeline”). On Vim’s command line, | simply stands for a command

separator, making it equivalent to the semicolon in the Unix shell. Look up

:h :bar

for more details.

Previously we used a scatter-shot approach, where the substitute command

was executed in every file in the project, whether or not that file contained

our target pattern. This strategy is a refinement. We still have to look inside

every file in the project, but this time we use vimgrep to do it. Then, with our

custom :Qargs command, we copy each file from the quickfix list into the

argument list. This allows us to be more focused when we run the substitute

command, only targeting the files that contain a match for our pattern.

A convenient side effect of this approach is that we are left with an argument

list containing each file that was changed by the substitute command. If we

want to review our changes, we can run :first and then step through the files

by running :next.


report erratum • discuss


Global Commands

The :global command combines the power of Ex commands with Vim’s pattern

matching abilities. We can use it to run an Ex command on each line that

matches a specified pattern. Alongside the Dot Formula and macros, the

:global command is one of Vim’s power tools for performing repetitive work


Tip 97

Meet the Global Command

The :global command allows us to run an Ex command on each line that matches

a particular pattern. Let’s start by studying its syntax.

The :global command takes the following form (see :h :g


:[range] global[!] /{pattern}/ [cmd]

The default range for the :global command is the entire file (%). That sets it

apart from most other Ex commands, including :delete, :substitute, and :normal,

whose range is the current line (.) by default.

The {pattern} field integrates with search history. That means we can leave it

blank and Vim will automatically use the current search pattern.

The [cmd] could be any Ex command except for another :global command. In

practice, Ex commands that interact with the text in the document prove

most useful, such as those in Table 9, Ex Commands That Operate on the Text

in a Buffer, on page 52. If we don’t specify a [cmd], then Vim will use :print by


report erratum • discuss

Chapter 15. Global Commands

• 238

We can invert the behavior of the :global command either by running :global! or

:vglobal (mnemonic: invert). Each of these tells Vim to execute [cmd] on each

line that doesn’t match the specified pattern. In the next tip, we’ll see examples

of both :global and :vglobal in action.

The :global command works by making two passes through the lines specified

by [range]. In the first pass, Vim marks each line that matches the specified

{pattern}. Then on the second pass, the [cmd] is executed for each marked line.

The [cmd] can accept a range of its own, which allows us to operate on multiline

regions. This powerful technique is demonstrated by Tip 100, on page 242.

Tip 98

Delete Lines Containing a Pattern

Combining the :global and :delete commands allows us to cut down the size of a

file rapidly. We can either keep or discard all lines that match a {pattern}.

This file contains links to the first few episodes from the archive:


  1. Show invisibles

  2. Tabs and Spaces

  3. Whitespace preferences and filetypes

Each list item contains two pieces of data: the title of an episode and its URL.

We’ll use a :global command to expose each of these.

Delete Matching Lines with ‘:g/re/d’

What if we wanted to throw away everything except for the contents of each

tag? In this file, the contents of each link appear on a line of their own,

report erratum • discuss

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tip 96. Find and Replace Across Multiple Files

Tải bản đầy đủ ngay(0 tr)