【问题标题】:find a line based on a pattern, add new columns from another file to that line [closed]根据模式找到一行,将另一个文件中的新列添加到该行[关闭]
【发布时间】:2013-10-04 05:13:35
【问题描述】:

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

新信息:没有字段映射。这些文件来自 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 个新列应始终添加到行尾。

【问题讨论】:

  • 你想要做的事情可以通过join轻松处理。只需输入“man join”并阅读手册。确保文件已排序。
  • file2 的字段分隔符是什么?
  • @Codism file2 需要使用一些相当复杂的 RE 搜索/替换逻辑将其文本转换为逗号分隔的字段。这不是join 可以或应该处理的任何事情。
  • 您已经得到了不关心文件中有多少行的答案,并且依赖于 file2 没有像您最初发布的那样以逗号分隔和格式化,那么为什么现在要更改输入格式?
  • 这个线程中最聪明的人是@glennjackman,他只问了基本问题并等待回复,而我们其他人则四处奔波做出假设、编写代码和辩论可能性。该死!

标签: bash text sed awk grep


【解决方案1】:

首先使用搜索和替换将file2 修改为所需格式(逗号分隔)。在这里,我使用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

【讨论】:

  • 如果文件的行数不同,加入仍然有效吗?
  • join 应该可以工作,无论两个文件中的行数如何,只要它们正确排序
  • 尝试加入,总是报错,zchang@some.org 之后 z.chang 来到@some.org 时似乎加入不开心
  • @EvilGenius 没找到你。您可以在您的问题中添加此示例,以便我可以在最后尝试吗?
【解决方案2】:

使用较新版本的 GNU awk(适用于 \s\S,而不是 [[:space:]][^[:space:]])(适用于 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

【讨论】:

    【解决方案3】:

    我之前建议加入评论而不注意输入和输出格式。正如 EdMorton 指出的那样,即使对输入文件进行了排序,这也不能仅在 join 中完成。因此,在与 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
    

    删除前导“用户”并将第一次出现的冒号更改为逗号,这使得 file2 可以与带有逗号分隔符的 file2 连接。

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

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

    这是输出:

    【讨论】:

    • 您介意发布运行该命令时获得的输出吗?老实说,它似乎不太可能产生 OP 发布的输出,所以它可能需要调整?
    • @EdMorton:如果我仍然错过了一些格式,我会放弃:-)
    • 感谢您发布输出。我不明白你怎么能得到它,但现在我注意到你所说的关于 file2 被制表符分隔的假设。如果这是真的,那么这对你的脚本来说是有意义的,它也会使发布的 awk 和 perl 解决方案变得更加简单 - 我从来没有想过可能是这种情况,它在我看来就像问题中的随机空白。如果是这样的话,我希望 OP 已经提示我们!
    • @EdMorton:同意,如果 file2 是空格分隔的,那么脚本必须要复杂得多。如果您仍然选择我,我将删除我对您的评论之一的支持;-)
    • @EdMorton:我只是在开玩笑,我不会觉得被冒犯;-)
    猜你喜欢
    • 1970-01-01
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多