每隔X行Sed,Y行之间进行GREP

时间:2018-09-20 20:41:27

标签: bash perl awk sed grep

我有一个文件,该文件基本上包含如下所示的值的排列方式(我已对行进行编号。)

! MATCH       1
!             2
HIT           3
NUM     1     4
VAL A  82     5
LEU A 144     6
ALA A 154     7
VAL A 333     8
ALA A 334     9
PHE A 372     10
END           11
!             12

我正在尝试创建一个文件,其中包含所有这些实例,其中这些值块在第10行中包含上述的PHE(与ALA或VAL等相对)。

文件中多个值块的简短示例:

! MATCH 
!
HIT 
NUM     1
VAL A 184
PHE A 209
END 
!
! MATCH
!
HIT
NUM     1
LEU A 296
ILE A 321
END 
!
! MATCH
!
HIT 
NUM     1
LEU A 296
PHE A 321
END 
!

我尝试执行此操作的代码是:

sed -n '23~12p' file.txt | grep -B 9 -A 2 PHE > newfile.txt

基本上,从第23行开始,每12行跳过一次(以便仅查看值块的第10行),然后grep前9行,如果值块的第10行中存在PHE,则grep接下来的2行。

但是,我敢肯定,您可以说,上面的代码仅在sed输出中输出前几行。

sed -n '23~12p' file.txt | grep -B 9 -A 2 PHE file.txt > newfile.txt

但是,如果我为grep添加文件(file.txt),即使PHE不在值块的第十行,它也会忽略sed输出,而是抓紧前几行。

IE:

ILE A 222
END
!
! MATCH
!
HIT
NUM     1
ILE A 605
ILE A 620
PHE A 644   <--- What grep is matching
VAL A 633
ALA A 634

我对如何编写此脚本以在我要查找的位置(位置10)搜索PHE,每12行查找一次,并grepping整个值块(前9个)感到有些困惑行和随后的2行)仅在位置10处找到PHE时。

将非常感谢任何建议!谢谢!

4 个答案:

答案 0 :(得分:3)

假定这些块用空行分隔

perl -00 -wne'print if (split /\n/)[9] =~ /^PHE/' data.txt

有关命令行开关,请参见perlrun。在这里,-00将输入分为几段,然后在special variable $_''下的程序中可用。那是换行符上的split,第十行用正则表达式检查它是否以PHE开头。如果是,我们将打印整个块。


已阐明,没有专用的块分隔符;块彼此接连出现,每个块都以! MATCH行开始,以!行结束。

然后,由于$/的{​​{1}}(可通过!开关设置)会引入虚假的输入记录,因此上述内容在过滤后无法轻易保留整个块。而是逐行处理。

使用问题更新中添加的数据示例

-0\x21

每行都添加到缓冲区(或“块”)perl -ne' if (/^\! MATCH/ or eof) { $b[5]=~/^PHE/ and print for @b; @b=() }; push @b, $_ ' data.txt 中。以@b开头的行开始一个新块,因此,如果它的第六行以! MATCH开头(在真实数据PHE中),则打印前一个,并清空下一个块的缓冲区。

$b[9]是必需的,因此对于输入的最后一个块/缓冲区,也要这样做。

我可以建议在写入此文件时在记录之间插入空白行。

答案 1 :(得分:1)

听起来这可能是您要尝试执行的操作:

$ awk '
    { recLine = NR%8 }
    { rec = (recLine==1 ? "" : rec ORS) $0 }
    recLine==6 { f = /PHE/ }
    (recLine==0) && f { print rec }
' file
! MATCH
!
HIT
NUM     1
VAL A 184
PHE A 209
END
!
! MATCH
!
HIT
NUM     1
LEU A 296
PHE A 321
END
!

只需将您的真实数据更改为8到12和6到10。

答案 2 :(得分:1)

这是一个易于理解和可扩展的脚本。

#!/usr/bin/env perl
use strict;
my $matchNum=0;
my @match;
while (<STDIN>) {
  chomp;
  if (/^! MATCH$/) {
    @match and checkMatch(\@match, \$matchNum);
    @match=($_);
  } else { push @match, $_ }
}
@match and checkMatch(\@match, \$matchNum);

sub checkMatch {
  my ($matchAR, $matchNumSR) = @_;
  ++$$matchNumSR;
  if ( $matchAR->[9] =~ /^PHE/ ) {
    print "Match $$matchNumSR = $matchAR->[9]\n";
  }
}

答案 3 :(得分:1)

这可能对您有用(GNU sed):

sed -n '14~12{h;b};H;23~12{/^PHE/!{x;z;x}};25~12{x;/^\n/!p;x}' file

设置类似grep的选项-n。从第14行开始,然后以12为模,将保持空间设置为当前行并脱离sed脚本。对于所有其他行,请将当前行追加到保留空间。在第23行和其后的模12处,检查当前行以开始PHE,如果没有清除保留空间,请检查。在第25行和其后的模12处,检查保留空间,如果它不是以换行开头,则在保留空间中打印所有12行。

如果在第23行和以12为模,及其后的行,当前行没有开始PHE,则保持将被清除,并添加后续行。附加的行之前有换行符,因此,如果保留空间以换行符开头,则对PHE的检查将失败,并且可以丢弃这些行。

替代方法:

sed -r '1,13d;:a;N;s/[^\n]*/&/12;Ta;/^([^\n]*\n){9}PHE/p;d' file

删除前13行。聚集12行,并在第10行以PHE开始时打印它们。