我有一个包含以下信息的文件:
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
指令。的基本语法sed
是address command
where 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
我无法抗拒给予珀尔当我们有时回答sed和awk回答!
# 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 -- "$@"