Text-Processing
通過 awk 合併具有 N 個公共列的多個文件,如果任何文件沒有公共鍵,則希望將列值替換為 0
我想基於公共列合併多個文件,並希望在任何文件沒有該公共列時添加 0。例如見下圖:
a1.txt
111,222,444,5.5 121,321,555,1.2
a2.txt
111,222,444,7.8 333,321,555,4.5 311,555,222,1.1
a3.txt
333,321,555,9.1 311,555,222,8.8 444,666,777,2.5
匹配應該與第一 3 列的組合。
輸出應如下所示:
111,222,444,5.5,7.8,0 121,321,555,1.2,0,0 333,321,555,0,4.5,9.1 311,555,222,0,1.1,8,8 444,666,777,0,0,2.5
3 個輸入文件中第 4 列的值不同,我想按順序排列。像 a1.txt 值應該是輸出文件中的第 4 列。a2.txt 的值應該在輸出文件的第 5 列,a3.txt 的值應該在輸出文件的第 6 列。我在下面嘗試過,但沒有給我預期的結果。
awk '{ a[$1 FS $2 FS $3 FS] = a[$1 FS $2 FS $3 FS] ( a[$1 FS $2 FS $3 FS] == "" ? "" : FS) $4 } END{ for (i in a){print i,a[i,0],a[i]} }' FS="," a1.txt a2.txt a3.txt
這樣我想對 4 個或 5 個或 6 個輸入文件執行相同的操作。有人可以幫我嗎?
將 GNU awk 用於數組和 ARGIND 數組:
$ cat tst.awk BEGIN { FS=OFS=SUBSEP="," } { vals[$1,$2,$3][ARGIND] = $NF } END { for ( key in vals ) { printf "%s", key for ( i=1; i<=ARGIND; i++ ) { printf "%s%g", OFS, vals[key][i] } print "" } }
$ awk -f tst.awk *.txt 111,222,444,5.5,7.8,0 311,555,222,0,1.1,8.8 333,321,555,0,4.5,9.1 444,666,777,0,0,2.5 121,321,555,1.2,0,0
如果輸出行的順序很重要,這是一個簡單的調整。
使用任何 awk 並在輸出中保留記錄的順序:
awk 'BEGIN{ SUBSEP=OFS=FS="," } FNR==1 && !reProccss{ fileNr++ } !reProccss{ keys[$1, $2, $3, fileNr]=$4; next } reProccss{ key=($1 OFS $2 OFS $3); recNr++ for(i=1; i<=fileNr; i++) if(seen[key]++<fileNr){ join[key]= join[key] OFS ((key, i) in keys ?keys[key, i]:"0") data[recNr]= key join[key] } } END{ for(rec=1; rec<=recNr; rec++) if(data[rec]!="") print data[rec] }' a[1-3].txt reProccss=1 a[1-3].txt
或者使用 with
join
+shell 將多個列作為鍵轉換為單個鍵,然後我們使用join
類似於按第一列合併多個文件的命令(因為連接僅適用於單列作為鍵)來產生我們想要的輸出。因此,我們通過
-
在前兩個文件中使用特定字元(輸入文件中不應存在的字元)將它們分隔並輸出到臨時文件,將多個鍵列轉換為一個*joined.tmp
*:join -t, -a1 -a2 -e 0 -o auto \ <(<a1.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') \ <(<a2.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') > joined.tmp
然後我們使用一個shell循環來處理文件的其余*
joined.tmp
*文件(它會更新每次執行以加入第二個下一個文件);我們也跳過了我們已經處理過的前兩個文件。for file in ./a*.txt; do [ "$file" = "./a1.txt" -o "$file" = "./a2.txt" ] && continue join -t, -a1 -a2 -e 0 -o auto \ joined.tmp <(sort "$file" |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') >joined.tmp.1 mv joined.tmp.1 joined.tmp done
最後改回添加
-
到他們原來的字元,
。sed 's/-/,/g' joined.tmp > joined-final.csv
join
由於需要對輸入文件進行排序,因此輸出中的記錄順序將被更改:$ cat joined-final.csv 111,222,444,5.5,7.8,0 121,321,555,1.2,0,0 311,555,222,0,1.1,8.8 333,321,555,0,4.5,9.1 444,666,777,0,0,2.5