使用bash从字符串中获取某些模式

时间:2011-12-20 03:25:57

标签: string grep design-patterns

这可能是问题的延伸: Incorporate variables into bash code line

我刚才在文字中意识到,行实际上是变量格式的。

2   118610455   P2_PM_2_5034    T   <DUP:TANDEM>    40  .   END=118610566;SVLEN=110;SVTYPE=TDUP;CIPOS=-100,55;CIEND=-56,100;IMPRECISE;DBVARID=esv7540;VALIDATED;VALMETHOD=CGH;SVMETHOD=RP 

1   859214  P2_M_061510_1_73    C   <DEL>   .   .   CIEND=-130,50;CIPOS=-57,93;END=860180;IMPRECISE;SVLEN=-966;SVTYPE=DEL;VALIDATED;DBVARID=esv10036;VALMETHOD=CGH;SVMETHOD=RD,RP

我需要的是

2 118610455 118610566
1 859214 860180

如上所示,此"END=#"可能位于第8列的不同位置。所以基本上我需要先从第8列找到“END = ..”部分,然后grep这个数字。 所以这实际上是关于如何从字符串grep特定模式(在这种情况下,模式是“END =”)

但我怎么能这样做? THX

3 个答案:

答案 0 :(得分:1)

grep的:

您可以使用-o的{​​{1}}选项进行搜索:

<强>测试

grep

但是如果你正在寻找一个完整的解决方案,那么如何使用[jaypal:~/Temp] grep -o "END=[0-9]\+;" file | tr -ds 'END=|;' '' 118610566 860180 (对不起,我知道这不是你的要求。但这里有两个解决方案:

awk中:

如果您想要的第一个和第二个参数的位置不变,那么我们可以在特定字段中拆分每个值,然后遍历每个参数。只要我们到达awk字段,我们就会打印$ 1和$ 4,然后打印END旁边的列。

END

<强>测试

awk -v FS="[ ;=]" '{for(i=1;i<=NF;i++) if ($i=="END") print $1,$4,$(i+1)}' file

GNU AWK:

如果你有[jaypal:~/Temp] cat file 2 118610455 P2_PM_2_5034 T <DUP:TANDEM> 40 . END=118610566;SVLEN=110;SVTYPE=TDUP;CIPOS=-100,55;CIEND=-56,100;IMPRECISE;DBVARID=esv7540;VALIDATED;VALMETHOD=CGH;SVMETHOD=RP 1 859214 P2_M_061510_1_73 C <DEL> . . CIEND=-130,50;CIPOS=-57,93;END=860180;IMPRECISE;SVLEN=-966;SVTYPE=DEL;VALIDATED;DBVARID=esv10036;VALMETHOD=CGH;SVMETHOD=RD,RP [jaypal:~/Temp] awk -v FS="[ ;=]" '{for(i=1;i<=NF;i++) if ($i=="END") print $1,$4,$(i+1)}' file 2 118610455 118610566 1 859214 860180 ,那么它有一个名为gawk的内置函数。这支持反向引用。所以你也可以做以下事情 -

gensub

<强>测试

gawk '{print $1,$2,gensub(/.*\<END\>=(.[^;]*);.*/,"\\1",$0)}' file

答案 1 :(得分:0)

使用sed:

$ cat input | sed -e 's/^\([0-9]\+\) \+\([0-9]\+\) .*\<END=\([0-9]\+\).*/\1 \2 \3/'

答案 2 :(得分:0)

您可以使用perl脚本,例如:

pax> perl -ne '{
         @arr=split;
         if (@arr[7] =~ /^END=/) {
             @arr[7] =~ s/^END=//;
         } else {
             @arr[7] =~ s/^.*;END=//;
         }
         @arr[7] =~ s/;.*$//;
         printf "%s %s %s\n", @arr[0], @arr[1], @arr[7];
     }' <qq.in
2 118610455 118610566
1 859214 860180

为了便于阅读,我已将该脚本格式化,但您可以轻松使用单行:

perl -ne '{@arr=split;if (@arr[7] =~ /^END=/) {@arr[7] =~ s/^END=//;} else {@arr[7] =~ s/^.*;END=//;} @arr[7] =~ s/;.*$//; printf "%s %s %s\n", @arr[0], @arr[1], @arr[7];}' <qq.in

一旦你理解它,它的工作方式很简单。 split为您提供了一行元素,您只需稍微修改数字7即可。

如果它以END=开头,那就去掉那个位。否则,如果一切都包括;END=

然后在第一个;之后删除所有内容(在已经修改过的版本中,在开始时N位为END=N。)

然后打印出三个所需的值。


考虑到这一点,可能会更好一点,比如:

pax> perl -ne '{
        ($a,$b,$x,$x,$x,$x,$x,$c,$x) = split;
        $c = ";$c";
        $c =~ s/^.*;END=//;
        $c =~ s/;.*$//;
        print "$a $b $c\n";
    }' <qq.in

或等效的单行:

perl -ne '{($a,$b,$x,$x,$x,$x,$x,$c,$x)=split;$c=";$c";$c=~s/^.*;END=//;$c=~s/;.*$//;print "$a $b $c\n";}' <qq.in