如何用Shell或Perl合并指定键的两个文件?原Shell脚本内存不足求方案
这种内存不足的问题通常是因为你之前的脚本把大文件(File A)整个加载到内存里了——毕竟53万行的文件如果全存数组,很容易吃满内存。好在File B很小(只有946行),我们可以换个思路:只把小文件加载到内存,然后逐行处理大文件,这样内存占用会极低,完全不会爆。
下面给你Shell(用awk)和Perl两种实现方案,都是这个思路:
Shell 实现(awk)
awk是处理这类文本合并的利器,而且默认就是逐行处理大文件,不会把整个文件读进内存。核心逻辑是先把File B的键(第一列)和对应行存到内存数组里,再遍历File A,用第三列匹配后输出合并结果:
awk ' # 先设置字段分隔符和输出分隔符,这里假设是制表符,根据你的实际情况修改 BEGIN { FS = OFS = "\t" } # 处理第一个输入文件(File B):把每行存入数组,键是第一列 NR == FNR { b_data[$1] = $0 next } # 处理第二个输入文件(File A):用第三列匹配,输出合并行 { target_key = $3 if (target_key in b_data) { # 如果你不需要输出File B的整行,只需要特定字段,可以修改输出逻辑,比如取B的第二列:print $0, $(split(b_data[target_key], arr, FS), arr[2]) print $0, b_data[target_key] } # 如果你想保留File A中没有匹配到的行,去掉上面的if判断,直接 print $0, b_data[target_key](不匹配时b_data[target_key]会是空) }' FileB FileA > merged_output.txt
为什么这个方案不会内存不足?
因为我们只把小的File B(946行)加载到内存数组里,File A是逐行读取、处理、输出,完全不会存整个文件,内存占用只和File B的大小有关,完全在系统承受范围内。
Perl 实现
Perl的思路和awk一致,先把File B读进哈希表,再逐行处理File A:
#!/usr/bin/perl use strict; use warnings; # 替换成你的实际文件名 my $file_b = "FileB"; my $file_a = "FileA"; my $output_file = "merged_output.txt"; # 第一步:读取File B,存入哈希(键是第一列,值是整行) my %b_hash; open my $fh_b, '<', $file_b or die "无法打开File B: $!"; while (my $line = <$fh_b>) { chomp $line; # 这里的分隔符改成你实际的字段分隔符,比如逗号就用 split /,/ my @fields = split /\t/, $line; $b_hash{$fields[0]} = $line; } close $fh_b; # 第二步:逐行处理File A,匹配后输出 open my $fh_a, '<', $file_a or die "无法打开File A: $!"; open my $fh_out, '>', $output_file or die "无法创建输出文件: $!"; while (my $line = <$fh_a>) { chomp $line; my @fields = split /\t/, $line; # Perl数组索引从0开始,第三列对应索引2 my $target_key = $fields[2]; if (exists $b_hash{$target_key}) { print $fh_out "$line\t$b_hash{$target_key}\n"; } # 要保留无匹配的行的话,改成: # print $fh_out "$line\t", $b_hash{$target_key} // "", "\n"; } close $fh_a; close $fh_out;
额外注意点
- 如果File B中有重复的键,上面的两种方案都会保留最后一次出现的行;如果需要保留所有匹配的行,可以把数组/哈希的值改成数组类型,存储多个行。
- 一定要根据你的实际文件分隔符修改
FS(awk)或split的参数(Perl),比如CSV文件就用逗号分隔。
内容的提问来源于stack exchange,提问作者ebk
相关产品推荐
相关产品推荐

