Reputation: 8026
I want to create a 'quickfix' list (see :help quickfix
) with all files that contain lines with "abc" but not "xyz". I was hoping I could run the following vim ex command:
:grep -nHr abc * | grep -v xyz
Unfortunately, vim doesn't like the "pipe" and the command fails. What is the best way to do this from within vim?
Upvotes: 9
Views: 4562
Reputation: 11144
You can set grepprg
, which defines what command Vim uses to execute :grep
. It can contain pipes, though it's difficult to get the escaping correct using :set
. If you use :let
instead, you can pass an expression. Using a string literal for that expression provides simpler escaping rules, so you can use a more readable value. Here's an example that filters out unwanted files first, then offers multiline matching with PCRE:
let &gp='find . \! \( \( -path ./node_modules -o -path ./vendor -o -name '*.tags' \) -prune \) -type f -printf \%P\\0 | sort -z | xargs -0r pcre2grep -nIM '
All the backslashes are passed to the shell as-is with the exception of those that immediately precede %
, as they are consumed when preventing expansion into filenames and paths.
Of course, that would affect all your :grep
s but you can revert to the default command with :set gp&
.
Upvotes: 0
Reputation: 119
It's many years later, but I found more direct alternative. cexpr
command in vim. It pretty much implements the answers from @jwd and @skeept, and is a bit more flexible because it accepts a list.
Below is an example use of my own which uses pipes. I prefix all my questions in my work notes with Q:
to find them quickly. I wanted to use quickfix list and vim to browse through questions, viewing the most recent first.
:cexpr system("grep -R --line-number Q: --exclude '*~' ~/_Notes/work-log/ \| sort \| tac")
Relevant excerpt from the vim help.
:cex[pr][!] {expr} Create a quickfix list using the result of {expr} and
jump to the first error.
If {expr} is a String, then each new-line terminated
line in the String is processed using the global value
of 'errorformat' and the result is added to the
quickfix list.
If {expr} is a List, then each String item in the list
is processed and added to the quickfix list. Non
String items in the List are ignored.
See |:cc| for [!].
Examples: >
:cexpr system('grep -n xyz *')
Upvotes: 5
Reputation: 58667
I just figured out how to escape :grepprg
to use a pipe.
In my case, my
:grepprg
is based on thelid
utility from GNU Idutils. I want the output of thelid
program to be sorted. this is because when I use a pattern,lid
finds matches out of order. I want to look for a pattern like, say,pthread.*lock
and step through the matches in order to follow lock nesting, and not jump around between files in a way that follows the internal order of theID
database generated bymkid
, which interleaves the matches from different files.
The pipe requires double escaping. It seems that Vim ends up processing the value of grepprg
as command material. If you escape the pipe just once, it is interpreted as Vim syntax.
So what I have now in my .vimrc
is this:
:set grepprg=lid\ --substring\ --result=grep\ '\\<$*\\>'\ \\\|\ sort
The pipe is escaped as \\\|
: an escaped backslash and an escaped pipe to produce \|
, which undergoes one more round of processing to end up with |
.
Based on this example, it is possible to customize your grep
command to include arbitrary Unix plumbing.
If you just want to trim the results of a quicklist, or to search within the results, there is an interactive way.
After doing the :grep
, open a buffer in a split window which contains the quicklist itself:
:cope[Enter]
To make the best use of this, learn about split windows and in particular, navigation between split windows using Ctrl-W Ctrl-W, and other Ctrl-W commands.
In the quickfix result window, you can "refine your search" by searching in the ordinary way, like in any buffer. Whatever line you navigate to, you can just hit Enter
on that line to jump to that quicklist item in your other window. The cursor automatically goes to that window: to return to the quicklist results to search for something else, use Ctrl-W Ctrl-W.
You can also trim the quicklist results simply by deleting unwanted lines from the :cope
buffer. The buffer is unmodifiable by default, so first you have to make sure your cursor is in that window and then:
:set modifiable
Then you can do something like
:g/xyz/d
to delete all results that contain xyz
. Only the lines which remain are now part of the quickfix list.
Upvotes: 2
Reputation: 12423
you can do it in two steps:
:!(grep -nHr abc * | grep -v xyz >| qf.txt)
:cfile qf.txt | copen
if you change frequently of patterns yo probably can use a function to wrap this the following is not perfect, but works:
fu! Mygrep(pat1, pat2)
let cmd = "silent !(grep -nHr " . a:pat1 . " * | grep -v " . a:pat2 . " >| qf.txt)"
silent exec cmd
cfile qf.txt
copen
endfunction
and then call it using:
:call Mygrep("abc", "xyz")
it seems to work for me but I also get an error message "trailin charathers" (you may need to type to clear the screen).
Upvotes: 1
Reputation: 11144
For some reason I can't leave this one alone!
How about use :!grep ... > filename
followed by :cf filename
, which will open the output as a quickfix list.
Upvotes: 4