【问题标题】:extracting unique values between 2 sets/files提取 2 个集合/文件之间的唯一值
【发布时间】:2011-06-10 16:13:03
【问题描述】:

在 linux/shell 环境中工作,我该如何完成以下操作:

文本文件 1 包含:

1
2
3
4
5

文本文件 2 包含:

6
7
1
2
3
4

我需要提取文件 2 中不在文件 1 中的条目。因此,在此示例中为“6”和“7”。

如何从命令行执行此操作?

非常感谢!

【问题讨论】:

  • 是作业吗?如果是肯定的,请标记它。
  • 值的分隔符是什么?
  • 好收获!每个值都在自己的行上;所以换行 sep。

标签: linux perl bash scripting command-line


【解决方案1】:
$ cat file1 file1 file2 | sort | uniq -u
6
7

uniq -- 报告或过滤掉文件中的重复行

...重复 如果输入中的行不相邻,则不会检测到它们,因此 可能需要先对文件进行排序。

-u      Only output lines that are not repeated in the input.

打印 file1 两次以确保 file1 中的所有条目都被 uniq -u 跳过。

【讨论】:

    【解决方案2】:
    cat file1 file2 | sort -u > unique
    

    【讨论】:

    • sort -u 将保留每个重复项出现一次。在问题示例中,除了 67 的所需值之外,仍将打印 123
    【解决方案3】:
    $ awk 'FNR==NR {a[$0]++; next} !($0 in a)' file1 file2
    6
    7
    

    代码如何工作的解释:

    • 如果我们正在处理 file1,请跟踪我们看到的每一行文本。
    • 如果我们正在处理 file2,但没有看到行文本,则打印它。

    细节说明:

    • FNR是当前文件的记录号
    • NR 是所有输入文件的当前总记录数
    • FNR==NR 仅在我们读取 file1 时为真
    • $0 是当前文本行
    • a[$0] 是一个散列,其键设置为当前文本行
    • a[$0]++ 跟踪我们看到的当前文本行
    • !($0 in a) 只有在我们没有看到行文本时才为真
    • 如果上述模式返回 true,则打印文本行,这是未给出显式操作时的默认 awk 行为

    【讨论】:

    • 甜蜜!这很好用,但是如果每个值都在单独的行上,而不是像我的示例中那样用空格分隔(我实际上将它们放在新行上,但 SO 将它们格式化在同一行上)怎么办?
    • @mark 我的代码适用于这两种情况,但如果每个数字都在单独的行上,您可以完全删除 RS="[ \n]" 以使代码更短。另外,欢迎来到 SO。
    • @mark 顺便说一句,为了防止 SO 格式化您的代码,请突出显示它,然后按 CTRL+K 或单击 { } 图标。我已经通过此更改编辑了您的问题。
    • 只是为了记录,尽管这种技术有效,但它会为第一个文件中不存在的所有第二个文件条目创建a[$0] mem 位置。如果第二个文件有一百万行,则此解决方案不是最好的。另一方面,$0 in a 方法检查是否存在,但不会为第二个文件条目创建额外的内存位置。
    • @Cole 你是对的,它没有。我一定一直在考虑 17 年的 for(i in a) 构造。谢谢你让我诚实,我会更新答案。
    【解决方案4】:

    怎么样:

    diff file_1 file_2 | grep '^>' | cut -c 3-
    

    这将打印file_2 中不在file_1 中的条目。对于相反的结果,只需将 '>' 替换为 '

    文件甚至不需要排序。

    【讨论】:

      【解决方案5】:

      使用 grep:

      grep -F -x -v -f file_1 file_2 
      

      【讨论】:

      • 这会导致错误的结果,如 . (点)添加到 file_1。 grep -F -x -v -f file_1 file_2 确实是正确的。
      • @xebeche:谢谢!根据您的建议更正了代码行。
      【解决方案6】:

      我想知道以下哪种解决方案对于“较大”文件来说是“最快”的:

      awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2 # awk1 by SiegeX
      awk 'FNR==NR{a[$0]++;next}!($0 in a)' file1 file2          # awk2 by ghostdog74
      comm -13 <(sort file1) <(sort file2)
      join -v 2 <(sort file1) <(sort file2)
      grep -v -F -x -f file1 file2
      

      简而言之我的基准测试结果:

      • 不要使用grep -Fxf,它会慢得多(在我的测试中是 2-4 次)。
      • commjoin 稍快。
      • 如果 file1 和 file2 已经排序,commjoin 比 awk1 + awk2 快得多。 (当然,它们不假定已排序文件。)
      • awk1 + awk2,据说,使用更多的 RAM 和更少的 CPU。 comm 的实际运行时间较短,可能是因为它使用了更多线程。 awk1 + awk2 的 CPU 时间较短。

      为简洁起见,我省略了全部细节。但是,我认为任何有兴趣的人都可以联系我或只是重复测试。大致上,设置是

      # Debian Squeeze, Bash 4.1.5, LC_ALL=C, slow 4 core CPU
      $ wc file1 file2
        321599   321599  8098710 file1
        321603   321603  8098794 file2
      

      最快运行的典型结果

      awk2: real 0m1.145s  user 0m1.088s  sys 0m0.056s  user+sys 1.144
      awk1: real 0m1.369s  user 0m1.324s  sys 0m0.044s  user+sys 1.368
      comm: real 0m0.980s  user 0m1.608s  sys 0m0.184s  user+sys 1.792
      join: real 0m1.080s  user 0m1.756s  sys 0m0.140s  user+sys 1.896
      grep: real 0m4.005s  user 0m3.844s  sys 0m0.160s  user+sys 4.004
      

      顺便说一句,对于笨蛋来说:似乎a[$0]=1a[$0]++ 快,而(!($0 in a))(!a[$0]) 快。因此,对于 awk 解决方案,我建议:

      awk 'FNR==NR{a[$0]=1;next}!($0 in a)' file1 file2
      

      【讨论】:

      • 出色的基准测试、结果和优化。谢谢!
      【解决方案7】:

      这是另一个 awk 解决方案

      $ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
      6
      7
      

      【讨论】:

      • 有哪些规则可以使用() 代替{}?我假设这不是傻瓜主义,因为在这种情况下你倾向于使用gawk
      • 如您所知,awk 语法由/pattern/{action} 组成。 ((!$0 in a)) 是“模式”部分。 {action} 默认打印。就像你可以做NR==1(例如)。
      • 我想我对双括号更好奇,为什么(!$0 in a) 不够?顺便说一句,如果您在评论前加上@username,那么用户名实际上会收到一条通知,告知他们有一条待处理的评论,否则他们不会。 @username 前缀不是必需的,仅当您是撰写问题和/或回答人们评论的人时。所以从技术上讲,我不需要为这个评论做这件事。
      • @SiegeX,在这种情况下,双括号无关紧要。它是我的一个习惯。但是,如果有更多条件,则需要双括号。
      • 此解决方案是唯一一个在不创建额外内存位置的情况下检查数组是否存在的解决方案。
      【解决方案8】:

      使用一些鲜为人知的实用程序:

      sort file1 > file1.sorted
      sort file2 > file2.sorted
      comm -1 -3 file1.sorted file2.sorted
      

      这将输出重复,所以如果file1中有1个3,但file2中有2个,这仍然会输出1个3。如果这不是您想要的,请将输出从sort 传送到uniq,然后再将其写入文件:

      sort file1 | uniq > file1.sorted
      sort file2 | uniq > file2.sorted
      comm -1 -3 file1.sorted file2.sorted
      

      GNU coreutils 包中有许多实用程序,允许进行各种文本操作。

      【讨论】:

      • 很好地调用这些实用程序。您可以将其组合成一个更简单的形式并消除对临时文件的需求:comm -13 &lt;(sort file1) &lt;(sort file2) 我仍然更喜欢awk,只是因为它运行单个进程而不是 3,因为它不需要排序文件。这会对大文件产生很大影响。
      • join 也可以用于此。
      • @SiegeX - 我个人更喜欢带有 3 个命令的版本 - 这样,如果我需要调整命令(或者例如,获取更新的文件 1)我不需要重新运行整件事;对于非常大的文件,这可能是一个好处。此外,您提供的语法听起来像 bash,它可能不适用于其他 shell(/bin/sh 或 csh 派生类)
      【解决方案9】:

      如果您真的决定从命令行执行此操作,this site(搜索“未找到重复项”)有一个搜索重复项的 awk 示例。这可能是一个很好的起点。

      但是,我建议您为此使用 Perl 或 Python。基本上,程序的流程是:

      findUniqueValues(file1, file2){
          contents1 = array of values from file1
          contents2 = array of values from file2
          foreach(value2 in contents2){
              found=false
              foreach(value1 in contents1){
                  if (value2 == value1) found=true
              }
              if(!found) print value2
          }
      }
      

      这不是最优雅的方法,因为它的时间复杂度为 O(n^2),但它会完成这项工作。

      【讨论】:

        猜你喜欢
        • 2019-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-22
        • 1970-01-01
        • 1970-01-01
        • 2011-06-19
        相关资源
        最近更新 更多