匹配两个列表中的行,其中一个列表中包含通配符

时间:2019-05-31 08:55:47

标签: join awk sed grep

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

例如:

文件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 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

sed 's/\./\\./g; s/\*/./g' file2 | xargs -I{} grep {} file1

说明:

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

第一步是使用sed执行这些替换,第二步是将所有结果行作为搜索模式传递​​到grep,并在file1中搜索该模式。允许执行此操作的粘合剂是xargs,其中{}是占位符,代表sed命令结果中的一行。

注意:

这不是一种简单,安全的通用解决方案,您只需复制并粘贴即可:在包含星号的文件中,请注意grep正则表达式中认为特殊的所有字符。