【问题标题】:grep to exclude pattern and exclude 2 preceding linesgrep 排除模式并排除前 2 行
【发布时间】:2018-08-08 20:01:01
【问题描述】:

我有一个文件,想用 grep 排除一个模式。但我也想删除每场比赛的前两行(排除在外)。我该怎么做?

我尝试过的:

cat file.txt
Sequence: MG719312_IGHV1-8*03_Homosapiens_F_V-REGION_127..422_296nt_1_____296+0=296___     from: 1   to: 296
  Start     End  Strand Pattern                 Mismatch Sequence
    217     225       + pattern:AA[CT]NNN[AT]CN        . aacacctcc
Sequence: M99648_IGHV2-26*01_Homosapiens_F_V-REGION_164..464_301nt_1_____301+0=301___     from: 1   to: 301
  Start     End  Strand Pattern                 Mismatch Sequence
    176     184       + pattern:AA[CT]NNN[AT]CN        . aatcctaca

# With grep -v I can remove the line with pattern

grep -v "[acgt]\{3\}cc[acgt][acgt]\{3\}" file.txt
Sequence: MG719312_IGHV1-8*03_Homosapiens_F_V-REGION_127..422_296nt_1_____296+0=296___ from: 1 to: 296
Start End Strand Pattern Mismatch Sequence
217 225 + pattern:AA[CT]NNN[AT]CN . aacacctcc
Sequence: M99648_IGHV2-26*01_Homosapiens_F_V-REGION_164..464_301nt_1_____301+0=301___ from: 1 to: 301
Start End Strand Pattern Mismatch Sequence

# But using -B 2 does not work here

grep -B 2 -v "[acgt]\{3\}cc[acgt][acgt]\{3\}" file.txt
Sequence: MG719312_IGHV1-8*03_Homosapiens_F_V-REGION_127..422_296nt_1_____296+0=296___ from: 1 to: 296
Start End Strand Pattern Mismatch Sequence
217 225 + pattern:AA[CT]NNN[AT]CN . aacacctcc
Sequence: M99648_IGHV2-26*01_Homosapiens_F_V-REGION_164..464_301nt_1_____301+0=301___ from: 1 to: 301
Start End Strand Pattern Mismatch Sequence

任何想法如何删除每场比赛的前两行?

【问题讨论】:

  • How do I delete a matching line and the previous one? 的可能重复项 - 问题有 -B 1 而不是 -B 2,但答案将直接适用。
  • 示例文件有一个清晰的记录结构,我会警惕尝试使用面向行的命令行工具,如grepsed,一起破解一些东西。看着它,我很想编写一个 Perl 脚本来解析序列记录并匹配它们。
  • @tripleee,感谢您指导我找到可能的副本。那里的最佳答案适用于前一行,但不适用于 2。
  • @b.nota 如果您指的是sed 答案,请尝试将“1d”替换为“2d”。
  • 2d 不正确:sed -n '/[acgt]\{3\}cc[acgt][acgt]\{3\}/{n;x;d;};x;2d;p;${x;p;}' file.txt Start End Strand Pattern Mismatch Sequence 217 225 + pattern:AA[CT]NNN[AT]CN . aacacctcc Sequence: M99648_IGHV2-26*01_Homosapiens_F_V-REGION_164..464_301nt_1_____301+0=301___ from: 1 to: 301

标签: bash grep


【解决方案1】:

GNU sed 上测试,语法/功能可能因其他实现而异

sed -E 'N;N; /[acgt]{3}cc[acgt][acgt]{3}/d' ip.txt
  • -E 使用 ERE,某些 sed 版本需要 -r 而不是 -E
  • N;N 在模式空间中再追加两行
  • /[acgt]{3}cc[acgt][acgt]{3}/d 如果条件匹配则删除
    • 请注意,这将尝试匹配三行中任何位置的正则表达式...此外,[acgt][acgt]{3} 可以简化为[acgt]{4}
    • /\n.*\n.*[acgt]{3}cc[acgt][acgt]{3}/d 将限制为仅匹配第 3 行

【讨论】:

  • 很好的解决方案,谢谢。您对{4} 部分是正确的,但是对于密码子(在生物学中),{3} 更合乎逻辑。
  • @b.nota 只是好奇-您是否考虑过在需要时如何调整该解决方案,例如跳过 10 行而不是 3 行或需要打印第一行或他们中的最后一个或打印跳过的块数或执行任何其他操作或仅在一个位置/字段中测试正则表达式,或者您移动到没有 GNU sed 的平台或....?显然,我建议您甚至不应该考虑为此使用 sed - 这只是 awk 的工作。
  • @EdMorton,我尝试从对我有用的脚本/代码中进行调整,在那里我可以理解基础知识,我认为答案中的解释是必不可少的。我希望您意识到我不是每天都在使用 sedawkgrep,但我喜欢使用(并了解更多)这些工具。
  • 明白,但就像在生活中一样,为正确的工作使用正确的工具并学会使用它们是有意义的。花时间成为最好的使用螺丝刀砍树是完全可行的,但大多数人会建议不要这样做,并且可能会建议使用锯子。我要说的是了解哪些工具适合哪些工作,然后学习如何将它们用于他们最擅长的事情,不要选择一种你知道如何用于一项任务的工具并假设你应该将它用于其他任务,即使可以这样做。
  • 我绝对不是建议你改变你接受的答案顺便说一句,只是想引导你走上正确的道路,了解如何思考 UNIX 工具以及 grep 的三大文本处理工具, sed 和 awk 最适合用于。
【解决方案2】:

你只需要:

tac file | awk '/regexp/{c=3} !(c&&c--)' | tac

