Awk

AWK 根據 1 列的內容將輸入 CSV 拆分為多個輸出文件

  • May 30, 2022

一個文件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.csv,yellow_fruit.csv

在這一班輪中需要幫助awk才能做到這一點

尋找awk唯一的答案

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

引用自:https://unix.stackexchange.com/questions/704050