我有一个非常相似的问题这个问题,但不知道如何使答案适应我自己的问题。
我有一个制表符分隔文件,第二列包含逗号分隔列表,例如:
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0000166,GO:0003674,GO:0005488,GO:0005515,GO:0005524,GO:0005575
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0005829,GO:0006457,GO:0006458,GO:0006950,GO:0008134
TRINITY_DN10_c0_g1 DN10_c0_g1 GO:0050896,GO:0051082,GO:0051084,GO:0051085
我想做到这一点:
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0000166
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0003674
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0005488
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0005515
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0005524
TRINITY_DN1_c0_g1 DN1_c0_g1 GO:0005575
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0005829
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0006457
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0006458
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0006950
TRINITY_DN1_c0_g3 DN1_c0_g3 GO:0008134
TRINITY_DN10_c0_g1 DN10_c0_g1 GO:0050896
TRINITY_DN10_c0_g1 DN10_c0_g1 GO:0051082
TRINITY_DN10_c0_g1 DN10_c0_g1 GO:0051084
TRINITY_DN10_c0_g1 DN10_c0_g1 GO:0051085
第三列中的术语数量可变。我需要为其关联的第一列和第二列各占一行。
如果有任何帮助,上述问题的起始内容是:
perl -lne 'if(/^(.*?: )(.*?)(\W*)$/){print"$1$_$3"for split/, /,$2}'
但我不知道需要更改哪些部分才能解决我的问题!
非常感谢您的帮助。
答案1
这个 awk 命令非常可读:
awk '
BEGIN {FS = "[,\t]"; OFS = "\t"}
{for (i=3; i<=NF; i++) print $1, $2, $i}
' file
在 Perl 中,这是
perl -F'[,\t]' -lane 'print join "\t", @F[0,1], $F[$_] for 2..$#F' file
# or
perl -F'[,\t]' -slane 'print @F[0,1], $F[$_] for 2..$#F' -- -,=$'\t' file
如果您不确定是否有实际的制表符:
- awk:
FS = ",|[[:blank:]]+"
- 珀尔:
-F',|\s+'
为了好玩,bash
while IFS= read -r line; do
prefix=${line%%GO:*}
IFS=, read -ra gos <<< "${line#$prefix}"
for go in "${gos[@]}"; do echo "$prefix$go"; done
done < file
这个版本不关心空格和制表符,但它会很多比 perl 或 awk 慢。
答案2
使用-a
开关将每行拆分为空格上的 @F 数组。
perl -lane 'print join "\t", @F[0, 1], $_ for split /,/, $F[2]'
答案3
这里的另一个选择nest --explode
是磨坊主
mlr --nidx --fs tab nest --explode --values --across-records --nested-fs ',' -f 3 file
或使用简写nest
说明符
mlr --nidx --fs tab nest --evar ',' -f 3 file
答案4
使用GNU sed它具有 [\n\t] 正则表达式功能,我们可以这样做,如下所示:
sed -n '
y/,/\n/
:a
P; s/\t[^\n\t]*\n/\t/
ta
' file
你可以使用珀尔还
perl -F'(\t)' -pale '$"="";
$_ = pop(@F) =~ tr/,/\n/r =~ s/^/@F/mgr;
' file
一种方法是珀尔显示在这里。 $a 标量保存前两个字段,然后在 while 循环中从由于 /c 修饰符而停止的位置开始搜索。
perl -lne '
my($a) = /^((?:.*?\t){2})/gc;
print $a, $1 while /\G([^,]+)(?:,|$)/g;
' file