【问题标题】:Compare two files with awk and map congregated multiple values使用 awk 比较两个文件并映射聚合的多个值
【发布时间】:2021-08-09 11:17:54
【问题描述】:

我有两个文件需要比较并映射一个多行匹配的值。

我的映射文件 (map.csv) 如下所示:

id,name
123,Hans
123,Britta
232,Peter
343,Siggi
343,Horst

数据文件(data.csv)是

contact,id,names
m@a.de,123,
ad@23.com,343,
adf@er.org,123,
af@go.er,232,
llk@fh.com,343,
ad@wer.org,789,

不想要的输出应该是这样的

contact,id,names
m@a.de,123,Hans Britta 
ad@23.com,343,Siggi Horst 
adf@er.org,123,Hans Britta 
af@go.er,232,Peter 
llk@fh.com,343,Siggi Horst
ad@wer.org,789,NO ENTRY

映射文件中的一个 ID 有多个值,它们应以空格分隔打印到数据文件的列 names 中。如果映射文件中没有 ID,则应打印“NO ENTRY”。

这是 awk 命令

awk 'NR==FNR{a[$1];next}{print $0,($2 in a)? a[$2]:"NO ENTRY"}' map.csv data.csv

我显然失败了,因为我不知道如何遍历映射文件以将多个值获取到一个 id(或当前任何值)。

【问题讨论】:

    标签: loops csv awk


    【解决方案1】:

    使用您展示的示例,请尝试以下操作。

    awk '
    BEGIN{
      FS=OFS=","
    }
    FNR==NR{
      arr[$1]=(arr[$1]?arr[$1] " ":"")$2
      next
    }
    FNR==1{
      print
      next
    }
    {
      sub(/,$/,"")
      print $0,($2 in arr)?arr[$2]:"NO ENTRY"
    }
    ' map.csv data.csv
    

    说明:为上述添加详细说明。

    awk '                                 ##Starting awk program from here.
    BEGIN{                                ##Starting BEGIN section of this program from here.
      FS=OFS=","                          ##Setting FS and OFS as comma here.
    }
    FNR==NR{                              ##Checking condition which will be TRUE when map.csv is being read.
      arr[$1]=(arr[$1]?arr[$1] " ":"")$2  ##Creating arr with index of $1 and which has value of $2 and keep concatenating its value with same index.
      next                                ##next will skip all further statements from here.
    }
    FNR==1{                               ##Checking condition if this is first line of data.csv then do following.
      print                               ##Printing current line here.
      next                                ##next will skip all further statements from here.
    }
    {
      sub(/,$/,"")                        ##Substituting last comma with NULL here.
      print $0,($2 in arr)?arr[$2]:"NO ENTRY"  ##Printing current line and printing either value of arr with index of $2 OR printing NO ENTRY as per requirement.
    }
    ' map.csv data.csv                    ##Mentioning Input_file names here.
    

    【讨论】:

      【解决方案2】:

      您可以根据自己的情况使用两条规则。一个是从map.csv 捕获数据,然后是第二个规则来输出结果,例如

      (编辑——更新以精确匹配第一行输出)

      awk -F, '
          NR==FNR { if (FNR > 1) a[$1]=a[$1]" "$2; next }
          FNR==1  { print; next }
                  { printf "%s,%s,%s\n", $1, $2, a[$2]?a[$2]:"NO ENTRY" }
      ' map.csv data.csv
      

      第一条规则由NR=FNR 限定(当前记录号等于文件记录号——例如第一个文件)。第二条规则只在第二个文件上运行,在输出聚合数据之前不改变输出标题行。

      使用/输出示例

      您可以简单地选择-复制和鼠标中键将上面的命令粘贴到当前目录包含map.csvdata.csv 的xterm 中,结果如下:

      $ awk -F, '
      >     NR==FNR { if (FNR > 1) a[$1]=a[$1]" "$2; next }
      >     FNR==1             { print; next }
      >                        { printf "%s,%s,%s\n", $1, $2, a[$2]?a[$2]:"NO ENTRY" }
      > ' map.csv data.csv
      contact,id,names
      m@a.de,123, Hans Britta
      ad@23.com,343, Siggi Horst
      adf@er.org,123, Hans Britta
      af@go.er,232, Peter
      llk@fh.com,343, Siggi Horst
      ad@wer.org,789,NO ENTRY
      

      替代方案

      做同样的事情,但通过在输出开始允许使用print而不是printf之前显式设置OFS=","来简化(稍微)是:

      awk -F, '
          NR==FNR { if (FNR > 1) a[$1]=a[$1]" "$2; next }
          FNR==1  { OFS=","; print; next }
                  { print $1, $2, a[$2]?a[$2]:"NO ENTRY" }
      ' map.csv data.csv
      

      (相同的输出)

      【讨论】:

      • 两个答案都很好,并且给出了正确的结果。我选择了另一个答案作为解决方案,只是因为它更快一点并且解释了 awk 进程的单个步骤。
      • 别担心,祝你的脚本好运。 awk 是一个很棒的工具。
      猜你喜欢
      • 2017-01-31
      • 1970-01-01
      • 2018-03-04
      • 1970-01-01
      • 1970-01-01
      • 2017-07-25
      • 2012-09-05
      • 2015-02-22
      相关资源
      最近更新 更多