【问题标题】:comparing files by fields in bash在bash中按字段比较文件
【发布时间】:2019-08-20 20:18:22
【问题描述】:

我有两个任意文件:

==> file1 <==
11110 abcdef
11111 apple
11112 banana
11113 carrot
11114 date
11115 eggplant

==> file2 <==
11110 abcdefg
11111 apple-pie
11112 banana-cake
11113 chocolate
11115 egg
11116 fruit

为了比较这些文件,我只关心第一列的数字,break后面的单词不重要。

我希望能够轻松识别每个文件中缺少的数字。

例如,文件 1 没有 11116,文件 2 没有 11114

如果我将文件排序在一起,我可以得到一个完整的列表:

$ sort file*
11110 abcdef
11110 abcdefg
11111 apple
11111 apple-pie
11112 banana
11112 banana-cake
11113 carrot
11113 chocolate
11114 date
11115 egg
11115 eggplant
11116 fruit

我可以通过 uniq 运行它并仅比较数字的长度来获得所有数字的列表:

$ sort file* | uniq -w5
11110 abcdef
11111 apple
11112 banana
11113 carrot
11114 date
11115 egg
11116 fruit

这是所有数字 11110-11116 的列表。

我可以通过要求 uniq 为我过滤它们来获得唯一和重复的列表:

重复(出现在两个文件中的数字):

$ sort file* | uniq -dw5
11110 abcdef
11111 apple
11112 banana
11113 carrot
11115 egg

唯一的数字,或只出现在一个文件中的数字:

$ sort file* | uniq -uw5
11114 date
11116 fruit

我想要一些输出类似于:

# shows numbers that do not exist in this file
$ sort file* | <is missing>
==> file1 <==
11116 fruit

==> file2 <==
11114 date

它可以做相反的事情并显示 OTHER 文件中缺少哪些数字,每种情况都是可行的:

# shows numbers that do exist ONLY in this file
$ sort file* | <has unqie>
==> file1 <==
11114 date

==> file2 <==
11116 fruit

第一个字段将包含约 30 个字母数字字符。

有问题的文件包含数千个条目,并且大多数条目预计都在这两个文件中。

数字右边的任意数据是相关的,需要保留。

我的想法是:

  • 生成完整的数字列表
  • 将该列表与 file1 进行比较以搜索唯一条目
  • 将该列表与 file2 进行比较以搜索唯一条目

但我不知道如何在一行中做到这一点:

sort file* | uniq -w5 | sort file1 | uniq -uw5
sort file* | uniq -w5 | sort file2 | uniq -uw5

但是,第一个 uniq 的输出并没有与 file1/2 的使用合并...

我想出的解决方案是创建所有数字的输出:

$ sort file* | uniq -w5

然后对每个文件单独运行它,这确实有效。我只是无法将它拼凑成一行:

$ sort all file1 | uniq -uw5
11116 fruit
$ sort all file2 | uniq -uw5
11114 date

我现在正在合并加入,谢谢卡米尔


编辑:我自己从来没有走得更远,@Shawn 用很短的一行就给了我:

join -j1 -v1 file1 file2 

在我有两个我需要的格式的编译列表后,对文件执行的join 会吐出所需的答案。从我上面的代码示例中:

$join -j1 -v1 file1 file2
11114 date

$ join -j1 -v2 file1 file2
11116 fruit

真实世界示例:

我想我会生成一个真实世界的例子来说明我一直在做的事情。取 5 个任意文件:

 lorem1.txt
 lorem2.txt
 lorem3.txt
 lorem4.txt
 lorem5.txt

