根据模式查找一行,将另一个文件中的新列添加到该行

时间:2013-09-27 19:30:10

标签: bash text sed awk grep

我需要根据给定的模式(在本例中为电子邮件地址)合并2个文件 如果可能的话,我想用grep / sed来做这个。请解释答案,以便我的弱脑能够处理它。

新信息:没有Field Map。这些文件来自2个不同的数据源,并且行数并不总是相同。这是现实世界:当Bob停止更新他的博物馆会员资格时,他将不再列在文件2中。这是关于大型非营利组织会员身份的每周报告的一部分。文件1将一直增长到年底,文件2可能会缩小或增长。

我已将第二个文件设置为始终以逗号分隔的位置,并且第一个字段将始终为电子邮件地址,如文件1中所示。

在文件1中有一行如下:

007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never

在文件2中,有一行如下:

User 007@some.org:  Forward To:None  Enabled:false  Action:KEEP

我希望将文件2中的内容添加到文件1中,以创建格式为3的文件:

007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never,Forward to:None,Enabled:false,Action:KEEP

3个新列应始终添加到该行的末尾。

3 个答案:

答案 0 :(得分:1)

首先使用搜索和替换修改所需格式的文件2(逗号分隔)。在这里,我使用perl来实现这一目标。 sed也可以使用

perl -pe 's/User\s+(\S+):\s+(.*?:\S+)\s+(.*?:\S+)\s+(.*?\S+)/\1,\2,\3,\4/g' file2 > file2_new

这将导致:

$ cat file2_new
007@some.org,Forward To:None,Enabled:false,Action:KEEP

然后只需使用join与分隔符,连接两个文件

join -t , file1 file2_new

输出:

007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never,Forward To:None,Enabled:false,Action:KEEP

答案 1 :(得分:1)

使用较新版本(适用于\s\S代替[[:space:]][^[:space:]])GNU awk(适用于gensub()):

$ cat tst2.awk
BEGIN {re="\\S+\\s+([^:]+):\\s+([^:]+:\\S+)\\s+(\\S+)\\s+(\\S+).*"; FS=OFS=","}
NR==FNR {map[gensub(re,"\\1","")] = gensub(re,"\\2,\\3,\\4",""); next}
{print $0, map[$1]}
$
$ cat file1
007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never
$
$ cat file2
User 007@some.org:  Forward To:None  Enabled:false  Action:KEEP
$
$ awk -f tst2.awk file2 file1
007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never,Forward To:None,Enabled:false,Action:KEEP

或任何现代的awk:

$ cat tst.awk
BEGIN{ FS=OFS="," }
NR==FNR {
    email = $0
    gsub(/^[^[:space:]]+[[:space:]]+|:.*/,"",email)

    sub(/^[^:]+:[[:space:]]*/,"")

    rec = ""
    while ( match($0,/[^:]+:[^:[:space:]]+/) > 0 ) {
        rec = rec (rec ? OFS : "") substr($0,RSTART,RLENGTH)
        $0 = substr($0,RSTART+RLENGTH+1)
        sub(/^[[:space:]]+/,"",$0)
    }

    map[email] = rec
    next
}

{ print $0, map[$1] }
$
$ cat file1
007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never
$
$ cat file2
User 007@some.org:  Forward To:None  Enabled:false  Action:KEEP
$
$ awk -f tst.awk file2 file1
007@some.org,007,/Members/Inactive/Delete,2013-06-07T04:41:56.000Z,Never,Forward To:None,Enabled:false,Action:KEEP

答案 2 :(得分:0)

我以前建议加入评论而不注意输入和输出格式。正如EdMorton指出的那样,即使输入文件已经排序,也不能单独在连接中完成。所以经过与EdMorton的一些讨论,我实际上详细地解决了这个问题,这是我目前的解决方案,假设第二个文件是TAB分离的:

sed -re 's/^User\s//' -e 's/:/,/' file2 | join -t , file1 - | sed -re 's/\t/,/g' -e 's/,,/,/'

以上命令在我的cygwin / win7环境下工作,如果你的shell或file2分隔符不同,你可能需要稍微玩一下。

一些解释:

sed -re 's/^User\s//' -e 's/:/,/' file2

删除前导“user”并将冒号的第一个匹配项更改为逗号,这使得file2可以使用逗号分隔符与file2连接。

sed -re 's/\t/,/g' -e 's/,,/,/'

根据最终格式的要求,用逗号替换分隔符。因为join会在file1和file2之间的输出中添加一个分隔符,所以我们会看到一对没有最后替换的逗号。

这是输出:

join result