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