AWK 根据 1 列的内容将输入 CSV 拆分为多个输出文件

AWK 根据 1 列的内容将输入 CSV 拆分为多个输出文件

一个文件data.csv有以下数据

1,avocado,mexican green fruit
1,kiwi,green fruit
1,banana,yellow fruit
1,mango,yellow fruit

为了将数据组织成水果类别,我已经完成了

awk -F ',' '{print >> ($3 ".csv")}' data.csv

它创建了 3 个文件,mexican green fruit.csv, green fruit.csv,yellow fruit.csv

我希望这些文件名称中的空格替换为下划线_

所以,文件名应该是mexican_green_fruit.csv,,green_fruit.csvyellow_fruit.csv

需要这一衬里的帮助awk才能做到这一点

寻找awk唯一的答案

答案1

GNU awk 的仅 awk 答案(按照 OP 的要求)将是:

awk -F',' '{print > gensub(/[[:space:]]+/,"_","g",$3) ".csv"}' data.csv

如果您的输入足够小以至于不能超过“打开文件太多”阈值,则任何 POSIX awk 的仅 awk 答案将是:

awk -F',' '{out=$3 ".csv"; gsub(/[[:space:]]+/,"_",out); print > out}' data.csv

如果您可能超过“打开文件过多”阈值,则任何 POSIX awk 的仅 awk 答案将是:

awk -F',' '{out=$3 ".csv"; gsub(/[[:space:]]+/,"_",out); if (!seen[$3]++) printf "" > out; print >> out; close(out)}' data.csv

但最后一个会很慢,因为它会为每次写入关闭并重新打开输出文件,并且假设您可以将每个$3值存储在内存中。您可以通过仅在输出文件发生更改时关闭输出文件来提高效率:

awk -F',' '$3 != prev {close(out); out=$3 ".csv"; gsub(/[[:space:]]+/,"_",out); if (!seen[$3]++) printf "" > out; prev=$3} {print >> out}' data.csv

如果您对不只使用 awk 的答案感到满意,那么使用任何 POSIX awk、排序和剪切的 DSU(装饰/排序/取消装饰)习惯用法,以下内容将针对任何大小的输入文件有效且稳健地工作可以sort处理(并且它被设计为使用请求分页等来处理极大的文件),并且对于任意数量的输出文件:

$ cat tst.sh
#!/usr/bin/env bash

awk '
    BEGIN{ FS=OFS="," }
    { print $3,NR,$0 }
' "${@:-}" |
sort -t',' -k1,1 -k2,2n |
cut -d',' -f3- |
awk '
    BEGIN{ FS=OFS="," }
    $3 != prev {
        close(out)
        out = $3 ".csv"
        gsub(/[[:space:]]+/,"_",out)
        prev = $3
    }
    { print > out }
'

$ ./tst.sh data.csv

$ head *.csv
==> data.csv <==
1,avocado,mexican green fruit
1,kiwi,green fruit
1,banana,yellow fruit
1,mango,yellow fruit

==> green_fruit.csv <==
1,kiwi,green fruit

==> mexican_green_fruit.csv <==
1,avocado,mexican green fruit

==> yellow_fruit.csv <==
1,banana,yellow fruit
1,mango,yellow fruit

有关 DSU 的更多信息,请参阅https://stackoverflow.com/questions/71691113/how-to-sort-data-based-on-the-value-of-a-column-for-part-multiple-lines-of-af/71694367#71694367

答案2

可以用一个函数来完成,例如:

awk -F, '
  function csvfile(name) {
    gsub(/[[:space:]]+/, "_", name)
    return name".csv"
  }
  {print >> csvfile($3)}'

此处将每个序列的一个或多个空白字符(包括空格、制表符、cr...)替换为_

答案3

(使用 gnu awk 或类似的)你可以按照以下方式运行一些东西

awk -F, '{print > gensub(/ /,"_","g",$3)".csv"}' ex.csv
  • gensub是一个函数式的sub——稍微容易编写。
  • >可能比>>除非您在执行此命令之前创建了一些 csv 更好。
  • 如果我们有数百万个不同的 3 美元值,我们可能会遇到问题。

编辑:应对新的要求(实际上是一个新的问题)

awk -F, '
  NF == 0     {next}
  !seen[$3]++ {print "Quantity, f..., c..." > gensub(/ /,"_","g",$3)".csv"}
              {print                        > gensub(/ /,"_","g",$3)".csv"}  
' ex.csv

相关内容