Linux

如何使用 AWK 將某些列從一個 CSV 文件提取到另一個文件?

  • February 27, 2021

我有一個非常大的 CSV 日誌文件,其中包含如下欄位:

aaa=somedata1,bbb=somedata2,ccc=somedata3,eee=somedata5,hhh=somedata8
aaa=somedata1,ddd=somedata4,fff=somedata6,hhh=somedata8
aaa=somedata1,bbb=somedata2,hhh=somedata8,ggg=somedata9,jjj=somedata11

這個文件的問題是,當沒有值時,生成設備甚至不包含“fieldname=”,因此,由於缺少欄位,CSV 看起來很混亂(因此,每次缺少一個欄位時,其餘的目前欄位被拖到 CSV 的左側)。

我的想法是只提取使用 AWK 相關的某些列,並且我需要將其輸出到新的 CSV 中。

例如,在上面的範例中,我想提取包含欄位“aaa”和“hhh”的所有列,以使新的 CSV 看起來像這樣:

aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

但是,我有兩個問題:

  1. 我不知道如何在 AWK 中查找幾個條件(我什至嘗試將我需要的欄位/關鍵字的名稱寫到 TXT 文件中並在 AWK 中讀取它,但我做不到)。
  2. 每次我嘗試列印結果列時,新的 CSV 只列印一個巨大的列,我似乎找不到列印分隔列的方法。

感謝任何幫助!

— 編輯1 —

是的,我嘗試使用一些單獨的 AWK 命令,有點像這樣:

awk '{for (i=1;i<=NF;i++) if ($i ~ /aaa/) { print $i}}' > aaa.csv
awk '{for (i=1;i<=NF;i++) if ($i ~ /hhh/) { print $i}}' > hhh.csv

然後嘗試使用(當然,我有興趣提取總共 10 個不同的列,但出於簡潔的原因,我只在範例中放置了 2 個):

paste -d "," aaa.csv hhh.csv > Allcolumns.csv

—編輯2—

我總共有大約 10 個相關列要提取到一個新文件中,因為原始文件是一個日誌,所以我確定了哪些列出現在所有行中,而那些列是我真正需要的。如果它們沒有出現在原始文件中,我想最好的做法是讓最終文件反映“aaa,hhh,,iii”之類的內容。

每當您的數據中有 tag=value 對時,我發現最好首先創建一個數組來保存該映射(tag2val[]如下),然後您可以通過它們的標籤(也稱為名稱或鍵)引用所有值。

在所有 Unix 機器上的任何 shell 中使用任何 awk:

$ cat tst.awk
BEGIN {
   FS = OFS = ","
   numTags = split("aaa,hhh",tags)
}
{
   delete tag2val
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       tag2val[tag] = $i
   }

   for (i=1; i<=numTags; i++) {
       tag = tags[i]
       printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
   }
}
$ awk -f tst.awk file
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

如果您想在每一行上列印所有可能的欄位,那麼這是一種 2 遍方法,其中第一遍只是從每一行中辨識所有可能的欄位:

$ cat tst.awk
BEGIN {
   FS = OFS = ","
}
NR==FNR {
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       if ( !seen[tag]++ ) {
           tags[++numTags] = tag
       }
   }
   next
}
{
   delete tag2val
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       tag2val[tag] = $i
   }

   for (i=1; i<=numTags; i++) {
       tag = tags[i]
       printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
   }
}
$ awk -f tst.awk file file
aaa=somedata1,bbb=somedata2,ccc=somedata3,eee=somedata5,hhh=somedata8,,,,
aaa=somedata1,,,,hhh=somedata8,ddd=somedata4,fff=somedata6,,
aaa=somedata1,bbb=somedata2,,,hhh=somedata8,,,ggg=somedata9,jjj=somedata11

如果您只想列印所有行中出現的欄位:

$ cat tst.awk
BEGIN {
   FS = OFS = ","
}
NR==FNR {
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       cnt[tag]++
   }
   next
}
FNR==1 {
   for (tag in cnt) {
       if ( cnt[tag] == (NR-1) ) {
           tags[++numTags] = tag
       }
   }
}
{
   delete tag2val
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       tag2val[tag] = $i
   }

   for (i=1; i<=numTags; i++) {
       tag = tags[i]
       printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
   }
}
$ awk -f tst.awk file file
hhh=somedata8,aaa=somedata1
hhh=somedata8,aaa=somedata1
hhh=somedata8,aaa=somedata1

如果欄位輸出的順序也很重要,那麼這也是一個簡單的調整,例如,要保留輸入順序,您只需在第一個塊中創建一個數組,以將遞增計數映射到每個新標籤,如下所示:

$ cat tst.awk
BEGIN {
   FS = OFS = ","
}
NR==FNR {
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       if ( !cnt[tag]++ ) {
           order[++totTags] = tag
       }
   }
   next
}
FNR==1 {
   for (i=1; i<=totTags; i++) {
       tag = order[i]
       if ( cnt[tag] == (NR-1) ) {
           tags[++numTags] = tag
       }
   }
}
{
   delete tag2val
   for (i=1; i<=NF; i++) {
       tag = $i
       sub(/=.*/,"",tag)
       tag2val[tag] = $i
   }

   for (i=1; i<=numTags; i++) {
       tag = tags[i]
       printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
   }
}
$ awk -f tst.awk file file
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

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