我有一个文本文件,并且有一个我希望 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
答案1
我倾向于只grep
在从文件中提取单行时使用,因此当我需要在文本中执行更复杂的编辑时,我会使用其他工具。
这里的所有解决方案都假设该模式可能在文本中出现多次,并将删除出现该模式的行以及紧邻它们之前的行。如果模式在连续行上匹配,前两个解决方案将会出现问题。
您可以使用sed
来匹配模式/pattern/
并让它触发命令N
and d
,它将下一行追加到缓冲区,然后丢弃两者:
sed '/pattern/ { N; d; }' file
因为你想丢弃该行前为了匹配模式,我们将数据向后输入sed
,从最后一行开始并向文件开头移动。sed
完成后我们再次反转数据。
tac file | sed '/pattern/ { N; d; }' | tac
该tac
实用程序是 GNU coreutils 的一部分。大多数非 GNU 系统可以使用tail -r
来代替tac
(检查你的tail(1)
手册)。
如果模式匹配两个连续行,则这将无法删除第一行之前的行(因为第一行将被删除)。
使用ed
编辑器:
printf '%s\n' 'g/pattern/ -1,. d' ,p Q | ed -s file
这会将命令应用于g/pattern/ -1,. d
文件的内容。此命令搜索与 匹配的每一行pattern
,然后删除该行及其前面的行。
最后的,p
编辑Q
命令打印整个文件并退出编辑器而不保存。
如果模式匹配两个连续行,则在删除第一行之前的行后,这将删除第二行之前的行。
(最后一句话是正确的当我写的时候,但这显然是一个只写的句子。)
我们还可以使用grep
其非标准但常用的-B
选项来为我们提供需要删除的行号。这些数字可以转换为sed
我们在原始数据上运行的脚本:
grep -n -B1 'pattern' file | sed 's/[:-].*/d/' | sed -f /dev/stdin file
grep
根据问题中的文本,该命令将输出
2-line 2
3:pattern
...第一个sed
命令将其转换为sed
编辑命令,2d
然后是3d
(“删除第 2 行和第 3 行”)。sed
管道中的最后一个命令采用此编辑脚本并将其应用到原始文本。
此变体不存在与模式匹配的连续行的问题,因为它使用一种 2-pass 方法,首先找到应删除的所有行,然后删除它们(而不是在第一次阅读文本时删除行)。
答案2
将任何 awk 与 tac 一起使用,您可以删除匹配模式之前的任意数量的行:
$ tac file | awk '/pattern/{c=2} !(c&&c--)' file | tac
line 2
line 1
只需更改c=2
为c=5
或任意要删除的行数(最多包括匹配行),例如删除包含数字 97 及其之前的 94 行的行:
$ seq 100 | tac | awk '/97/{c=95} !(c&&c--)' | tac
1
2
98
99
100
现在尝试使用 sed 而不是 awk 来完成此操作:-)。
看使用 sed-或 awk-a-line-follow-a-matching-pattern 进行打印对此和其他相关习语的解释。
答案3
笔记:仅当 中file
不存在与 的输出匹配的重复行或每行的子字符串时,此代码才有效grep -B1 pattern file
。
例如,如果file
包含以下行:
line 1
line 2
line 2
pattern
line 1 line 2
line 3
而且我使用的grep -B1 pattern file | grep -v "$(cat)" file
输出不会如你所料:
line 1
line 3
解决这个问题的最好方法是使用拘萨罗南达的回答
解决方案(这仅适用于没有重复行或子字符串的情况,正如我上面所解释的)
这对bash
我有用(我认为有更好的方法):
grep -B1 pattern file | grep -v "$(cat)" file
在zsh
上面的命令中将不起作用。我不知道为什么。但你可以使用:
grep -B1 pattern file | { val="$(cat)" ; grep -v "$val" file; }
聚苯乙烯你不必使用cat your_file | grep pattern
它,这是多余的。你应该使用grep pattern your_file
答案4
您可以使用pcregrep
及其M
ultiline 模式:
pcregrep -Mv '\n.*pattern'
请注意,如果第一行与模式匹配,则不会将其删除。这可以通过使用来解决:
pcregrep -Mv '(\n)?.*pattern'
((...)
周围\n
显然是必要的,我不知道为什么它不能与8.39 版本一起使用)\n?.*pattern
。[\n]?.*pattern