【问题标题】:Match Lines From Two Lists With Wildcards In One List将两个列表中的行与一个列表中的通配符匹配
【发布时间】:2019-06-03 22:00:59
【问题描述】:

我有两个列表,其中一个包含通配符(在本例中由 * 表示)。我想比较这两个列表并创建一个匹配的输出,每个通配符 * 代表一个字符。

例如:

文件 1

123456|Jane|Johnson|Pharmacist|janejohnson@gmail.com
09876579|Frank|Roberts|Butcher|frankie1@hotmail.com
092362936|Joe|Jordan|Joiner|joe@joesjoinery.com
928|Bob|Horton|Farmer|bhorton@farmernews.co.uk

文件 2

1***6|Jane|Johnson|Pharmacist|janejohnson@gmail.com
09876579|Frank|Roberts|Butcher|f**1@hotmail.com
092362936|Joe|Jordan|J*****|joe@joesjoinery.com
928|Bob|Horton|Farmer|b*****n@f*********.co.uk

输出

092362936|Joe|Jordan|Joiner|joe@joesjoinery.com
928|Bob|Horton|Farmer|bhorton@farmernews.co.uk

解释

前两行不被视为匹配,因为 *s 的数量不等于第一个文件中显示的字符数。后两者是,所以它们被添加到输出中。

我试图找出在 AWK 中执行此操作并使用 Join 的方法,但我什至不知道有什么方法可以开始尝试实现这一目标。任何帮助将不胜感激。

【问题讨论】:

  • 在文件 2 上,* 的列可以出现在任何列中还是只出现在最后两列中?
  • 这只是一个例子,但在我正在使用的文件中,它只会出现在最后一列。
  • 您可以修改问题以仅反映最后一列吗?还添加您所做的任何努力
  • 有必要根据列来追求吗?要求是整行匹配,而不是单列。

标签: join awk sed grep


【解决方案1】:
$ cat tst.awk
NR==FNR {
    file1[$0]
    next
}
{
    # Make every non-* char literal (see https://stackoverflow.com/a/29613573/1745001):
    gsub(/[^^*]/,"[&]")  # Convert every char X to [X] except ^ and *
    gsub(/\^/,"\\^")     # Convert every ^ to \^

    # Convert every * to .:
    gsub(/\*/,".")

    # Add line start/end anchors
    $0 = "^" $0 "$"

    # See if the current file2 line matches any line from file1
    # and if so print that line from file1:
    for ( line in file1 ) {
        if ( line ~ $0 ) {
            print line
        }
    }
}

$ awk -f tst.awk file1 file2
092362936|Joe|Jordan|Joiner|joe@joesjoinery.com
928|Bob|Horton|Farmer|bhorton@farmernews.co.uk

【讨论】:

    【解决方案2】:
    sed 's/\./\\./g; s/\*/./g' file2 | xargs -I{} grep {} file1
    

    说明:

    我会利用正则表达式匹配。为此,我们需要将每个星号* 变成一个点.,它代表正则表达式中的任何字符。作为启用正则表达式的副作用,我们需要转义所有特殊字符,尤其是.,以便按字面意思理解它们。在正则表达式中,我们需要使用\. 来表示一个点(而不是任何字符)。

    第一步是使用sed 执行这些替换,第二步是将每个结果行作为搜索模式传递​​给grep,然后在file1 中搜索该模式。允许这样做的粘合剂是xargs,其中{} 是一个占位符,表示sed 命令结果中的一行。

    注意:

    这不是一个通用的、安全的解决方案,您可以简单地复制和粘贴:您应该注意文件中包含星号的任何字符,这些字符在 grep 正则表达式中被认为是特殊的。


    更新:

    jhnc 将转义扩展到以下任何字符:.\^$[],因此几乎适用于所有类型的电子邮件地址。然后,他/她通过使用-f -sed 的结果作为搜索表达式传递给grep,从而避免使用xargs

    sed 's/[.\\^$[]/\\&/g; s/[*]/./g' file2 | grep -f - file1
    

    这个解决方案更通用也更高效,请参阅下面的评论。

    【讨论】:

    • sed 's/[.\\^$[]/\\&/g; s/[*]/./g' file2 | grep -f - file1(或-f /dev/stdin,如果-f -未被识别,则将sed输出写入临时文件)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多