Shell 在bash(使用sed或awk)中删除匹配前后的行?

Shell 在bash(使用sed或awk)中删除匹配前后的行?,shell,sed,awk,Shell,Sed,Awk,我试图从一个充满事务的文件中删除模式匹配两边的两行。即:找到匹配项,然后删除它前面的两行,然后删除它后面的两行,然后删除匹配项。将其写回原始文件 所以输入数据是 D28/10/2011 T-3.48 PINITIAL BALANCE M ^ 我的模式是 sed -i '/PINITIAL BALANCE/,+2d' test.txt 但是,这只是在模式匹配后删除两行,然后删除模式匹配。我无法找到任何逻辑方法使用sed从原始文件中删除所有5行数据。对于这样的任务,我可能会使用更高级的工具,如P

我试图从一个充满事务的文件中删除模式匹配两边的两行。即:找到匹配项,然后删除它前面的两行,然后删除它后面的两行,然后删除匹配项。将其写回原始文件

所以输入数据是

D28/10/2011
T-3.48
PINITIAL BALANCE
M
^
我的模式是

sed -i '/PINITIAL BALANCE/,+2d' test.txt

但是,这只是在模式匹配后删除两行,然后删除模式匹配。我无法找到任何逻辑方法使用sed从原始文件中删除所有5行数据。

对于这样的任务,我可能会使用更高级的工具,如Perl:

perl -ne 'push @x, $_;
          if (@x > 4) {
              if ($x[2] =~ /PINITIAL BALANCE/) { undef @x }
                  else { print shift @x }
          }
          END { print @x }' input-file > output-file

这将从输入文件中删除5行。这些线将是赛前的两条线,匹配的线,以及赛后的两条线。您可以通过修改
@x>4
(这将删除5行)和修改
$x[2]
(这将删除第三行上的匹配项,从而在匹配之前删除两行)。

awk单行程序可以执行以下操作:

awk '/PINITIAL BALANCE/{for(x=NR-2;x<=NR+2;x++)d[x];}{a[NR]=$0}END{for(i=1;i<=NR;i++)if(!(i in d))print a[i]}' file

awk'/p初始余额/{for(x=NR-2;x
sed
将执行以下操作:

sed '/\n/!N;/\n.*\n/!N;/\n.*\n.*PINITIAL BALANCE/{$d;N;N;d};P;D'
它是这样工作的:

  • 如果sed在模式空间中只有一个字符串,它将连接另一个字符串
  • 如果只有两个,它将加入第三个
  • 如果它不使用平衡模式行+行+行,它将连接以下两个字符串,删除它们并从开头开始
  • 如果不是,它将打印模式中的第一个字符串,并删除它,然后在不刷模式空间的情况下从开头开始
要防止模式出现在第一个字符串上,应修改脚本:

sed '1{/PINITIAL BALANCE/{N;N;d}};/\n/!N;/\n.*\n/!N;/\n.*\n.*PINITIAL BALANCE/{$d;N;N;d};P;D'

但是,如果您的字符串中有另一个将被删除的
p初始余额
,它将失败。但是,其他解决方案也将失败=)

将此代码保存到文件
grep.sed

H
s:.*::
x
s:^\n::
:r
/PINITIAL BALANCE/ {
    N
    N
    d    
}

/.*\n.*\n/ {
    P
    D
}
x
d
并运行如下命令:

`sed -i -f grep.sed FILE`
您可以使用它,以便:

sed -i 'H;s:.*::;x;s:^\n::;:r;/PINITIAL BALANCE/{N;N;d;};/.*\n.*\n/{P;D;};x;d' FILE
这可能适用于您(GNU-sed):


更简单易懂的解决方案可能是:

awk '/PINITIAL BALANCE/ {print NR-2 "," NR+2 "d"}' input_filename \
    | sed -f - input_filename > output_filename
awk用于生成一个sed脚本,删除有问题的行,结果写入输出文件名


这使用了两个可能比其他答案效率更低的过程。

感谢choroba的建议。我以前从未用Perl编程过……在bash中没有办法做到这一点吗?如果可能的话,在bash中也有可能。但这不值得付出努力……感谢awk oneliner Kent。这看起来非常复杂。这很简单如果你能提供一点解释,那就太棒了?@juliushibert short Explainion在
N;N;d
块内
/bar/
之前添加了d
$d
,如果
字符串是最后一个,它将非常有效。
sed -i 'H;s:.*::;x;s:^\n::;:r;/PINITIAL BALANCE/{N;N;d;};/.*\n.*\n/{P;D;};x;d' FILE
sed ':a;$q;N;s/\n/&/2;Ta;/\nPINITIAL BALANCE$/!{P;D};$q;N;$q;N;d' file
awk '/PINITIAL BALANCE/ {print NR-2 "," NR+2 "d"}' input_filename \
    | sed -f - input_filename > output_filename