Join 将两个列表中的行与一个列表中的通配符匹配

Join 将两个列表中的行与一个列表中的通配符匹配,join,awk,sed,grep,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

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

例如:

文件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中找出实现这一点的方法,但我甚至不知道如何开始尝试实现这一点。任何帮助都将不胜感激

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

我会利用正则表达式匹配。为此,我们需要将每个星号
*
转换为点
,它表示正则表达式中的任何字符。作为启用正则表达式的一个副作用,我们需要转义所有特殊字符,特别是
,以便从字面上理解它们。在正则表达式中,我们需要使用
\.
来表示点(与任何字符相对)

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

注意:

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


更新:

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

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

此解决方案更通用、更高效,请参见下面的注释。

文件2中带有
*
的列可以出现在任何列中,还是只出现在最后两列中?这只是一个示例,但在我使用的文件中,它只会出现在最后一列中。您能否修改问题以仅反映最后一列?还要根据专栏添加您为实现这一目标所做的任何努力?要求整行匹配,而不是一列。如果此处发布的任何答案有帮助,请查看下一步要做什么。
sed的/[.\\^$[]/\&/g;s/[*]//g'file2 | grep-f-file1
(或者
-f/dev/stdin
,或者在未识别到
-f-
的情况下将sed输出写入临时文件)
$ 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