并对它们进行备份。我在lorem2.txt 中修改了一点,并从备份中删除了`lorem4.txt(认为它是一个新文件,或者无论出于何种原因,它只是一个丢失的文件):

test$ tree
.
├── data
│   ├── lorem1.txt
│   ├── lorem2.txt
│   ├── lorem3.txt
│   ├── lorem4.txt
│   └── lorem5.txt
└── data-backup
    ├── lorem1.txt
    ├── lorem2.txt
    ├── lorem3.txt
    └── lorem5.txt

2 directories, 9 files
mad@test$ md5deep data/* | sort > hash1
mad@test$ md5deep data-backup/* | sort > hash2
mad@test$ head hash*
==> hash1 <==
44da5caec444b6f00721f499e97c857a  /test/data/lorem1.txt
5ba24c9a5f6d74f81499872877a5061d  /test/data/lorem2.txt
a00edd450c533091e0f62a06902545a4  /test/data/lorem5.txt
b80118923d16f649dd5410d54e5acb2d  /test/data/lorem4.txt
fb8f7f39344394c78ab02d2ac524df9d  /test/data/lorem3.txt

==> hash2 <==
000e755b8e840e42d50ef1ba5c7ae45d  /test/data-backup/lorem2.txt
44da5caec444b6f00721f499e97c857a  /test/data-backup/lorem1.txt
a00edd450c533091e0f62a06902545a4  /test/data-backup/lorem5.txt
fb8f7f39344394c78ab02d2ac524df9d  /test/data-backup/lorem3.txt

运行我们的joins:

加入 1

mad@test$ join -j1 -v1 hash*
5ba24c9a5f6d74f81499872877a5061d /test/data/lorem2.txt
b80118923d16f649dd5410d54e5acb2d /test/data/lorem4.txt

从我们的两组哈希文件中,joining 他们针对第一个文件进行了验证,我们看到 lorem2.txt 和 lorem4.txtare missing from the second file. (lorem2because we changed a bit, andlorem4 的匹配哈希,因为我们没有复制,或者我们从备份中删除了文件)。

做reverse join可以看到lorem2存在,只是hash不正确:

加入 2

mad@test$ join -j1 -v2 hash*
000e755b8e840e42d50ef1ba5c7ae45d /test/data-backup/lorem2.txt

使用我之前的sortuniq 示例,我可以获得类似的结果,但上面的join 要好得多。 join1 向我们展示了我们需要重新访问的文件,join2 专门向我们展示了哪些哈希值不正确。

按名称排序并显示 uniq 名称(这超出了原始问题的范围)可以向我们显示备份中丢失的文件。在此示例中,我转换了备份文件名,以便它们模仿原始文件名,将它们与原始文件名合并/排序,并仅根据名称而不是哈希值进行排序。这将显示备份中缺少的文件:

test$ sort -k2 hash1 <(sed 's/data-backup/data/g' hash2) | uniq -uf1
b80118923d16f649dd5410d54e5acb2d  /test/data/lorem4.txt

如果我们有一个包含所有哈希的文件:

test$ sort -k2 hash1 allhashes | uniq -uf1
b80118923d16f649dd5410d54e5acb2d  /test/data/lorem4.txt

再次感谢所有帮助我制定此计划的人。它已经变成了一个真正的生命和时间节省者。

【问题讨论】:

  • 我们有一立方公里的近乎重复。你能告诉我们你已经搜索过什么以及你找到了什么吗?例如,一个简单的 Awk 脚本应该不难找到。
  • 我试图证明我一直在努力。我确实想出了一个的答案,但我不能把它放在一行中。
  • man join。加入第一个字段并从两个文件中打印不可比较的行。
  • @KamilCuk 很好,谢谢,我不知道加入!
  • 特别是,假设文件已排序,join -j1 -v1 file1 file2 将打印仅在 file1 中的记录,join -j1 -v2 file1 file2 将仅在 file2 中打印记录。

标签: linux bash sorting uniq


【解决方案1】:

使用gnu awk,您可以使用这种方法:

awk 'ARGIND < ARGC-1 {
   a[ARGIND][$1] = 1
   next
} {
for (i=1; i<ARGC-1; i++)
   if (!a[i][$1])
      print ARGV[i] ":", $0
}' file1 file2 <(sort file1 file2)

file2: 11114 date
file1: 11116 fruit

【讨论】:

    【解决方案2】:

    仅在文件 1 中:

    grep `comm -23 <(cut -d \  -f 1 f1 | sort) <(cut -d \  -f 1 f2 | sort)` f1
    

    【讨论】:

      【解决方案3】:

      这个 awk 版本只需要遍历每个文件: 它假定文件中没有重复的 ID。

      awk '
          NR == FNR   {f1[$1] = $0; next}
          !($1 in f1) {printf "only in %s: %s\n", FILENAME, $0}
            $1 in f1  {delete f1[$1]}
          END         {for (id in f1) printf "only in %s: %s\n", ARGV[1], f1[id]}
      ' file1 file2
      

      输出

      only in file2: 11116 fruit
      only in file1: 11114 date
      

      【讨论】:

        【解决方案4】:

        您可以在 2 个文件之间使用差异。但是,如果您对这些文件进行 diff,则会列出所有行。

        $ diff file1 file2
        1,6c1,6
        < 11110 abcdef
        < 11111 apple
        < 11112 banana
        < 11113 carrot
        < 11114 date
        < 11115 eggplant
        ---
        > 11110 abcdefg
        > 11111 apple-pie
        > 11112 banana-cake
        > 11113 chocolate
        > 11115 egg
        > 11116 fruit
        

        但你只关心领先的数字。

        $ diff <(cut -d' ' -f1 file1) <(cut -d' ' -f1 file2)
        5d4
        < 11114
        6a6
        > 11116
        

        如果文件未排序,则添加排序

        $ diff <(cut -d' ' -f1 file1 | sort) <(cut -d' ' -f1 file2 | sort)
        5d4
        < 11114
        6a6
        > 11116
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-06-15
          • 2014-11-11
          • 1970-01-01
          • 1970-01-01
          • 2013-01-26
          • 1970-01-01
          • 2014-12-25
          • 2016-10-23
          相关资源
          最近更新 更多