显然将regexp 设置为您想要匹配的任何正则表达式,并将3 更改为您想要跳过的任何行,包括匹配的行。例如跳过包含7 的每一行及其之前的 4 行:

$ seq 20 | tac | awk '/7/{c=5} !(c&&c--)' | tac
1
2
8
9
10
11
12
18
19
20

请参阅https://stackoverflow.com/a/17914105/1745001,了解如何在匹配行周围打印您喜欢的任何行。

用你的例子:

$ tac file | awk '/[acgt]{3}cc[acgt][acgt]{3}/{c=3} !(c&&c--)' | tac
Sequence: MG719312_IGHV1-8*03_Homosapiens_F_V-REGION_127..422_296nt_1_____296+0=296___     from: 1   to: 296
  Start     End  Strand Pattern                 Mismatch Sequence
    217     225       + pattern:AA[CT]NNN[AT]CN        . aacacctcc

现在,您可能需要为数据考虑一些事情:

$ cat tst.awk
++lineNr == 1 {
    delete fldNr2tag
    delete tagNr2tag
    delete tag2val
    numTags = 0

    for (i=1; i<=NF; i+=2) {
        sub(/:.*/,"",$i)
        tag = $i (i>1 ? "" : 1) # to distinguish the 2 "Sequence" tags
        val = $(i+1)
        tagNr2tag[++numTags] = tag
        tag2val[tag] = val
    }
}
lineNr == 2 {
    for (i=1; i<=NF; i++) {
        tag = $i
        fldNr2tag[i] = tag
    }
}
lineNr == 3 {
    for (i=1; i<=NF; i++) {
        tag = fldNr2tag[i]
        val = $i
        tagNr2tag[++numTags] = tag
        tag2val[tag] = val
    }

    prt()

    lineNr = 0
}

function prt(   tagNr, tag, val) {
    for (tagNr=1; tagNr<=numTags; tagNr++) {
        tag = tagNr2tag[tagNr]
        val = tag2val[tag]
        printf "tag2val[%s] = <%s>\n", tag, val
    }
    print "----"
}

.

$ awk -f tst.awk file
tag2val[Sequence1] = <MG719312_IGHV1-8*03_Homosapiens_F_V-REGION_127..422_296nt_1_____296+0=296___>
tag2val[from] = <1>
tag2val[to] = <296>
tag2val[Start] = <217>
tag2val[End] = <225>
tag2val[Strand] = <+>
tag2val[Pattern] = <pattern:AA[CT]NNN[AT]CN>
tag2val[Mismatch] = <.>
tag2val[Sequence] = <aacacctcc>
----
tag2val[Sequence1] = <M99648_IGHV2-26*01_Homosapiens_F_V-REGION_164..464_301nt_1_____301+0=301___>
tag2val[from] = <1>
tag2val[to] = <301>
tag2val[Start] = <176>
tag2val[End] = <184>
tag2val[Strand] = <+>
tag2val[Pattern] = <pattern:AA[CT]NNN[AT]CN>
tag2val[Mismatch] = <.>
tag2val[Sequence] = <aatcctaca>
----

请注意,使用上述方法,您可以通过其名称访问每个值,从而从比较或其他计算中删除不精确和/或错误匹配,您可以选择特定字段以您喜欢的任何顺序打印,只需使用字段名称,例如print tag2val["Sequence"], tag2val["Pattern"]。因此,您可以轻松地将数据转换为 CSV 以导入 Excel 或转换为 HTML 或 JSON,或者用它做任何其他事情。

【讨论】:

  • 这不起作用:tac file.txt | awk '/[acgt]\{3\}cc[acgt][acgt]\{3\}/{c=3} !(c&amp;&amp;c--)' | tac
  • 正确,因为[acgt]\{3\}cc[acgt][acgt]\{3\} 不是有效的 ERE,甚至也不是 POSIX BRE,它是 BRE 的不可移植版本,添加了用于激活 ERE 元字符的构造,仅在 GNU sed 中有效在没有 -E 或 -r 参数的情况下调用时。 awk 只适用于 POSIX ERE。我更新了我的答案以显示它使用有效的正则表达式。
  • 感谢您的帮助。
  • 不客气。但是,当您的输入中有名称-值对时,最好的方法通常是创建一个数组,首先将名称映射到它们的值,然后通过它们的名称访问这些值。因此,我更新了我的答案,向您展示了一个执行此操作的脚本。 真的您应该如何处理您的数据,所以如果您不了解其中的好处,请随时提问。
【解决方案3】:

查看示例文件,它似乎有一个面向记录的结构,所以我会非常谨慎地尝试使用面向行的工具(例如grepsed)来操作它。正如 cmets 中指出的那样,there is already a similar problem in with a solution in sed,但脚本并不漂亮,维护或扩展将是一场噩梦。

我很想编写一个简短的 Perl 或 Python 脚本来将文件解析为记录,然后处理这些记录。我不知道文件格式的细节,但是像下面这样的东西可能是一个好的开始,并且会产生你想要的输出。

#!/usr/bin/perl -w

use strict;

my $line = <>;
unless (defined($line) && $line =~ /^Sequence/) {
    die "expected line to start with Sequence";
}
while (defined($line)) {
    my $record = $line;
    $line = <>;
    while (defined($line) && $line !~ /^Sequence/) {
        $record .= $line;
        $line = <>;
    }
    print $record unless $record =~ /[acgt]{3}cc[acgt][acgt]{3}/;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-10
    • 2013-08-30
    • 1970-01-01
    • 2014-10-25
    • 1970-01-01
    • 2023-04-01
    • 2014-07-30
    • 2014-02-28
    相关资源
    最近更新 更多