【问题标题】:Compare two files based on key, print difference in value in another file [duplicate]根据键比较两个文件,在另一个文件中打印值的差异[重复]
【发布时间】:2020-04-16 16:58:51
【问题描述】:

我有 2 个大约 1.2 GB 数据的大文件,带有键和值,我需要根据键比较两个文件并将值的差异存储在第三个文件中,

文件1:

test1 marco;polo;angus
test2 mike;zen;liza
test3 tom;harry;alan
test4 bob;june;janet

文件 2:

test1 polo;angus
test2 mike
test4 bob;janet

我想比较 file1 的前两列和 file2(在前两列中搜索 file2 的全部内容),如果它们匹配打印值的差异。然后搜索文件 1 的第二行,以此类推。还应打印文件 1 中唯一的键。

预期输出:

test1 marco
test2 zen;liza
test3 tom;harry;alan
test4 june

我的文件很大,大约有 100,000 行,所以我想加快执行速度。

【问题讨论】:

  • 内存使用是否值得关注?
  • 另外,我删除了 bash、ksh 和 sh 标签。重新添加您实际使用的一个
  • 感谢回复伙伴,我是shell脚本的新手,运行命令时出现以下错误,-bash: perl: command not found -bash: perl: command not found -bash: perl:找不到命令 -bash: datamash: 找不到命令 -bash: perl: 找不到命令 -bash: perl: 找不到命令 -bash: perl: 找不到命令 comm: '/dev/fd/62': 没有这样的文件或目录 这是在 shell 脚本中运行,使用 #!/usr/bin/env bash 真正的条目如下所示: 1332239_44557576_CONTI Lased & Micro kjd $353.50_30062020_lsdf3_no-rule 343323H;343434311H;454656556H;343343432H
  • 关于错误消息bash: perl: command not found 并不神秘。您正在尝试从 bash 调用命令 perl,但在您的 PATH 中找不到该命令。 perl 不是一个标准的 UNIX 工具,所以你可能必须安装它,但是你在你的平台上安装它。

标签: bash shell sh ksh


【解决方案1】:

一种方式:

$ comm -23 <(perl -lane 'print "$F[0]\t$_" for split /;/, $F[1]' file1 | sort) \
           <(perl -lane 'print "$F[0]\t$_" for split /;/, $F[1]' file2 | sort) | \
  datamash -g1 collapse 2 | tr ',' ';'
test1   marco
test2   liza;zen
test3   alan;harry;tom
test4   june

这有很多步骤,但其中大部分可以并行发生:

  1. 使用perl 将每一行转换为多行,第二列中的每一列都有一个值而不是多个,然后对所有内容进行排序。 (注意这部分需要像 bashksh93 这样理解 &lt;(command) 重定向的 shell。)

  2. comm -23 需要排序,这将打印仅存在于其第一个输入文件中的行(因此,两者中的 test1 polo 之类的内容将被禁止)。

  3. 使用datamash 将拆分的行折叠成一个基于共同的第一列值的行,第二列值用逗号分隔。

  4. 使用tr 将这些逗号转换为分号以匹配原始文件。

如果您的真实文件已经像您的示例一样根据第一列进行排序,则可以对其进行改进以提高效率:

comm -23 <(perl -lane 'print "$F[0]\t$_" for sort split(/;/, $F[1])' file1) \
         <(perl -lane 'print "$F[0]\t$_" for sort split(/;/, $F[1])' file2) | \
 datamash -g1 collapse 2 | tr ',' ';'

或者,纯perl 版本(将整个第二个文件加载到内存中):

#!/usr/bin/env perl
# Run as perl whatever.pl file1 file2
use warnings;
use strict;
use autodie;
use feature qw/say/;

my %values;
open my $file2, "<", $ARGV[1];
while (<$file2>) {
    chomp;
    my ($key, $val) = split;
    push @{$values{$key}}, split(/;/, $val);
}

open my $file1, "<", $ARGV[0];
while (<$file1>) {
    chomp;
    my ($key, $val) = split;
    if (exists $values{$key}) {
        my %set = map { $_ => 1 } split(/;/, $val);
        delete @set{@{$values{$key}}};
        say "$key\t", join(";", keys %set);         
    } else {
        say "$key\t$val"
    }
}

【讨论】:

    猜你喜欢
    • 2020-08-01
    • 2013-06-17
    • 2016-12-24
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 2018-10-07
    相关资源
    最近更新 更多