Find and Grep – Reliable Way to Open Files from Find Command Results

findgrepvimxargs

I often run commands like this:

find … -exec grep … --color=always -l {} \+

and sometimes I need to open the matching files in Vim.

But what is the most reliable way to do so?

One way seems to be

vim $(find … -exec grep … {} \+)

Another way seems to make use of xargs.

Are there advantages/disvantages/concerns to be aware for these two, and others, if any, methods?

Best Answer

vim $(find path/ -exec grep -l 'pattern' {} +)

is an unquoted command substitution, so word splitting will be performed on whitespace on its result, as well as pathname expansion. I.e., if a file a b matches, Vim will incorrectly open a and b. If a file * matches, alas, that will be expanded to every file in the corresponding directory. An appropriate solution is

find path/ -type f -exec grep -q 'pattern' {} \; -exec vim {} +

Grep runs in quiet mode: Only its return value is used for each file. If 0, a match was found in that file and the file is passed on to Vim.

{} \; means one file will be analysed at a time by Grep. If we used {} +, all files would be passed as arguments to Grep, and a found match in any of those files would result on 0 exit status, so all those files would be opened in Vim. On the other hand, {} + is used for Vim so that it each found file goes to one buffer in a single Vim process. You can try changing them to feel the difference.

If you need to speed things up:

  • If 'pattern' is not a regular expression, but only a fixed pattern, add the -F flag to Grep.

  • grep -lZ, Xargs and special shell constructs should also speed-up the process if you have those available, see Stéphane Chazelas' answer.

And here are another similar use cases with Xargs, Find and Vim.

Related Question