【问题标题】:Grep file with two columns as input具有两列作为输入的 Grep 文件
【发布时间】:2013-07-03 15:45:15
【问题描述】:

我有一个包含以下行的文件:

"ALMEREWEG               ";" 45  ";"      ";"ZEEWOLDE                ";"3891ZN"
"ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
"ALMEREWEG               ";" 51  ";"      ";"ZEEWOLDE                ";"3891ZN"
"ALMEREWEG               ";" 52  ";"      ";"ZEEWOLDE                ";"3891ZP"
"ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"

我还有第二个文件,其中包含以下行:

3891ZP;50;
3891ZN;53;A
3891ZN;53;B
3891ZN;54;

现在我想根据第二个文件的模式对第一个文件进行 grep,其中:

A) 第 2 个文件的第 1 列存在于第 1 个文件的第 5 列;和

B) 第 2 个文件的第 2 列存在于第 1 个文件的第 2 列中。

我的问题:如何做到这一点?

2013 年 7 月 7 日更新:我更新了 file2 格式以反映第三列(数字就足够了)。

【问题讨论】:

    标签: bash awk grep matching


    【解决方案1】:

    awk 的一种方式:

    awk -F';' '
    NR==FNR {
      a[$1]=$2
      next
    }
    {
      line=$0
      gsub(/\"/,"")
      gsub(/ *; */,";")
      if (a[$5]==$2) {
        print line
        line=""
      }
    }' file2 file1
    

    输出

    "ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"
    

    【讨论】:

    • 您的代码非常好 - 但是当特定邮政编码中有多个门牌需要匹配时,它会失败(在第一个文件中,第二列是门牌号,第五列是邮政编码)。我冒昧地写了一个改进的答案 - 但对你来说,如果没有你的榜样,我将无法做到这一点。在研究了您的代码之后,我终于(我想我)了解了 NR==FNR 的工作原理!仅仅为此而支持您的答案 - “它非常有用”。
    • 感谢@Floris 的客气话。我同意,如果第二个文件有多个特定 zip 的门牌号,那么我们的关联数组肯定会失败。根据 OP 的样本数据,我有点假设情况并非如此。 :)
    • 是的 - 但我来自荷兰,我认识数据类型。而且由于 OP 在对另一个问题的评论中说他在文件中有数百万行,我认为冲突将是不可避免的。因此需要改进的答案。不幸的是,无法在评论中真正显示出来。不确定代码在如此大的数据集上的表现如何......
    • 性能可能与perl 不匹配,但我作为系统测试人员经常使用awk,它在数据解析和报告方面非常快。 :)
    • 我同意 awk 速度很快,但检查表中每一行的 5M 元素数组是一个相当大的挑战。我想知道是否有办法加快速度。
    【解决方案2】:

    大量借鉴@JS,我提供了以下改进的解决方案。他的代码的问题是,如果你在同一个邮政编码中有多个门牌号,它只会匹配最后一个。通过创建一个复合关联数组(如果是名称......基本上将两个字段连接在一起),您可以解决这个问题:

    创建文件postcode.awk:

    BEGIN {
      FS=";"
    }
    # loop around as long as the total number of records read
    # is equal to the number of records read in this file
    # in other words - loop around the first file only
    NR==FNR {
      a[$1,$2]=1 # create one array element for each $1/$2 pair
      next
    }
    # loop around all the elements of the second file:
    # since we're done processing the first file
    {
      # copy the original line before modifying it
      line=$0
      # take out the double quotes
      gsub(/\"/,"")
      # take out the spaces on either side of the semicolons
      gsub(/ *; */,";")
      # see if the associative array element exists:
      if (a[$5,$2]==1) {
        # echo the original line that matched:
        print line
      }
    }
    

    使用测试文件file1如下(我添加了一行来显示边框情况):

    "ALMEREWEG               ";" 45  ";"      ";"ZEEWOLDE                ";"3891ZN"
    "ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 52  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"
    

    和密钥文件file2(再次添加一行):

    3891ZP;50
    3891ZP;52
    3891ZN;53
    

    你会看到 JS 的代码与数字 50 的行不匹配。

    但我的代码可以:

    awk -f postcode.awk file2 file1
    

    生产

    "ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 52  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"
    

    【讨论】:

    • 感谢您的出色建议!我刚刚运行它,但我得到了零匹配。原因可能是 file2 实际上有一点不同的格式,它是:3891ZP;52; 3891ZP;53;A 3891ZP;53;B(A 和 B 是门牌号的特殊后缀)。这可能是不匹配任何内容的原因(即使它仅适用于文件中的几行)? (问题相应更新)
    • 假设file2 中没有多余的空格,格式更改不应影响大多数匹配项。你确定你的论点是正确的吗?稍后将使用更新的文件格式进行测试(在电脑前,而不是手机前)
    • 顺便说一句 - 我的解决方案至少适用于您的原始(已发布)测试文件吗?
    • 我刚刚用你的新文件测试了它 - 得到了相同的结果(虽然现在你只得到一个匹配 53A53B 但我怀疑这就是你想要的)。您能否发布一段不适合您的文件,包括您用来处理它的确切命令?
    【解决方案3】:

    您可以使用sed 之类的东西为grep 构造模式:

    $ grep -Ef <(sed -r 's/(.*);(.*)/^[^;]*;[^;]*\2[^;]*;([^;]*;){2}[^;]*\1/' file2) file1
    "ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"
    

    【讨论】:

    • 谢谢,但我担心文件太大而无法执行此操作,因为我得到:“grep:内存耗尽”。 file1 有 8,197,924 行,file2 有 50,0001 行。
    【解决方案4】:

    我已使用 bash 的 IFSread 将 file2 拆分为列。然后将列传递给 grep:

    # read line by line
    while IFS=$'\n' read line ; do
        # split into columns
        IFS=$';' read -a col <<< "$line"
        # the expression can be refined but should work well as is
        grep -e ' '${col[1]}'  ";".*;.*";"'${col[0]} file1
    done < file2
    

    输出:

    "ALMEREWEG               ";" 50  ";"      ";"ZEEWOLDE                ";"3891ZP"
    "ALMEREWEG               ";" 53  ";"      ";"ZEEWOLDE                ";"3891ZN"
    

    【讨论】:

    • IFS=$'\;' & IFS=';' 有区别吗?两种用法我都见过,不知道有没有区别。
    • @anishsane IFS=$';' 没问题。这只是一个错字。 \; 也可以,但不是必需的。将删除它,谢谢! ;)
    • 我是出于好奇才问的。不是要纠正你;-)那么,有没有区别? (可悲的是,因为它主要是特殊字符,我无法谷歌答案......:O)
    • 没有问题。只有在您使用转义序列(如\n\t)时,\ 才有意义。因为没有使用d 的转义序列,所以 `` 没有意义。它将被忽略
    猜你喜欢
    • 2013-07-30
    • 2019-05-09
    • 1970-01-01
    • 2013-09-24
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    • 2016-03-11
    相关资源
    最近更新 更多