我有两个文件。第一个包含n列的文件file_A.txt如下所示:
SRK_00017 MRS10904.1
SRK_00017 MRS14430.1
SRK_00005 MRS13611.1
...
第二个文件file_B.txt看起来像:
MRS10904.1 DMN02113.10
MRS10983.1 DMN07690.11
SRK_00011 DMN02311.14
MRS13611.1 DMN12833.2
MRS10981.1 DMN00149.23
SRK_00011 DMN02872.13
MRS14430.1 DMN12777.2
SRK_00005 DMN00659.13
SRK_04765 DMN12781.2
SRK_04765 DMN03028.10
MRS13611.1 DMN1234.10
SRK_00017 DMN03028.10
SRK_00017 DMN03029.10
SRK_14211 DMN13843.1
SRK_00017 DMN00069.20
MRS10904.1 DMN00659.13
....
我想创建一个新的第三个文件file_C.txt,如:
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1
....
第二个文件包含ID SRK_和MRS的值(如DMN..)。所有SRK_都在file_A的couln1中,所有MRS都在file_A的Couln2中。SRK_和MRS中的任何一个可以具有多个DMN值(在File_B中给出)。在我的File_C中,我真正想要的是SRK_和MRS的值的所有可能配对都属于File_A的一行。例如,File_A中的第一行是SRK_00017 MRS10904.1,如果我们看File_B,SRK_00011有三个DMN值(DMN03028.10、DMN03029.10、DMNO0069.20),MRS10904.也有两个DMN数值(DMN02113.10和DMN00659.13)。因此,对于File_A的第一行,将有3*2=6对可能的DMN。结果File_C.txt的前六行中有。我希望我能传达我的问题。
使用awk
awk 'NR==FNR{a[$1]++;b[$1 FS a[$1]]=$2;next}
{for (i=1;i<=a[$2];i++)
for (j=1;j<=a[$1];j++)
print $1,b[$1 FS j],b[$2 FS i],$2
}' File_B.txt File_A.txt
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1
在Gnu Awk 4.1版中,您可以使用类似的数组
gawk 'NR==FNR {
a[$1][$2]++
next
}
{
for (i in a[$1])
for (j in a[$2])
print $1, i, j, $2
}' File_B.txt File_A.txt
输出:
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1
这是一个Perl版本。
注意:为了清楚起见,我省略了任何错误检查(不确定这里的政策是什么)。
open my $file_a, '<', 'File_A.txt';
open my $file_b, '<', 'File_B.txt';
open my $file_c, '>', 'File_C.txt';
my %map;
foreach (<$file_b>) {
chomp;
my ($id,$dmn) = split;
push(@{ $map{$id} },$dmn);
}
foreach (<$file_a>) {
chomp;
my ($srk,$mrs) = split;
for my $dmn2 ( @{ $map{$mrs} } ) {
for my $dmn1 ( @{ $map{$srk} } ) {
print $file_c "$srk $dmn1 $dmn2 $mrsn";
}
}
}
close $file_a;
close $file_b;
close $file_c;
File_C.txt包含:
SRK_00017 DMN03028.10 DMN02113.10 MRS10904.1
SRK_00017 DMN03029.10 DMN02113.10 MRS10904.1
SRK_00017 DMN00069.20 DMN02113.10 MRS10904.1
SRK_00017 DMN03028.10 DMN00659.13 MRS10904.1
SRK_00017 DMN03029.10 DMN00659.13 MRS10904.1
SRK_00017 DMN00069.20 DMN00659.13 MRS10904.1
SRK_00017 DMN03028.10 DMN12777.2 MRS14430.1
SRK_00017 DMN03029.10 DMN12777.2 MRS14430.1
SRK_00017 DMN00069.20 DMN12777.2 MRS14430.1
SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1
SRK_00005 DMN00659.13 DMN1234.10 MRS13611.1
这是一个shell脚本版本,使用sort
和join
。这适用于我在ubuntu 12.04和osx:
#!/bin/bash
sort $2 > $2.MRS.SRK
sort $1 > $1.SRK
join $1.SRK $2.MRS.SRK | sed 's/^([^ ]*) +/1:/' | sort > SRK.join
sort -k2 $1 > $1.MRS
join -o "1.1 1.2 2.2" -1 2 $1.MRS $2.MRS.SRK | sed 's/^([^ ]*) +/1:/' | sort > MRS.join
join SRK.join MRS.join | sed 's/([^ ]*):([^ ]*) ([^ ]* [^ ]*)/1 3 2/'
输出为:
ubuntu@ubuntu:~$/DMN.sh文件_A.txt文件_B.txtSRK_00005 DMN00659.13 DMN1234.10 MRS13611.1SRK_00005 DMN00659.13 DMN12833.2 MRS13611.1SRK_0017 DMN00069.20 DMN00659.13 MRS10904.1SRK_0017 DMN00069.20 DMN02113.10 MRS10904.1SRK_0017 DMN03028.10 DMN00659.13 MRS10904.1SRK_0017 DMN03028.10 DMN02113.10 MRS10904.1SRK_0017 DMN03029.10 DMN00659.13 MRS10904.1SRK_0017 DMN03029.10 DMN02113.10 MRS10904.1SRK_0017 DMN00069.20 dmn2777.2 MRS14430.1SRK_0017 DMN03028.10 dmn2777.2 MRS14430.1SRK_0017 DMN03029.10 dmn2777.2 MRS14430.1ubuntu@ubuntu:~$
忽略排序,基本流程为:
- 两个文件的字段1上的
join
("SRK"字段)。确保字段输出顺序为"SRK MRS DMN"> sed
是结果输出中"SRK"one_answers"MRS"字段之间的冒号,因此这两个字段对于未来的join
s显示为一个字段- 文件A的字段2和文件B的字段1("MRS"字段)上的
join
。确保字段输出顺序为"SRK MRS DMN"> sed
是结果输出中"SRK"one_answers"MRS"字段之间的冒号,因此这两个字段对于未来的join
s显示为一个字段- CCD_ 9结果为2。和4。上面的"SRK:MRS"字段
sed
删除冒号并将生成的输出字段按顺序移动为"SRK DMN DMN MRS">
注意,join
的输入必须在联接字段上排序,因此在这里的每个join
之前实际上都需要排序。因此,不能保证最终输出的排序顺序。但我认为,对于任何类型的DB加入来说,通常都是这样。
这也可以全部重写为一个(长)命令,不需要中间文件,并使用bash基元而不是sed
:
join <(
join <(
sort File_A.txt) <(
sort File_B.txt) |
while read a b; do echo "$a:$b"; done | sort) <(
join -o "1.1 1.2 2.2" -1 2 <(
sort -k2 File_A.txt) <(
sort File_B.txt) |
while read a b; do echo "$a:$b"; done | sort) |
while IFS=" :" read a b c d; do echo "$a $c $d $b"; done > File_C.txt
注意,join
只能从stdin读取其一个输入文件,因此<()
进程替换被广泛用于将输入管道传输到join
。
还要注意,两个输入文件都必须有效地读取两次。