【问题标题】:AWK: How to merge CSV files and eliminate rows that contain certain values?AWK:如何合并 CSV 文件并消除包含某些值的行?
【发布时间】:2020-09-03 10:28:32
【问题描述】:

我有数百个 CSV 文件。每个 CSV 文件都与此类似:

| KEYWORD | NUMBER OF COMPS | AVGE M E (K) | GS/M | EST. A SE/M | C CORE |
|---------|-----------------|--------------|------|-------------|--------|
| Apples  | 311             | 12           | N/A  | <100        | 10     |
| Bananas | >1,200          | 737          | N/A  | 490         | 88     |
| Oranges | 48              | 184          | N/A  | N/A         | 1      |
| Fruits  | 161             | 94           | N/A  | -           | 6      |

(我以表格形式发布,以使其更具可读性,但 CSV 数据位于本文底部)。

所有 CSV 文件都有相同的标题行。只是数据不同。

我想做以下事情:

  1. 将所有 CSV 文件合并在一起,但只有 1 个标题行。
  2. 省略 EST. A SE/M(第 5 列)包含以下任何数据的任何行:&lt;100N/A-

数据说明

有时 CSV 文件中的部分甚至所有单元格都用引号引起来。 其他时候他们不是。 有时第一列(关键字)可能包含多个单词或重音字符。

到目前为止我的代码

此代码将所有 CSV 文件合并为 1,没有一个标题

awk '(NR == 1) || (FNR > 1)' *.csv > ^0-output.csv

这很好用。

但是,我不确定如何在合并后删除不需要的行。 到目前为止,我有这个:

awk '$5 !~ /(<100|N\/A|-)/' ^0-output.csv > ^0-output.csv

但是当我使用这段代码时,它只会产生一个空白文件。 另外,我不确定是否有办法将它集成到第一行,所以它用一个命令完成所有事情。


注释

以下是 CSV 格式的数据外观

Sample1.csv

KEYWORD,NUMBER OF COMPS,AVGE M E (K),GS/M,EST. A SE/M,C CORE
Apples,311,12,N/A,<100,10
Bananas,">1,200",737,N/A,490,88
Oranges,48,184,N/A,N/A,1
Fruits,161,94,N/A,-,63

Sample2.csv

KEYWORD,NUMBER OF COMPS,AVGE M E (K),GS/M,EST. A SE/M,C CORE
Dino,588,67,N/A,888,234
Thunder,">1,200",211,N/A,<100,77
Ninja,95,37,N/A,-,878

Sample3.csv

KEYWORD,NUMBER OF COMPS,AVGE M E (K),GS/M,EST. A SE/M,C CORE
Blur,84,2454,N/A,-,234

Sample4.csv

"KEYWORD","NUMBER OF COMPS","AVGE M E (K)","GS/M","EST. A SE/M","C CORE"
"hedgehog rolls ròund",32,481,N/A,"878",13
"Clever Fox jumps Hîgh",233,83,N/A,"<100",12
"Bear à lot",122,35,N/A,"-",11
"kitten hîgh life","121","673","32","N/A","15"

请注意:将使用已完成脚本的实际文件将具有多种文件名。他们不会总是遵循样本 1、样本 2 等的模式。


预期输出

预期输出:(CSV 格式)

KEYWORD,NUMBER OF COMPS,AVGE M E (K),GS/M,EST. A SE/M,C CORE
Bananas,">1,200",737,N/A,490,88
Dino,588,67,N/A,888,234
"hedgehog rolls ròund",32,481,N/A,"878",13

(注意:当最终的 CSV 文件在 Apple Numbers 中打开时,预期输出是否保留换行引号并不重要)

预期输出:(可读格式)

| KEYWORD | NUMBER OF COMPS | AVGE M E (K) | GS/M | EST. A SE/M | C CORE |
|---------|-----------------|--------------|------|-------------|--------|
| Bananas | >1,200          | 737          | N/A  | 490         | 88     |
| Dino    | 588             | 67           | N/A  | 888         | 234    |
| hedgehog rolls ròund    | 588             | 67           | N/A  | 888         | 234    |

