Linux
如何使用 AWK 將某些列從一個 CSV 文件提取到另一個文件?
我有一個非常大的 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
但是,我有兩個問題:
- 我不知道如何在 AWK 中查找幾個條件(我什至嘗試將我需要的欄位/關鍵字的名稱寫到 TXT 文件中並在 AWK 中讀取它,但我做不到)。
- 每次我嘗試列印結果列時,新的 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