我有一个文本文件,并且我有一个不希望 grep 匹配的模式。问题是,我也希望之前的行不匹配。
我的文件:
line 1
line 2
pattern
line 4
我试过cat file | grep -v pattern
了,它输出:
line 1
line 2
line 4
然后我尝试cat file | grep -B 1 pattern
了,它输出:
line 2
pattern
但是,当我同时使用它们时cat file | grep -v -B 1 pattern
,我得到:
line 2
我怎样才能使输出为:
line 1
line 4
我倾向于只在
grep
从文件中提取单行时使用,所以当我需要在文本中执行更复杂的编辑时,我会使用其他工具。这里的所有解决方案都假定该模式可能在文本中出现多次,并将删除出现该模式的行和紧接在它们之前的行。如果模式在连续行上匹配,前两个解决方案将出现问题。
您可以使用
sed
匹配模式/pattern/
并让其触发命令N
andd
,它将下一行附加到缓冲区,然后丢弃两者:由于您想丢弃模式匹配之前的行,因此我们将数据向后馈入
sed
,从最后一行开始并移至文件的开头。然后我们在sed
完成后再次反转数据。该
tac
实用程序是 GNU coreutils 的一部分。大多数非 GNU 系统可以用来tail -r
代替tac
(查看您的tail(1)
手册)。如果模式匹配两个连续的行,这将无法删除第一行之前的行(因为第一行将被删除)。
使用
ed
编辑器:这会将命令应用于
g/pattern/ -1,. d
文件的内容。此命令搜索与 匹配的每一行,pattern
然后删除该行及其之前的行。最终
,p
和Q
编辑命令打印整个文件并退出编辑器而不保存。如果模式匹配两个连续的行,这将在删除第一行之前的行之后删除成为第二行之前的行。
(最后一句我写的时候是对的,但显然是只写句。)
我们还可以使用
grep
它的非标准但通常实现-B
的选项来为我们提供需要删除的行号。这些数字可以转换为sed
我们在原始数据上运行的脚本:给定问题中的文本,该
grep
命令将输出...并且第一个
sed
命令将其转换为sed
编辑命令2d
,然后是3d
(“删除第 2 行和第 3 行”)。管道中的最后一个sed
命令采用此编辑脚本并将其应用于原始文本。这个变体对于匹配模式的连续行没有问题,因为它使用一种 2-pass 方法,首先找到所有应该删除的行,然后删除它们(而不是在第一次阅读文本时删除行)。
使用带有 tac 的任何 awk,您可以在匹配模式之前删除任意数量的行:
只需更改
c=2
或c=5
您要删除的任何数量的行,直到并包括匹配的行,例如删除包含数字 97 的行和它之前的 94 行:现在尝试使用 sed 而不是 awk :-)。
请参阅print-with-sed-or-awk-a-line-following-a-matching-pattern以了解有关此习惯用法和其他相关习惯用法的说明。
注意:此代码仅在
file
中不存在与 的输出匹配的每行的重复行或子字符串时才有效grep -B1 pattern file
。例如,如果
file
包含以下行:而且我使用
grep -B1 pattern file | grep -v "$(cat)" file
的输出不会像你预期的那样:解决这个问题的最好方法是使用Kusalananda 的答案
解决方案(这仅适用于我上面解释的没有重复行或子字符串的情况)
这
bash
对我有用(我认为有更好的方法):在
zsh
上面的命令中将不起作用。我不知道为什么。但是你可以使用:PS你不必使用
cat your_file | grep pattern
那是多余的。你应该使用grep pattern your_file
Kusalananda 和 Ed Morton 的解决方案是最简单和最实用的,但它们需要在开始之前阅读内容两次或完整阅读内容。管道不能被重新读取,它们也不总是有限的。适用于任何文本流的解决方案可能是这样的:
如果你想让不打印的行数可变,那就有点复杂了:
顺便说一句,这些解决方案都没有连续匹配的问题。
您可以使用
pcregrep
它的M
ultiline 模式:请注意,如果第一行与模式匹配,则不会被删除。这可以通过使用来解决:
(
(...)
周围\n
显然是必要的,我不知道为什么它不适用于此处\n?.*pattern
的[\n]?.*pattern
8.39 版本)。使用Raku(以前称为 Perl_6)
前两个答案(上图)基本上检测到两行模式,并将其删除。因此,不会处理连续出现的单词
pattern
,也不会处理pattern
第一行中出现的单词。对于这两个答案,lines
都是从文件中读入并 在换行符join
上一起编辑(因为默认情况下为 autochomps)。然后搜索所需的两行正则表达式和 1)。ituted(什么都没有,即删除)或 2)。在两行正则表达式上并ed out 。\n
lines
subst
split
join
put
接下来的两个答案(如下)处理
pattern
第一行中的出现,以及处理单词的连续出现pattern
。[\N* \n]?
他们在正则表达式的开头使用分组:样本输入:
示例输出(删除 2 行正则表达式的前 2 个示例):
示例输出(示例 3 和 4 还处理
pattern
第一行以及连续出现的pattern
):仅供参考:Raku 的
lines
例程被宣传为懒惰,因此可能无需先读入整个文件即可分析文件。有关 Rakulines
例行程序的评论,请参阅下面的 URL。https://speakerdeck.com/util/reading-files-cant-be-this-simple
https://raku.org
特别感谢用户 @JoL 在此答案中对原始正则表达式进行了深刻的批评。
您可以将包含
pattern
该行及其上方的行存储在一个变量中。然后你可以使用这个变量在你的文件中再次 grep。