我有一系列的字符串。它们由标记为“节点”的较小字符串组成,有时单独存在,有时通过字符:
或连接,
。
我想将较大的字符串(“标题”)拆分为“节点”。
我已经设法用 sed 删除了一些额外的字符 ( >
, ;
, '
),并且我使用 awk 来分割剩余的字符串:
和,
问题是我想循环遍历输出(“节点”),而不仅仅是第一个 awk 列。我尝试过使用{print $0}
for awk,但这只是打印出带有分隔符等的初始字符串。
请帮忙?
示例输入(由示例中的 for 循环处理,在较大的代码中,它是 if/else 的输出):
>NODE_3028138_length_2215_cov_1.9513_ID_6056275:NODE_6264558_length_375_cov_4.0000_ID_12529115';
>NODE_4338305_length_1150_cov_1.0000_ID_8676609;
>NODE_3552704_length_509_cov_1.0000_ID_7105407:NODE_4456634_length_439_cov_1.9597_ID_8913267',NODE_4457268_length_491_cov_0.9657_ID_8914535';
示例输出(没有节点 NODE_4338305,因为它是独立的):
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
理想情况下,我想循环遍历上面的每个条目(NODE_3028138_length_2215_cov_1.9513_ID_6056275
,然后NODE_6264558_length_375_cov_4.0000_ID_12529115
等)
for i in ">NODE_3028138_length_2215_cov_1.9513_ID_6056275:NODE_6264558_length_375_cov_4.0000_ID_12529115';" \
">NODE_4338305_length_1150_cov_1.0000_ID_8676609;" \
">NODE_3552704_length_509_cov_1.0000_ID_7105407:NODE_4456634_length_439_cov_1.9597_ID_8913267',NODE_4457268_length_491_cov_0.9657_ID_8914535';"
do
if [[ $i == *":"* ]];
then
echo $i
i=$(sed "s/[>;\']//g" <<< $i);
echo $i
echo $i | awk -F '[:,]' '{print $1}' | while IFS= read -r line; do echo "$line"; done
fi; done
编辑添加操作系统信息:
- 操作系统:CentOS Linux 7(核心)
- 内核:Linux 3.10.0-1127.el7.x86_64
- 架构:x86-64
答案1
您不需要显示任何步骤。如果我理解正确的话,您将从一组 fasta 文件开始,其格式如下所示:
>header
sequence
您想要提取标头,删除>
和 任何内容'
并将它们拆分为,
或;
。如果是这样,您可以直接对 fasta 文件本身执行此操作:
$ sed -n '/^>/{s/>//; s/[,:]/\n/gp}' *.fasta | tr -d "';"
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
解释
sed -n
:抑制正常输出,除非明确告知,否则不打印任何内容。/^>/{something}
:如果该行以 a 开头>
,则执行something
。s/^>//;
>
:从行首删除。s/[,:]/\n/gp
:替换所有(全部因为g
末尾的),
或:
换行符( )然后打印(因为末尾\n
的而打印。p
tr -d "';"
:删除任何;
或'
。
在您的评论中,您说您尝试过'i=$(sed "s/[:,]/\n/g" <<< $i)'
但只得到空格,而不是换行符。那是因为你然后运行echo $i
而不是echo "$i"
,所以换行符丢失了。
如果您确实需要对显示的字符串集合执行此操作,您可以执行以下操作:
for i in ">NODE_3028138_length_2215_cov_1.9513_ID_6056275:NODE_6264558_length_375_cov_4.0000_ID_12529115';" ">NODE_4338305_length_1150_cov_1.0000_ID_8676609;" ">NODE_3552704_length_509_cov_1.0000_ID_7105407:NODE_4456634_length_439_cov_1.9597_ID_8913267',NODE_4457268_length_491_cov_0.9657_ID_8914535';"; do
sed -n '/^>/{s/>//; s/[,:]/\n/gp}' <<<"$i" | tr -d "';" ;
done
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
答案2
以下解决方案有些粗糙,但应该可行。正如您在示例输入中所示,假设所有节点都以字符串开头NODE
(如果不是这种情况,您需要提供更完整的输入示例)。
假设您的字符串实际上位于 file 中input.txt
,则以下awk
调用即可解决问题:
awk '{gsub(/[:>,;\047]/,""); n=split($0,a,/NODE/); for (i=2;i<=n;i++) printf("NODE%s\n",a[i])}' input.txt
gsub()
这将首先使用(\047
是单引号'
,不能逐字放置在命令行上,因为awk
命令本身位于单引号内)替换所有“额外”字符。- 然后,它将剩余的字符串拆分为模式中的字段
NODE
,并将结果存储在数组中a
。 - 然后,除第一个字段(即第一次出现 之前的字符串
NODE
)之外的任何“字段”都将单独打印,并NODE
预先添加。
对于您的示例输入,结果是:
awk '{gsub(/[:>,;\047]/,""); n=split($0,a,/NODE/); for (i=2;i<=n;i++) printf("NODE%s\n",a[i])}' input.txt
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_4338305_length_1150_cov_1.0000_ID_8676609
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
如果你想跳过只包含一个这样的“节点”的行,命令可以修改为:
awk '{gsub(/[:>,;\047]/,""); if ((n=split($0,a,/NODE/))<3) next; for (i=2;i<=n;i++) printf("NODE%s\n",a[i])}' input.txt
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
答案3
尝试使用下面的 python 方法
#!/usr/bin/python
import re
m=re.compile(r'[:;,]')
k=open('filename','r')
for i in k:
co=i.count("NODE")
if co > 1:
q=i.strip()
k=re.sub(m,"\n",q)
print k.strip().replace("'","").replace(">","")
输出
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
awk:awk 中已经提供了最好的解决方案,这只是我的尝试
awk '{print $0,gsub("NODE",$0)}' filename| awk '$NF >1 {print $1}'| sed "s/[;:,]/\n/g"|sed '/^$/d'| sed "s/[\>']//g"
答案4
使用sed
编辑器我们可以生成所需的输出,如下所示。
sed \
-e '/\n/{/^\n/!P;D;}' \
-e "/^>NODE_.*NODE/ y/>;:,'/\n\n\n\n\n/" \
-e '/\n/G;D' \
file
结果:
NODE_3028138_length_2215_cov_1.9513_ID_6056275
NODE_6264558_length_375_cov_4.0000_ID_12529115
NODE_3552704_length_509_cov_1.0000_ID_7105407
NODE_4456634_length_439_cov_1.9597_ID_8913267
NODE_4457268_length_491_cov_0.9657_ID_8914535
工作方法:
- 只有包含至少两个节点的行加上它们以开头的行
>NODE_
让我们称它们为“有趣的”行。我们将有趣的行中每个出现的 更改>;:,'
为换行符。 - 然后在感兴趣的行中附加一个换行符,以防它不以分号结尾。该
D
命令将启动隐式循环并将我们带到 sed 代码的第一行。 - 第一行是所有操作发生的地方,并且有趣的行被完全消耗,同时 sed 连续输出节点,每行一个。