6

When I want to recursively search files for a text pattern I use

:grep -R 'color:' .

Vim then lists the results in a quicklist window for easy access.
But now I need to perform another grep pass to exclude some results.
So far I have tried

:grep -R 'color:' . \| grep -v '@'

result: empty list, also tried:

:grep -R 'color:' . <bar> grep -v '@'

result:

/bin/bash: bar: No such file or directory

How can I get shell piping to work, when the command is generated from Vim?

10
  • 2
    Does the following work? : :cexpr system("grep -Rn 'color:' . \| grep -v '@'") If it does, to save some keystrokes you could define the command :CS like this: command! -nargs=1 CS :cexpr system(<q-args>), and then use it like this: :CS grep -Rn 'color:' \| grep -v '@'
    – saginaw
    Jan 2, 2016 at 8:27
  • yep, it sure does! I wondered if system would work, but Im not familiar enough with vimscript to use it, also even if it did work, I wouldn't have thought the quicklist would work. thats sweet. Jan 2, 2016 at 8:34
  • 1
    ah, you need to use the -n switch to grep and then vim can parse it into its quicklist Jan 2, 2016 at 8:35
  • system() is just a function to which you can give any shell command as an argument. It sends it to the shell which executes it and then gives you its output. :cexpr is an Ex command which takes a string or a list as an argument, and parses it to populate the quickfix list. So basically, you're sending your shell command from vim to the shell through system() and then parse its output through :cexpr. And yes, you're right, using the -n switch with grep seems necessary for vim to correctly parse the output. In :help cexpr, they use it in one of their example.
    – saginaw
    Jan 2, 2016 at 10:25
  • 1
    I wrote a plugin called lister.vim which could be of use here. Among other things, it provides a :Qgrep command that allows you to narrow down the quickfix list by a pattern.
    – tommcdo
    Jan 3, 2016 at 22:14

2 Answers 2

6

The reason the pipe isn't functioning like you'd expect is because you're not grepping through the shell, you're grepping through Vim (see digression). For instance, this would be using your shell from Vim to perform your search:

:!grep -R 'color:' . | grep -v '@'

Note the ! at the beginning, which means "execute this command from the shell" (see :h :!). This doesn't help your underlying problem, however, because your shell's end result won't go the quickfix list. This simply executes the whole command at the shell, lets you see it, and returns you to Vim.

Your fastest solution might be to employ grep's --exclude (note: see EDIT below):

:grep -R --exclude='*@*' 'color:' .

Digression: :grep is different from :!grep in that Vim is using a file pattern searcher of your choice as an Ex command within Vim, not the shell. The grepprg option indicates which file pattern searcher to use (use :set grepprg? to see what's currently set). If you have set grepprg=grep\ -nH in your .vimrc, for instance, then :grep will indeed use grep. If grepprg is set to internal, however, then :grep will use :vimgrep instead, which will require a Vim search pattern. (You might be familiar with the Vim search pattern from using / and ?.) See :h :grep and :h :vimgrep for details. Also, if you like ack or ag, you can set grepprg to tell :grep to use them instead of regular grep or vimgrep.

EDIT

@the_velour_fog Thanks for correcting me by pointing out that grep's --exclude is excluding files to grep, rather than omitting lines that otherwise match grep's search pattern.

One solution is to use Vim script's getqflist() and setqflist() to pass searches to the existing quickfix list and populate a new one. See Kent's Stack Overflow answer and resulting QFGrep on GitHub. With this script, you can perform your first grep/ack like normal, then use the following actions:

  • <Leader>g input pattern to do further filtering
  • <Leader>v input pattern to do further inverted filtering (like grep -v)
  • <Leader>r restore the Quickfix/location-list with original entries
1
  • 2
    thanks, I wish you were right, but grep's --exclude is for excluding files, not patterns. e.g. from man grep --exclude=GLOB Skip files whose base name matches GLOB (using wildcard matching). Jan 2, 2016 at 6:41
2

In similar situations I usually do:

:r! grep -R  'color:' . | grep -v '@'

This way, it reads the command's standard output to your buffer; we can edit, delete it etc. (and undoing if you are not happy)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.