删除从特定单词开始直到下一个相似块(下一个相似的“节标题”)的行块

删除从特定单词开始直到下一个相似块(下一个相似的“节标题”)的行块

我有一个包含以下信息的文件:

     gene            3025..3855
                     /gene="Sp34_10000100"
                     /ID="Sp34_10000100"
     CDS             join(3025..3106,3722..3855)
                     /gene="Sp34_10000100"
                     /codon_start=1
                     /ID="Sp34_10000100.t1.cds1,Sp34_10000100.t1.cds2"
     mRNA            3025..3855
                     /ID="Sp34_10000100.t1"
                     /gene="Sp34_10000100"
     gene            12640..13470
                     /gene="Sp34_10000200"
                     /ID="Sp34_10000200"
     CDS             join(12640..12721,13337..13470)
                     /gene="Sp34_10000200"
                     /codon_start=1
                     /ID="Sp34_10000200.t1.cds1,Sp34_10000200.t1.cds2"
     mRNA            12640..13470
                     /ID="Sp34_10000200.t1"
                     /gene="Sp34_10000200"
     gene            15959..20678
                     /gene="Sp34_10000300"
                     /ID="Sp34_10000300"
     CDS             join(15959..16080,16268..16367,18913..19116,20469..20524,20582..20678)
                     /gene="Sp34_10000300"
                     /codon_start=1
                     /ID="Sp34_10000300.t1.cds1,Sp34_10000300.t1.cds2,Sp34_10000300.t1.cds3,Sp34_10000300.t1.cds4,Sp34_10000300.t1.cds5"
     mRNA            15959..20678
                     /ID="Sp34_10000300.t1"
                     /gene="Sp34_10000300"
     gene            22255..23085
                     /gene="Sp34_10000400"
                     /ID="Sp34_10000400"

我想删除所有基因部分但是CDS信使RNA信息应该在那里。输出应该是这样的:

     CDS             join(3025..3106,3722..3855)
                     /gene="Sp34_10000100"
                     /codon_start=1
                     /ID="Sp34_10000100.t1.cds1,Sp34_10000100.t1.cds2"
     mRNA            3025..3855
                     /ID="Sp34_10000100.t1"
                     /gene="Sp34_10000100"
     CDS             join(12640..12721,13337..13470)
                     /gene="Sp34_10000200"
                     /codon_start=1
                     /ID="Sp34_10000200.t1.cds1,Sp34_10000200.t1.cds2"
     mRNA            12640..13470
                     /ID="Sp34_10000200.t1"
                     /gene="Sp34_10000200"
     CDS             join(15959..16080,16268..16367,18913..19116,20469..20524,20582..20678)
                     /gene="Sp34_10000300"
                     /codon_start=1
                     /ID="Sp34_10000300.t1.cds1,Sp34_10000300.t1.cds2,Sp34_10000300.t1.cds3,Sp34_10000300.t1.cds4,Sp34_10000300.t1.cds5"
     mRNA            15959..20678
                     /ID="Sp34_10000300.t1"
                     /gene="Sp34_10000300"

请给我任何建议如何做到这一点。

答案1

awk 通常更容易阅读和理解:

这是一个简单的程序,默认情况下写入,当它看到第一个单词是“gene”的行时,将“wewrite”切换为“0”(=关闭,我们不会写入),并在他看到第一个单词是“gene”时将其重新打开。看到第一个单词是“CDS”或“mRNA”的行:

awk '
  BEGIN                               { weprint=1 }

  ( $1 == "gene" )                    { weprint=0 }
  ( $1 == "CDS" ) || ( $1 == "mRNA" ) { weprint=1 }
  ( weprint == 1)                     { print $0 ;}

  '  file_to_read

BEGIN 在读取任何行之前完成。

另一个( test ) { action if test successful }针对每行输入进行解析(...除非操作包含next,否则它将忽略其余的内容,而是会获取下一行输入)

这只会打印“CDS”和“mRNA”部分,而不打印“基因”部分

这可能是“高尔夫球”(例如,成功“测试”的默认操作是打印 $0,因此您可以像( weprint == 1)最后一行一样,但在我看来,掌握起来不太清楚......)

答案2

sed -e '
   /^ *gene /!b   # print non-gene block begin lines
   :a  
   $d; N          # do-while loop accumulates lines for gene block
   s/\n *\///;ta
   D              # clip the gene block
' yourfile

您需要意识到该sed模型是按行读取文件,并且sed该部分中的命令-e在转换时按顺序应用到该行上,除非涉及branching指令。的基本语法sedaddress commandwhere command 可以是任何有效sed命令,并且address可以是以下任一命令:linenum, $(= 最后一行)​​, regex, , range of addresses, 最后没有任何意义这将应用于所有行。请注意,行存储在名为 的寄存器中pattern space

因此,在完成这些基本内容后,我们将转到sed -e手头的实际代码: b=> 分支到 sed 代码的末尾并打印模式空间。这意味着我们继续打印任何没有(!地址模式之后)将字符串gene作为第一个字段的行。

