【问题标题】:merging 2 csv files using awk使用 awk 合并 2 个 csv 文件
【发布时间】:2018-06-25 14:19:01
【问题描述】:

我有 3 个 CSV 文件:

基础文件(用 0 初始化的值)

steve tignor ash michael jose sam joshua
0       0     0     0     0     0    0

文件 1:

tignor michael  jose
888      9       -2

文件 2:

ash joshua
77   66

我需要的输出:

      steve tignor ash michael jose sam joshua
File1   0    888    0    9      -2   0     0
File2   0     0     77   0       0   0     66

我尝试先用 awk 对文件进行排序,然后用 paste 合并,但由于我有 1000 多列和 30 个文件,所以它不起作用。

代码:

awk -F"," 'NR==1{
  split($0,a,FS);asort(a);
  for(i=1;i<=NF;i++)b[$i]=i
} {
  for(i=1;i<=NF;i++)printf("%s,",$(b[a[i]]));
  print x
}' File1 > 1.csv

awk -F"," 'NR==1{
  split($0,a,FS);asort(a);
  for(i=1;i<=NF;i++)b[$i]=i
} {
  for(i=1;i<=NF;i++)printf("%s,",$(b[a[i]]));
  print x
}' File2 > 2.csv

paste -d"\n" 1.csv 2.csv > merge.csv

在这里需要一些帮助。提前致谢。

【问题讨论】:

  • 欢迎来到 SO,请将您的示例(代码)包装在 CODE TAGS 中,然后单击按钮 {} 并告诉我们。
  • 转换数据(或重新导出)以便于处理,tigor,888。祝你好运。

标签: bash awk


【解决方案1】:

我假设您省略了文件中的逗号。如果您使用的是空格分隔的文件,您可以更改 split 函数中使用的分隔符。

awk '
ARGIND==1 && FNR==1{
  split($0, base, ",")
  printf("file,%s\n",$0)
}
ARGIND > 1 && FNR==1{
  split($0, names, ",")
  printf("%s", ARGV[ARGIND])
}
ARGIND > 1 && FNR==2{
  split($0, values, ",")
  for(i in names)
    line[names[i]] = values[i]
  for(i in base){
    if(base[i] in line)
      printf(",%s", line[base[i]])
    else
      printf(",0")
  }
  delete line
  print ""
}
' base.csv file1.csv file2.csv

例子:

file1.csv:

tignor,michael,jose
888,9,-2

file2.csv:

ash,joshua
77,66

和base.csv:

steve,tignor,ash,michael,jose,sam,joshua
0,0,0,0,0,0,0

输出是:

file,steve,tignor,ash,michael,jose,sam,joshua
file1.csv,0,888,0,9,-2,0,0
file2.csv,0,0,77,0,0,0,66

基本上,脚本分两步运行:

  • 首先,我们从 base.csv 中读取名称并将它们存储到 大批。
  • 然后,对于每个文件,我们存储出现在其标题中的名称和 尝试为基本 csv 中的每一列打印一个值。如果我们不 具有对应于特定文件中的列的值,我们刚刚 改为打印 0。

附注我制作了一个新的 POSIX awk 兼容版本的脚本:

awk --posix '
NR==FNR && FNR==1{
  split($0, base, ",")
  printf("file,%s\n",$0)
}
NR>FNR && FNR==1{
  split($0, names, ",")
  printf("%s", FILENAME)
}
NR>FNR && FNR==2{
  split($0, values, ",")
  for(i in names)
    line[names[i]] = values[i]
  for(i in base){
    if(base[i] in line)
      printf(",%s", line[base[i]])
    else
      printf(",0")
  }
  delete line
  print ""
}
' base.csv file1.csv file2.csv

【讨论】:

  • 嗨卢卡斯,它就像一个魅力。您非常巧妙地使用了拆分功能。如果我理解得很好, FNR==2 用于选择最后两个文件。你能解释一下 ARGIND 和 ARGV 的用法吗?
  • 嗨!您可以找到所有内置变量here 的定义。为了节省您阅读文档的时间,我使用 ARGIND(代表参数索引)来选择我正在处理的文件,此变量仅存在于 gnu awk 中,对于常规 awk,请查看 here)。 FNR 变量表示文件中的行(或记录)编号,而不是 NR,它计算全局记录数(不是为每个文件重新启动)。
  • 所以总结一下使用的过滤器:ARGIND==1 &amp;&amp; FNR==1 -> 第一个文件(base.csv)和第一行ARGIND &gt; 1 &amp;&amp; FNR==1 -> 第一行和第一行之后的所有文件ARGIND &gt; 1 &amp;&amp; FNR==2-> 所有文件在第一行和第二行之后为了使其与 awk 兼容(不仅是 gawk),您可以通过以下方式更改过滤器:ARGIND==1 &amp;&amp; FNR==1 -&gt; NR==FNR &amp;&amp; FNR==1ARGIND &gt; 1 &amp;&amp; FNR==1 -&gt; NR&gt;FNR &amp;&amp; FNR==1ARGIND &gt; 1 &amp;&amp; FNR==2 -&gt; NR&gt;FNR &amp;&amp; FNR==2ARGIND==1 等同于 NR==FNR,如 here 所述。跨度>
  • 我忘了解释ARGV,它只是包含输入文件的文件名的数组。表达式ARGV[ARGIND] 对应于当前文件名并且可以被变量FILENAME 替换。我不知道为什么我改用ARGV[ARGIND](可能是因为它更接近其他一些编程语言)。
  • 嗨 Lukas,到目前为止,逻辑运行良好,我能够完美地解析我的文件。但是现在我遇到了问题,我看到每个文件都添加了额外的 cols。你想看看吗?我无法弄清楚逻辑在哪里中断。 $ 猫 4G.csv | awk -F"," '{print NF}' 1314 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 1319 .. As you can see there are 5 extra columns introduced after应用 awk 代码。如果你愿意,我可以和你分享确切的文件。
猜你喜欢
  • 1970-01-01
  • 2014-07-02
  • 2015-02-20
  • 2021-11-21
  • 1970-01-01
  • 2016-05-01
  • 2016-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多