我经常运行这样的命令:
find … -exec grep … --color=always -l {} \+
有时我需要在 Vim 中打开匹配的文件。
但最可靠的方法是什么?
单程似乎是
vim $(find … -exec grep … {} \+)
另一种方法似乎是利用xargs
.
这两种方法以及其他方法(如果有)是否有需要注意的优点/缺点/问题?
答案1
vim $(find path/ -exec grep -l 'pattern' {} +)
是一个不带引号的命令替换,因此将对其结果上的空格执行分词,以及路径名扩展。即,如果文件a b
匹配,Vim 将错误地打开a
并且b
.如果文件*
匹配,唉,它将扩展到相应目录中的每个文件。一个合适的解决方案是
find path/ -type f -exec grep -q 'pattern' {} \; -exec vim {} +
Grep 以q
uiet 模式运行:每个文件仅使用其返回值。如果0
,则在该文件中找到匹配项,并将该文件传递给 Vim。
{} \;
表示 Grep 一次分析一个文件。如果我们使用{} +
,所有文件都将作为参数传递给 Grep,并且在中找到匹配项任何这些文件的数量将导致0
退出状态,因此所有这些文件都将在 Vim 中打开。另一方面,{} +
用于 Vim,以便每个找到的文件都进入单个 Vim 进程中的一个缓冲区。您可以尝试更改它们以感受差异。
如果您需要加快速度:
如果
'pattern'
不是正则表达式,而只是固定模式,则将-F
标志添加到 Grep 中。grep -lZ
、 Xargs 和特殊的 shell 结构也应该会加速这个过程,如果你有的话,请参阅斯特凡·查泽拉斯的回答。
和这里是 Xargs、Find 和 Vim 的另一个类似用例。
答案2
使用 GNU 工具(您--color=always
已经是 GNU 扩展)和支持 Ksh 样式进程替换的 shell:
xargs -r0a <(grep -rlZ pattern .) vim
或者:
xargs -r0a <(find . ... -exec grep -lZ pattern {} +) vim
和zsh
:
vim ${(0)"$(find . ... -exec grep -lZ pattern {} +)"}
4.4+版本bash
:
readarray -td '' files < <(find . ... -exec grep -lZ pattern {} +)
vim "${files[@]}"
这些最大限度地减少grep
了执行的调用次数。要点是告诉grep
输出以 NUL 分隔的文件名,这样它们就可以通过vim
GNU xargs -0
orzsh
的0
参数扩展标志 orbash
可靠地分割成单独的参数readarray -td ''
。
在 中zsh
,您还可以执行以下操作:
vim ./**/*(...e['grep -q pattern $REPLY'])
(其中...
代表您可能想要添加的进一步限定符,例如find
方法)。
然而,这意味着就像使用 的方法一样find -exec grep -q pattern {} ';'
,grep
每个文件将运行一次调用,这会使其速度显着变慢。
zsh
如果您将 替换--color=always
为并将-Z
的值从默认值更改为,则您的第一种方法将起作用。我不会在其他 shell 中工作,因为它们不支持在变量中存储 NUL,更不用说,并且还会对您需要使用或禁用的拆分结果的单词执行文件名生成。IFS
IFS=$'\0'
IFS=$' \r\n\0'
$IFS
set -o noglob
set -f
为什么循环查找的输出是不好的做法?将为您提供有关如何处理find
可靠找到的文件列表的更多提示。
答案3
两种不同的方法:
- 将结果放入快速修复列表中:
find ... -exec grep -Hn {} + >results
vim -q results
或者清洁工(但非sh)
vim -q <(find ... -exec grep -Hn {} +)
- 使用
:grep -R
和:Cfilter
:
vim
:grep -R ...
:packadd cfilter
:Cfilter[!]
请参阅相关的:help
一些命令。
:cnext
无论哪种方式,您都可以使用和其他命令来导航匹配:help quickfix
。