在VIM中查找第一个不匹配的行

32
有时我需要查看Windows上的各种日志和跟踪文件,通常我使用VIM来完成这个任务。
不过我的问题是,我仍然找不到VIM中grep -v相似功能:在缓冲区中查找不匹配给定正则表达式的行。例如,日志文件中充满了某些行,在其中包含短语all is ok,而我需要查找第一行不包含all is ok的行。
我可以编写自定义函数来实现,但目前似乎这是一种杀鸡焉用牛刀的方法,而且可能比本地解决方案更慢。
是否有任何简单的方法来在VIM中完成呢?
7个回答

25

我认为,如果您只想让光标停留在第一行非匹配行,您可以在全局命令中使用"visual"命令。所以:

:v/pattern/visual

这将使光标停留在第一行不匹配的位置。或者:

:g/pattern/visual

将使您的光标停留在第一匹配行。


1
可视化命令也可以缩写为vi...因此您可以执行:v/pattern/vi。 - Neg_EV
14
使用 :1,/pattern/-1v/pattern/ 可以从文件开头开始查找第一个不匹配的行,但不会从当前光标位置开始。如果要从光标位置开始查找,可以使用 :.,$v/pattern/vi - Denilson Sá Maia
2
Denilson:你应该把你的答案提交上来。 - Nick

24

您可以使用负向回顾后发操作符@<!

例如,要查找不包含 "a" 的所有行,请使用/\v^.+(^.*a.*$)@<!$

(\v 仅使某些运算符如 (@<! 非必须反斜杠转义)

更简单的方法是删除匹配或不匹配该模式的所有行 (:g/PATTERN/d:g!/PATTERN/d)


1
/^.\+\(^.*all is ok.*$\)\@<!$ 的工作效果很好 - 非常感谢! - Dummy00001
":g!/PATTERN/p"在几百万行的文件中会产生太多输出,因此不是一个好的选择。但是":g!/PATTERN/d"看起来非常有用,例如用于处理从标准输入读取而尚未保存到文件中的缓冲区。 - Dummy00001
嗯...显然,对于我的情况,:g!/PATTERN/d(忽略匹配PATTERN的无趣行)应该写成:g/PATTERN/d - 没有感叹号 - 以删除无趣的行。 - Dummy00001
不错的解决方案!我认为你不需要 ^,而且我喜欢“verymagic”,所以我更喜欢这个可读性更好的写法:/\v(all is ok.*)@<!$ - Chris
2
这个可以运行,但是速度非常慢。我认为更高效的方法是使用在vim wiki上记录的/\v^((.*all is ok.*)@!.)*$ - Mark Grimes
1
我发现 /\v^(.*pattern)@!/ 看起来很好用。 - Edward Falk

9

可能有点晚了,但我认为这个应该在某个地方说出来。

Vim(从7.4版本开始)附带了一个名为LogiPat的插件,它使查找不包含某个字符串的行变得非常容易。因此,使用此插件查找不包含all is ok的行可按以下方式完成:

:LogiPat !"all is ok"

接下来,您可以使用 nN 在匹配(或在本例中不匹配)的行之间跳转。

您还可以使用逻辑运算符 &| 将不同的字符串连接到一个模式中:

:LP !("foo"|"bar")&"baz"

LP是指LogiPat,这个命令将搜索包含单词baz且不包含foobar任何一个的行。


1
LogiPat真的很酷,但在某些情况下,它生成的正则表达式比手写的要慢得多。例如,如果我想查找没有"a"、"b"和"c"的行,则/\v^(.*(a|b|c))@!:LogiPat !"a"&!"b"&!"c"更快,后者生成\%(^\%(\%(a\)\@!.\)*$\&\%(^\%(\%(b\)\@!.\)*$\&^\%(\%(c\)\@!.\)*$\)\) - torbiak

7

我经常遇到这种情况,所以为了“清理”日志文件,我使用以下方法:

:g/all is ok/d

您的grep -v可以通过以下方式实现:
:v/error/d

这将删除所有不包含错误的行。


3

我刚刚使用"g"命令完成了一个有些笨拙的过程:

:%g!/search/p

这段话的意思是打印出不匹配的行...不确定是否成功了,但光标最终停留在第一行不匹配的位置。

(当然,将“search”替换为其他字符串)


@Jefromi:谢谢,我忘记了:v - Carl Smotricz

3
你可以使用以下代码搜索并按“n”键跳转到第一个不匹配的行。
^\(.*all is ok\)\@!.*$

运算符分类:

^           -> means start of the line
\( and \)   -> To match a whole string multiple times, it must be grouped into one item. This is done by putting "\(" before it and "\)" after it.
\@!         -> Matches with zero width if the preceding atom does NOT match at the current position.
.*          -> Matches any character repeated 1 or more times
$           -> end of the line

以下是它运作的示例动画。为简单起见,我搜索了单词“apple”。 输入图像说明


1
您可以使用 g 和空替换来迭代非匹配项:
:g!/pattern/s/^//c

如果每次回复“n”,您甚至不会将文件标记为已更改。
您需要使用Ctrl-C来从循环中退出(或继续到文件底部)。

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