这是我在这个网站上提出的第一个问题,所以如果我解释得不好,请原谅。我也是新手。我研究了一些 perl 和 unix 命令行的东西,但就是不知道如何解决这个问题。
我有 2 个文件 - 文件 A 是主文件,包含 10 多列和大约 15,000 行,文件 B 包含 4 列和大约 1500 行。
我想一次获取文件 B 中的每一行,并将这些列与文件 A 中的相应列进行匹配(这两个文件之间的顺序不同,但列标题相同)。如果文件 A 中文件 B 的所有 4 列都匹配,则从文件 A 中删除整行,并放入新文件(文件 C)中。
例子:
文件A
individual_id study_id.x chromosome g_start gene referencel1hs SampleFile_num id sample_name
54 Baillie2011 4 57497067 na no 612 612 DonorAR2
54 Baillie2011 X 154790187 TMLHE no 612 612 DonorAR2
54 Baillie2011 5 159351203 ADRA1B no 612 612 DonorAR2
54 Baillie2011 13 79259801 na no 612 612 DonorAR2
54 Baillie2011 8 4452925 CSMD1 no 610 610 DonorAH
文件B
study_id.x sample_name chromosome g_start
Baillie2011 DonorAH 8 4452925
Baillie2011 DonorBC 9 5491376
Baillie2011 DonorAH 8 5829283
Baillie2011 DonorCH 8 5829283
结果:
文件A
individual_id study_id.x chromosome g_start gene referencel1hs SampleFile_num id sample_name
54 Baillie2011 4 57497067 na no 612 612 DonorAR2
54 Baillie2011 X 154790187 TMLHE no 612 612 DonorAR2
54 Baillie2011 5 159351203 ADRA1B no 612 612 DonorAR2
54 Baillie2011 13 79259801 na no 612 612 DonorAR2
文件C
individual_id study_id.x chromosome g_start gene referencel1hs SampleFile_num id sample_name
54 Baillie2011 8 4452925 CSMD1 no 610 610 DonorAH
答案1
perl -MFatal='open,close' -ali -ne '
if ( @ARGV ) { # FileB readin
if ( $. == 1 ) { push @names, @F; }
else { push @A, join $/, @F; }
print;
} else { # FileA readin
if ( $. == 1 ) {
open FILEC, ">", "FileC.out";
print FILEC $_;
print;
@remap =
map {
my $n = $names[$_];
grep { $n eq $F[$_] } 0 .. $#F;
} 0..$#names;
} else {
my $n = join $/, @F[@remap];
if ( grep { $n eq $_ } @A ) { print FILEC $_; }
else { print; }
}
}
eof and $. = 0;
eof() and close FILEC;
' FileB FileA
解释
- 我们按顺序向 Perl 命令行提供 2 个文件“FileB”和“FileA”。
- 在读取 FileB 的过程中,我们根据是在第一行还是其他行执行两件事:
- 对于 FileB 的第一行,我们将 FileB 字段的名称存储到数组中
@names
。 - 对于其他行,我们使用由默认情况下提供的
@A
换行符连接的字段填充数组。\n
$/
RS
- 在这两种情况下,我们都会将这些行打印到 STDOUT,以便在
Perl
s-i
模式下无损地读取 FileB。
- 对于 FileB 的第一行,我们将 FileB 字段的名称存储到数组中
- 现在,当我们读取 FileA 时,在它的第一行打开一个写入文件句柄,
FILEC
以便我们能够填充该FileC.out
文件。- 我们打印到 STDOUT,因为我们希望保留 FileB 中的这一行。
- 我们还打印到文件句柄 FILEC,因为我们希望标头也进入 FileC.out。
- 现在这是关键的步骤,其中将FileB的字段映射到FileA的字段。
- 对于 FileA 中的非第一行,我们对这些重新映射的字段与数组中已存储的 FileB 数据执行相等性检查
@A
。 - 如果找到匹配项,则将此行写入 FileC.out 但不要写入 FileA。如果未找到匹配项,则写入 FileA,但不写入 FileC.out。
- 在任一文件结束时,我们将行计数器重置
$.
为 0,以便可以对两个文件执行第一行相等性检查。 - 在最终的 eof 后(通过 eof() 检测到),我们关闭文件句柄 FILEC。
- 该模块
Fatal.pm
加载open
并close
具有在这些操作上自动退出的功能。