如何比较两个在unix中有多个字段的文本文件

时间:2010-07-06 12:40:40

标签: linux perl unix awk

我有两个文本文件

  • 档案1

    number,name,account id,vv,sfee,dac acc,TDID
    7000,john,2,0,0,1,6
    7001,elen,2,0,0,1,7
    7002,sami,2,0,0,1,6
    7003,mike,1,0,0,2,1
    8001,nike,1,2,4,1,8
    8002,paul,2,0,0,2,7 
    
  • 文件2

    number,account id,dac acc,TDID
    7000,2,1,6
    7001,2,1,7
    7002,2,1,6
    7003,1,2,1
    

我想比较这两个文本文件。如果文件2的四列在文件1中,则相等意味着我想要像这样输出

7000,john,2,0,0,1,6
7001,elen,2,0,0,1,7
7002,sami,2,0,0,1,6
7003,mike,1,0,0,2,1

nawk -F"," 'NR==FNR {a[$1];next} ($1 in a)' file2.txt file1.txt ..这适用于比较两个文件中的两个单列。我想比较多列。有谁有建议?


编辑:来自OP的评论:

nawk -F"," 'NR==FNR {a[$1];next} ($1 in a)' file2.txt file1.txt

..这适用于比较两个文件中的两个单列。我想比较多列。你有什么建议吗?

7 个答案:

答案 0 :(得分:3)

此awk单行程序适用于 未排序 文件的多列:

awk -F, 'NR==FNR{a[$1,$2,$3,$4]++;next} (a[$1,$3,$6,$7])' file1.txt file2.txt

为了使其正常工作,用于输入的第一个文件(在我的示例中为file1.txt)必须是仅包含4个字段的文件:

FILE1.TXT

7000,2,1,6
7001,2,1,7
7002,2,1,6
7003,1,2,1

FILE2.TXT

7000,john,2,0,0,1,6
7000,john,2,0,0,1,7
7000,john,2,0,0,1,8
7000,john,2,0,0,1,9
7001,elen,2,0,0,1,7
7002,sami,2,0,0,1,6
7003,mike,1,0,0,2,1
7003,mike,1,0,0,2,2
7003,mike,1,0,0,2,3
7003,mike,1,0,0,2,4
8001,nike,1,2,4,1,8
8002,paul,2,0,0,2,7

输出

$ awk -F, 'NR==FNR{a[$1,$2,$3,$4]++;next} (a[$1,$3,$6,$7])' file1.txt file2.txt
7000,john,2,0,0,1,6
7001,elen,2,0,0,1,7
7002,sami,2,0,0,1,6
7003,mike,1,0,0,2,1

或者,您也可以使用以下语法,该语法与您问题中的语法更接近,但不太可读恕我直言

awk -F, 'NR==FNR{a[$1,$2,$3,$4];next} ($1SUBSEP$3SUBSEP$6SUBSEP$7 in a)' file1.txt file2.txt

答案 1 :(得分:1)

TxtSushi看起来像你想要的。它允许使用SQL处理CSV文件。

答案 2 :(得分:1)

这不是一个优雅的单行,但你可以用perl来做。

#!/usr/bin/perl
open A, $ARGV[0];
while(split/,/,<A>) {
    $k{$_[0]} = [@_];
}
close A;

open B, $ARGV[1];
while(split/,/,<B>) {
    print join(',',@{$k{$_[0]}}) if
        defined($k{$_[0]}) &&
        $k{$_[0]}->[2] == $_[1] &&
        $k{$_[0]}->[5] == $_[2] &&
        $k{$_[0]}->[6] == $_[3];
}
close B;

答案 3 :(得分:0)

快速回答:使用cut拆分您需要的字段,并diff来比较结果。

答案 4 :(得分:0)

这既不高效又不漂亮,但它会完成工作。它不是最有效的实现,因为它多次解析file1,但它不会将整个文件读入RAM,因此与简单的脚本方法相比具有一些优势。

sed -n '2,$p' file1 | awk -F, '{print $1 "," $3 "," $6 "," $7 " " $0 }' | \
sort | join file2 - |awk '{print $2}'

这可以如下工作

  1. sed -n '2,$p' file1将file1发送到没有标题行的STDOUT
  2. 第一个awk命令以与file2相同的格式打印file1中的4个“关键字段”,后跟一个空格,后跟file1的内容
  3. sort命令确保file1的顺序与file2
  4. 的顺序相同
  5. join命令加入file2和STDOUT,只写入在file2
  6. 中具有匹配记录的记录
  7. 最终的awk命令只打印file1的原始部分
  8. 为了使其正常工作,您必须确保在运行命令之前对file2进行排序。

    针对您的示例数据运行此操作会产生以下结果

    7000,john,2,0,0,1,6
    7001,elen,2,0,0,1,7
    7002,sami,2,0,0,1,6
    7003,mike,1,0,0,2,1
    

    修改

    我从您的评论中注意到您收到了排序错误。如果在运行管道命令之前对file2进行排序时发生此错误,那么您可以拆分文件,对每个部分进行排序,然后再将它们重新组合在一起。

    像这样的东西会为你做那件事

    mv file2 file2.orig
    for i in 0 1 2 3 4 5 6 7 8 9
    do
      grep "^${i}" file2.orig |sort > file2.$i
    done
    cat file2.[0-9] >file2
    rm file2.[0-9] file2.orig
    

    如果您的文件未在所有前导数字范围内均匀分布,则可能需要修改传递给的变量。

答案 5 :(得分:0)

测试不是很好,但这可能会有效:

join -t, file1 file2 | awk -F, 'BEGIN{OFS=","} {if ($3==$8 && $6==$9 && $7==$10) print $1,$2,$3,$4,$6,$7}'

(当然,这假定输入文件已排序)。

答案 6 :(得分:0)

统计包R可以轻松处理多个csv表。 请参阅An Intro. to RR for Beginners