当我们最终击中gene第一个字段行时,我们设置一个 do-while 循环(:a设置要跳转到的标记)以继续将行累积到模式空间寄存器中(N追加下一行;s命令删除\n *\/,这是换行符,后跟空格和 a /) 直到两个条件中的任何一个不满足,即,要么我们点击 eof => 我们删除它($d=> 如果我们在最后一行,则删除模式空间),因为这出现在 eof 附近且必须删除的基因块。

或者我们到达下一个块的开头:如果s可以找到并删除所述模式,则将t跳转到:a,否则(一个新块,因此未找到该模式),我们继续。现在模式空间保存了整个基因块和下一个块的第一行。我们立即删除基因块,并在下一个块的开始处转到 sed 代码的顶部(这就是命令的作用D)。

答案3

我无法抗拒给予珀尔当我们有时回答sedawk回答!

# make perl complain when it should
use strict;
use warnings;

# declare variable
my $section;

# run through every line
while (<>) {
  # set the current section to 'gene', 'CDS' or 'mRNA' when it matches
  $section = $1 if /^\h*(gene|CDS|mRNA)/;

  # print if the current section is not 'gene'
  print if $section ne 'gene';
}

答案4

这是一个 sed 程序,用于从文件中删除一行块,其中每个块以特定模式的行开始,并在下一个块开始的地方结束。 (我可以称这种块为部分,任务是使用 sed 删除一个部分。)

让我们从一个明显的尝试开始解决这个问题(类似于@Stéphane-Chazelas 评论中的建议),但是这是行不通的:

sed '/^     gene/,/^     [^ ]/ d'

当行首有 5 个空格,后跟一个非空格时,我们的部分结束,下一部分开始。我们的部分以 5 个空格和 开始gene

这个简单的 sed 程序的问题是地址范围也匹配下一节的起始行并将其删除。

但这有效:

end='^     [^ ]'
begin='^     gene'
sed --regexp-extended -e "/$begin/,/$end/ {
        /$end/! d # skip the end, otherwise delete
        /$begin/ d # do not skip (even if it happens to match end)
    }" \
    -i -- "$@"

在您的示例上尝试给出了想要的结果:

$ cp example-stackexchange-360117.txt{.orig,} -vf
'example-stackexchange-360117.txt.orig' -> 'example-stackexchange-360117.txt'
$ ./delete-section2-gene example-stackexchange-360117.txt
$ diff example-stackexchange-360117.txt{.orig,}
1,3d0
<      gene            3025..3855
<                      /gene="Sp34_10000100"
<                      /ID="Sp34_10000100"
11,13d7
<      gene            12640..13470
<                      /gene="Sp34_10000200"
<                      /ID="Sp34_10000200"
21,23d14
<      gene            15959..20678
<                      /gene="Sp34_10000300"
<                      /ID="Sp34_10000300"
31,33d21
<      gene            22255..23085
<                      /gene="Sp34_10000400"
<                      /ID="Sp34_10000400"
$ 

请注意,它甚至神奇地删除了文件末尾的最后一部分,尽管没有后续行与之匹配$end! (从 GNU sed 手册中我并不清楚原因。)

我遇到了类似的问题,为此我提出了这个解决方案:从Python源中删除整个函数定义,即从库/测试/audit-tests.py来自 Python 3.8.1 源。

我使用具有不同模式的相同脚本:

end='^[^[:blank:]]'
begin='^def .*winreg'
sed --regexp-extended -e "/$begin/,/$end/ {
        /$end/! d # skip the end, otherwise delete
        /$begin/ d # do not skip (even if it happens to match end)
    }" \
    -i -- "$@"

它很好地删除了一个函数定义,该定义在缩进再次是顶级缩进的地方结束(即下一节从零缩进开始):

$ git checkout 3.8
$ ../delete-section2 Lib/test/audit-tests.py 
$ git --no-pager diff
diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py
index 33f320992b..ed08612c04 100644
--- a/Lib/test/audit-tests.py
+++ b/Lib/test/audit-tests.py
@@ -304,29 +304,6 @@ def test_unraisablehook():
     write_unraisable_exc(RuntimeError("nonfatal-error"), "for audit hook test", None)


-def test_winreg():
-    from winreg import OpenKey, EnumKey, CloseKey, HKEY_LOCAL_MACHINE
-
-    def hook(event, args):
-        if not event.startswith("winreg."):
-            return
-        print(event, *args)
-
-    sys.addaudithook(hook)
-
-    k = OpenKey(HKEY_LOCAL_MACHINE, "Software")
-    EnumKey(k, 0)
-    try:
-        EnumKey(k, 10000)
-    except OSError:
-        pass
-    else:
-        raise RuntimeError("Expected EnumKey(HKLM, 10000) to fail")
-
-    kv = k.Detach()
-    CloseKey(kv)
-
-
 if __name__ == "__main__":
     from test.libregrtest.setup import suppress_msvcrt_asserts

$ 

此 sed 程序的变体,带有b命令(“分支”):


end='^[^[:blank:]]'
begin='^def .*winreg'
sed --regexp-extended -e "/$begin/,/$end/ {
        /$begin/ d # delete, do not skip
        /$end/ b # skip
        d # default action
    }" \
    -i -- "$@"

相关内容