环境: 我正在使用 Mac OS X 10.14.6。我无法安装其他版本的 awk。

【问题讨论】:

    标签: csv awk


    【解决方案1】:

    您可以使用 &amp;&amp; 将 2 个条件合并为一个:

    awk -F, 'NR==1 || (FNR>1 && $5 !~ /^(<100|N\/A|-)$/)' *.csv > output.csv
    

    如果$5&lt;100-N/A,此处$5 !~ /^(&lt;100|N\/A|-)$/) 将跳过一行。使用正则表达式锚点 ^$ 以避免匹配不需要的字符串(例如 1000AB-123)非常重要。


    您似乎在 file1.csv 中也有双引号中的逗号。在这种情况下,您应该使用gnu-awk 命令:

    awk -v FPAT='"[^"]*"|[^,]*' '
    NR == 1 || (FNR > 1 && $5 !~ /^(<100|N\/A|-)*$/)' *.csv > output.csv
    

    【讨论】:

    【解决方案2】:

    编辑:根据 OP 的 cmets," 之间也可能有一个逗号,因此最好使用 FPAT,编写和测试使用 GNU awk

    awk -v FPAT='[^,]*|"[^"]+"'  '
    { sub(/\r$/,"") }
    FNR==1{
      if(NR==1){ print }
      next
    }
    $5=="<100"||$5=="N/A"||$5=="-"{
      next
    }
    1
    ' *.csv
    


    您能否仅在所示示例上尝试使用 GNU awk 进行跟踪、编写和测试。

    awk '
    BEGIN{
      FS=OFS=","
    }
    FNR==1{
      if(NR==1){ print }
      next
    }
    $5=="<100"||$5=="N/A"||$5=="-"{ next }
    1
    '  *.csv
    

    或者,如果您的值还可以包含其他内容,并且您想使用正则表达式来匹配您想要忽略的值,请尝试以下操作。

    awk '
    BEGIN{
      FS=OFS=","
    }
    FNR==1{
      if(NR==1){ print }
      next
    }
    $5~/<100/ || $5~/N\/A/ || $5~/-/{ next }
    1
    '  *.csv
    

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

    awk '                                        ##Starting awk program from here.
    BEGIN{                                       ##Starting BEGIN section of this program from here.
      FS=OFS=","                                 ##Setting field separator as comma here.
    }
    FNR==1{                                      ##Checking condition if its firt line of current Input_file then do following.
      if(NR==1){ print }                         ##If its very first line of very first Input_file then print that line.
      next                                       ##next will skip all further statements from here.
    }
    $5=="<100"||$5=="N/A"||$5=="-"{ next }       ##Checking condition if 5th field contains either <100 OR N/A OR - then skip all further statements.
    1                                            ##awk'sh way to print the current line.
    '  *.csv                                     ##Passing all .csv files to awk program from here.
    

    【讨论】:

    【解决方案3】:

    在我看来,您只对测试倒数第二个字段感兴趣,并且该字段和最后一个字段都不能包含逗号,因此只需从末尾而不是从每行的开头计算字段编号,然后您就不用'不在乎前面的字段是否包含逗号。鉴于此,这将使用任何 awk:

    $ awk -F',' '(NR==1) || (FNR>1 && $(NF-1)!~/^"?(<100|N\/A|-)"?$/)' *.csv
    KEYWORD,NUMBER OF COMPS,AVGE M E (K),GS/M,EST. A SE/M,C CORE
    Bananas,">1,200",737,N/A,490,88
    Dino,588,67,N/A,888,234
    "hedgehog rolls ròund",32,481,N/A,"878",13
    

    【讨论】:

      猜你喜欢
      • 2011-09-06
      • 2021-01-31
      • 2017-05-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 2014-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多