【问题标题】:Use AWK to print FILENAME to CSV使用 AWK 将 FILENAME 打印到 CSV
【发布时间】:2016-06-30 01:04:55
【问题描述】:

我有一个小脚本来比较一堆 CSV 文件中的一些列。 它工作正常,但有些事情困扰着我。

代码如下:

FILES=./*
for f in $FILES

do
    cat -v $f | sed "s/\^A/,/g" > op_tmp.csv
    awk -F, -vOFS=, 'NR == 1{next} $9=="T"{t[$8]+=$7;n[$8]} $9=="A"{a[$8]+=$7;n[$8]} $9=="C"{c[$8]+=$7;n[$8]} $9=="R"{r[$8]+=$7;n[$8]} $9=="P"{p[$8]+=$7;n[$8]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' op_tmp.csv >> output.csv
    rm op_tmp.csv
done

只是为了解释: 我得到目录上的所有文件,然后我使用 CAT 替换除数 ^A 为管道 |。 然后我使用 awk onliner 比较我需要的列并将结果打印到 output.csv。

但现在我想在每个循环之前打印文件名。 我尝试在同一行中使用 cat sed 和 awk 并打印 $FILENAME,但它不起作用:

cat -v $f | sed "s/\^A/,/g" | awk -F, -vOFS=, 'NR == 1{next} $9=="T"{t[$8]+=$7;n[$8]} $9=="A"{a[$8]+=$7;n[$8]} $9=="C"{c[$8]+=$7;n[$8]} $9=="R"{r[$8]+=$7;n[$8]} $9=="P"{p[$8]+=$7;n[$8]} END{ for (i in n){print i "|" "A" "|" a[i]; print i "|" "C" "|" c[i]; print i "|" "R" "|" r[i]; print i "|" "P" "|" p[i]; print i "|" "T" "|" t[i] "|" (t[i]==a[i]+c[i]+r[i]+p[i] ? "ERROR" : "MATCHED")} }' > output.csv

谁能帮忙?

【问题讨论】:

    标签: bash csv awk sed cat


    【解决方案1】:

    您可以更好地重写整个脚本,但假设它现在可以满足您的需求,只需添加

    echo $f >> output.csv

    在 awk 调用之前。

    如果要在每个 awk 输出行中添加文件名,则必须将其作为参数传递,即

     awk ... -v fname="$f" '{...; print fname... etc
    

    【讨论】:

      【解决方案2】:

      重写:

      for f in ./*; do
          awk -F '\x01' -v OFS="|" '
              BEGIN { 
                  letter[1]="A"; letter[2]="C"; letter[3]="R"; letter[4]="P"; letter[5]="T" 
                  letters["A"] = letters["C"] = letters["R"] = letters["P"] = letters["T"] = 1
              }
              NR == 1 {next} 
              $9 in letters {
                  count[$9,$8] += $7
                  seen[$8]
              }
              END { 
                  print FILENAME
                  for (i in seen) {
                      sum = 0
                      for (j=1; j<=4; j++) {
                          print i, letter[j], count[letter[j],i]
                          sum += count[letter[j],i]
                      }
                      print i, "T", count["T",i], (count["T",i] == sum ? "ERROR" : "MATCHED")
                  } 
              }
          ' "$f"
      done > output.csv
      

      注意事项:

      • 一旦您的文件名中包含空格,您遍历文件的方法就会中断
      • 尽量减少重复。
      • 换行符是免费的,使用它们来提高可读性
      • 改进变量名称in 等——这里的“字母”和“字母”可以使用改进来保持关于这些符号的一些含义
      • awk 有一个 FILENAME 变量(这是您问题的实际答案
      • awk 将 \x01 理解为 Ctrl-A -- 我假设这是您输入文件中的字段分隔符
      • 定义一个您将实际使用的输出字段分隔符

      如果您有 GNU awk(版本???),您可以使用 ENDFILE 块并完全取消 shell for 循环:

      gawk -F '\x01' -v OFS="|" '
          BEGIN {...}
          FNR == 1 {next}
          $9 in letters {...}
          ENDFILE {
              print FILENAME
              for ...
              # clean up the counters for the next file
              delete count
              delete seen
          }
      ' ./* > output.csv
      

      【讨论】:

      • 即使没有 gawk,您也可以通过其他一些调整,将该 END 块放在一个函数中并在 FNR==1END 条件下调用它,这样就不需要 shell 循环. ENDFILE 出现在 gawk 4.0 顺便说一句,请参阅 gnu.org/software/gawk/manual/gawk.html#Feature-History
      • 重写工作,最终我会更新到类似的东西,但现在我只是使用变量 $fname 调整并在每个循环之前打印。还删除了临时文件和猫。非常感谢。
      猜你喜欢
      • 2017-10-14
      • 1970-01-01
      • 1970-01-01
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 2014-04-09
      • 1970-01-01
      • 2012-01-03
      相关资源
      最近更新 更多