如何用awk/sed/Perl实现两文件键值匹配替换输出指定结果
问题原因
你之前写的awk命令未生效,核心原因有两点:一是没有显式指定列分隔符,若文件使用tab分隔、或存在多空格/tab混合分隔的情况,awk默认分割规则会导致键值对读取异常,映射表构建失败,自然无法完成替换;二是硬编码了固定列号($2到$8),一旦文件实际列数和你写的编号不匹配,就会输出异常。
推荐方案:awk实现(大文件性能最优)
以下代码不需要硬编码列数,自动适配分隔符,仅需读取一次两个文件即可同时生成Output-1、Output-2两个结果,IO开销最低,适合处理大体积文件:
awk ' BEGIN { # 若文件是空格分隔,将下面两行改为 FS="[ \t]+"; OFS=" " 即可 FS = OFS = "\t" } # 读取File-1构建映射表 NR == FNR { map[$1] = $2 next } # 表头行直接写入两个输出文件 FNR == 1 { print $0 > "Output-1" print $0 > "Output-2" next } # 处理数据行 { # 拼接第一列之外的其他列,若需要删除最后一列,将循环终止条件改为 i <= NF-1 other_cols = "" for (i = 2; i <= NF; i++) { other_cols = other_cols OFS $i } # 写入Output-1:所有行保留,匹配到键则替换第一列 if ($1 in map) { print map[$1] other_cols > "Output-1" # 匹配到的行同时写入Output-2 print map[$1] other_cols > "Output-2" } else { print $1 other_cols > "Output-1" } } ' File-1 File-2
备选方案:Perl实现
如果习惯使用Perl,可使用以下脚本,逻辑和awk版本完全一致:
#!/usr/bin/perl use strict; use warnings; my ($file1, $file2) = @ARGV; my %map; # 读取File-1构建映射 open my $f1, "<", $file1 or die "无法打开File-1: $!"; while (<$f1>) { chomp; my ($k, $v) = split /\s+/, $_, 2; $map{$k} = $v; } close $f1; # 处理File-2并输出结果 open my $f2, "<", $file2 or die "无法打开File-2: $!"; open my $o1, ">", "Output-1" or die "无法写入Output-1: $!"; open my $o2, ">", "Output-2" or die "无法写入Output-2: $!"; my $is_header = 1; while (<$f2>) { chomp; my @cols = split /\s+/, $_, -1; if ($is_header) { print $o1 "$_\n"; print $o2 "$_\n"; $is_header = 0; next; } my $first_col = shift @cols; # 若需要删除最后一列,打开下面这行注释 # pop @cols; my $other_cols = join "\t", @cols; # 空格分隔则把"\t"改为" " if (exists $map{$first_col}) { print $o1 "$map{$first_col}\t$other_cols\n"; print $o2 "$map{$first_col}\t$other_cols\n"; } else { print $o1 "$first_col\t$other_cols\n"; } } close $f2; close $o1; close $o2;
运行命令:perl map_script.pl File-1 File-2
注意事项
- 如果你确实需要删除File-2的最后一列,按照代码里的注释修改对应位置即可,当前默认代码完全匹配你给出的示例输出规则
- 若运行后仍未正常替换,优先检查文件是否携带Windows格式的换行符(\r),可先执行
dos2unix File-1 File-2转成Unix格式后再运行 - 超过百万行的超大文件优先选择awk方案,内存占用更低,处理速度更快
内容的提问来源于stack exchange,提问作者mike
相关产品推荐
相关产品推荐

