如何用Perl/Awk合并同文件匹配列并保留所有行?
搞定Perl脚本丢空行的问题,实现按第5列合并第6列值
嘿,我来帮你解决这个脚本的问题!首先咱们先理清楚你的需求和现有脚本的问题:
你的需求和输入输出
你有个file1.txt文件,内容是:
1 101 111 BCX A@WWW 123 1 298 306 CCC A@QQQ 234 1 299 308 CCD A@QQQ 1 299 309 DDD A@ZZZ 345 1 299 309 DDD A@ZZZ 678
想要实现的是:只要第5列内容相同,所有对应行的第6列都要统一成合并后的值(比如A@QQQ对应的行,哪怕原来第6列是空的,也要改成234;A@ZZZ的行都改成345,678),同时要保留所有行,不能丢任何一行。但你现在用的脚本会把第6列为空的行弄丢,得改改。
原脚本为啥会丢行?
原脚本的逻辑是逐行对比当前行和上一行的第5列,只有遇到不同的才输出上一行的内容,而且没有提前记录每个第5列对应的完整合并值,碰到空第6列的行时,没办法正确匹配到对应的合并值,自然就丢了这些行。另外原脚本用制表符输出,和你要的空格分隔格式也不符。
修改后的解决方案
咱们换个思路,先把所有行的信息收集全,再统一处理输出,这样就不会丢任何行啦。下面是修改后的Perl命令:
perl -lane ' BEGIN { %col5_map = () } # 第一步:先收集每个第5列对应的所有非空第6列值 $col5_map{$F[4]} //= []; push @{$col5_map{$F[4]}}, $F[5] if defined $F[5] && $F[5] ne ""; # 把每一行的内容存起来,后面输出用 push @lines, [@F]; # 所有行读完后,处理合并值并输出 END { # 对每个第5列的去重并拼接成目标格式 foreach my $key (keys %col5_map) { my %unique_vals = map { $_ => 1 } @{$col5_map{$key}}; $col5_map{$key} = join(",", sort keys %unique_vals); } # 遍历所有存储的行,替换第6列后输出 foreach my $line (@lines) { my @fields = @$line; # 替换第6列:如果有对应合并值就用,没有就留空 $fields[5] = $col5_map{$fields[4]} // ""; # 用空格分隔输出,和输入格式一致 print join(" ", @fields); } } ' file1.txt
运行后的输出
执行这个命令后,就能得到你想要的结果:
1 101 111 BCX A@WWW 123 1 298 306 CCC A@QQQ 234 1 299 308 CCD A@QQQ 234 1 299 309 DDD A@ZZZ 345,678 1 299 309 DDD A@ZZZ 345,678
关键改动说明
- 用
%col5_map哈希表提前把每个第5列对应的所有有效第6列值收集起来,确保不会漏掉任何组的有效值; - 用
@lines数组把所有行的内容都存下来,保证输出时能保留原始的行顺序和所有行; - 在END块里对每个第5列的数值去重排序,然后统一替换每行的第6列,确保相同第5列的行第6列完全一致;
- 用
join(" ", @fields)保持和输入一致的空格分隔格式,避免原脚本的制表符问题; - 用
// ""处理极端情况,如果某个第5列没有任何有效第6列值,就留空,不会出现乱码。
内容的提问来源于stack exchange,提问作者bapors
相关产品推荐
相关产品推荐

