在Vim中使用管道进行grep搜索

9
我希望创建一个“快速修复”列表(参见:help quickfix),其中包含所有包含“abc”但不包含“xyz”的行的文件。我希望能够运行以下vim ex命令:
:grep -nHr abc * | grep -v xyz

不幸的是,vim不支持"管道符",命令会失败。在vim中怎样最好地完成这个任务呢?


你确定你不是想要 :!... 吗?(感叹号) - jwd
是的。如果您使用“!”符号,则不会返回“quickfix”列表。 - gdw2
5个回答

4

出于某些原因,我无法放弃这个问题!

你可以尝试使用:!grep ... > filename命令,然后再使用:cf filename命令将输出作为快速修复列表打开。


4
多年过去了,但我找到了更直接的替代方法。在vim中使用cexpr命令就可以了。它基本上实现了@jwd和@skeept的答案,并且更加灵活,因为它接受一个列表。
以下是我自己的示例用法,使用管道。我在工作笔记中给所有问题加上前缀Q:以便快速查找。我想使用quickfix列表和vim浏览问题,按最近的先看。
:cexpr system("grep -R --line-number Q: --exclude '*~' ~/_Notes/work-log/ \| sort \| tac")

来自 vim 帮助文档的相关摘录。

: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 *')


2

我刚刚发现如何逃脱:grepprg以使用管道。

在我的情况下,我的:grepprg基于GNU Idutils的lid实用程序。我希望lid程序的输出被排序。这是因为当我使用模式时,lid按无序方式查找匹配项。我想要查找像pthread.*lock这样的模式,并按顺序遍历匹配项以跟踪锁嵌套,而不是以遵循mkid生成的ID数据库的内部顺序交错来自不同文件的匹配项。

管道需要双重转义。似乎Vim最终会将grepprg的值处理为命令材料。如果你只转义一次管道,它会被解释为Vim语法。

所以现在在我的.vimrc中是这样的:

:set grepprg=lid\ --substring\ --result=grep\ '\\<$*\\>'\ \\\|\ sort

管道符号被转义为\\\|:一个转义的反斜杠和一个转义的管道符号,以产生\|,这经过一轮处理后最终变成了|
根据这个例子,你可以自定义grep命令,包括任意的Unix管道。
如果您只想裁剪快速列表的结果或在结果中搜索,则有一种交互式的方法。
在执行 :grep 命令后,打开一个包含快速列表本身的分屏窗口:
:cope[Enter]

为了充分利用此功能,请了解拆分窗口,特别是使用Ctrl-W Ctrl-W和其他Ctrl-W命令在拆分窗口之间导航。在快速修复结果窗口中,您可以像在任何缓冲区中一样以普通方式搜索来“完善您的搜索”。无论您导航到哪一行,只需在该行上按Enter即可跳转到其他窗口中的快速列表项。光标会自动移至该窗口:要返回到快速列表结果以搜索其他内容,请使用Ctrl-W Ctrl-W。您还可以通过从:cope缓冲区中删除不需要的行来简化快速列表结果。默认情况下,该缓冲区是不可修改的,因此首先请确保您的光标位于该窗口中,然后:
 :set modifiable

然后您可以做类似于这样的事情

 :g/xyz/d

删除包含xyz的所有结果。只有剩下的行才是快速修复列表的一部分。

多次转义的方法几乎无法阅读。使用let与字面值更容易解决这个问题(请参考:let-optionliteral-string的帮助)。你可以在你的vimrc文件或者在:提示符下使用类似let &grepprg='grep ... $* | sort'的语句。请注意,你仍然需要用额外的反斜杠转义字面值中的%字符,以防止文件名扩展。 - undefined

1

你可以分两步完成:

:!(grep -nHr abc * | grep -v xyz >| qf.txt)
:cfile qf.txt | copen

如果您经常更改模式,则可以使用函数来包装此操作。以下代码不完美,但可行:

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

然后使用以下方式调用:

:call Mygrep("abc", "xyz")

对我来说似乎可以工作,但我也收到了一个错误消息“trailin charathers”(您可能需要键入以清除屏幕)。


我喜欢 | copen 部分。我不知道你可以这样做!外部 grep 命令看起来不需要括号。 - gdw2

0

您可以设置grepprg,它定义了Vim用于执行:grep的命令。虽然我没有尝试过,但可能需要使用管道。

当然,这将影响您所有的:grep


它似乎不能使用管道符。或者至少,这对我不起作用:“set grepprg=grp\ -n\ $*\ |\ cut\ -c\ 1-$COLUMNS”(也尝试在“|”之前加上“\”)。它会给出“Not an editor command: cut -c 1-$COLUMNS”的错误提示。也许需要正确转义,但我还没有弄清楚如何做到。 - Jonathan Hartley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接