Awk 如果字符串匹配且下一行包含另一个字符串,则删除该行

Awk 如果字符串匹配且下一行包含另一个字符串,则删除该行,awk,sed,grep,Awk,Sed,Grep,遇到了一个恼人的文本操作问题,如果文件中有一行包含字符串,我需要删除该行,但仅当下一行还包含另一个字符串时。例如,我有以下几行: john paul george john paul 12 john paul 我想删除任何包含“john paul”的行,如果它后面紧跟一行包含“george”,那么它将返回: george john paul 12 john paul 不知道该如何对此进行grep或sed。如果有人能帮忙,那就太好了 您可能必须将\r\n更改为\n或\r,而不是这样做: <

遇到了一个恼人的文本操作问题,如果文件中有一行包含字符串,我需要删除该行,但仅当下一行还包含另一个字符串时。例如,我有以下几行:

john paul
george
john paul
12
john paul
我想删除任何包含“john paul”的行,如果它后面紧跟一行包含“george”,那么它将返回:

george
john paul
12
john paul

不知道该如何对此进行grep或sed。如果有人能帮忙,那就太好了

您可能必须将\r\n更改为\n或\r,而不是这样做:

<?php
$string = "john paul
george
john paul
12
john paul";

$string = preg_replace("#john paul\r\n(george)#i",'$1',$string);

echo $string;
?>
您还可以将文件读入变量,然后在覆盖该文件后执行此操作。

此awk应执行以下操作:

cat file
john paul
george
john paul
12
john paul
hans
george
这只会删除乔治上面的名字,如果是约翰·保罗

输出:

george
john paul
12
john paul

这里有一个更一般的版本: 如果行与字符串匹配,并且前一行正好是john paul,则不执行任何操作,否则,打印前一行。如果只希望检测到george,请将^[a-zA-Z]$部分更改为george

awk '!(/^[a-zA-W]+$/ && previous ~/^john paul$/){print previous}{previous=$0}END{print}'
在您的示例中:

$> echo 'john paul
george
john paul
12
john paul' |awk '!(/^[a-zA-W]+$/ && previous ~/^john paul$/){print previous}{previous=$0}END{print}'

george
john paul
12
john paul
如果行中有一些数字,则打印上一行,否则不会:

$> echo 'john paul
george 234
john paul
auie
john paul' |awk '!(/^[a-zA-W]+$/ && previous ~/^john paul$/){print previous}{previous=$0}END{print}'

john paul
george 234
auie
john paul

这可能适用于GNU sed:

sed '/john paul/{$!N;/\n.*george/!P;D}' file
如果该行包含约翰·保罗,请阅读下一行,如果包含乔治,请不要打印第一行

注意:如果包含george的行中包含john paul,则也将对其进行检查。

sed解决方案很短:两个命令和大量注释

/john paul/ {
        # read the next line and append to pattern space
        N   
        # and then if we find "george" in that next line,
        # only retain the last line in the pattern space
        s/.*\n\(.*george\)/\1/
        # and finally print the pattern space,
        # as we don't use the -n option
}
将上述内容放入某个sedscript文件中,然后运行:

sed -f sedscript your_input_file

使用GNU awk进行多字符RS:

$ gawk -vRS='^$' '{gsub(/john paul\ngeorge/,"george")}1' file
george
john paul
12
john paul
或者,如果每一行的内容都超过了示例输入显示的内容,只需更改RE以适应并使用gensub:

$ gawk -vRS='^$' '{$0 = gensub(/[^\n]*john paul[^\n]*\n([^\n]*george[^\n]*)/,"\\1","")}1' file
george
john paul
12
john paul

只需在混合中加入一些Perl:

perl -ne 'print $p unless /george/ && $p =~ /john paul/; $p = $_ }{ print $p' file

打印上一行,除非当前行匹配/george/和上一行$p匹配/john paul/。将$p设置为前一行的值。}{有效地创建了一个结束块,因此最后一行也会在文件读取后打印。

这与konsolebox的解决方案相同,只是您在开始时缺少NR>1&&以防止出现一个空白行。嗨,我在下面发布sed解决方案时,还没有看到这个漂亮而简短的答案……请您解释一下在$!N?中,当D命令删除模式空间的第一行时,它将同时加载新行。我在这里吗?这不是一个潜在的问题吗?@jaybee$!N意味着当您在文件末尾时,不要尝试获取下一行。D命令将删除模式空间中的第一行,如果模式空间为空,则调用e获取下一行的正常sed循环等。如果模式空间不为空,则调用sed脚本,跳过获取下一行和剥离换行的正常过程。grep用于在一行上查找regexp。sed用于替换一行上的regexp。对于任何其他内容,awk都是正确的工具。
$ gawk -vRS='^$' '{$0 = gensub(/[^\n]*john paul[^\n]*\n([^\n]*george[^\n]*)/,"\\1","")}1' file
george
john paul
12
john paul
perl -ne 'print $p unless /george/ && $p =~ /john paul/; $p = $_ }{ print $p' file