1. 程式人生 > >使用awk對某列去重並且可保留其他列

使用awk對某列去重並且可保留其他列

同事說需要統計日誌中按url的path去重之後的結果,結果中要保留引數。相同url不同引數的,只保留第一行。

理論上各種命令都是流式處理,一行一行的處理。每道命令都相當於一個過濾器。比如你要按某列去重,則事先把資料cut到只剩你需要去重的這一列。對url中的path做sort+uniq。

但是這樣會導致引數都沒了。所以不能用這個。

最後成功的命令如下:

cut -f 7 -d " " access.2018-01-11.log | sed 's/?/ /' | awk '!a[$1]++{print}'

解釋一下

a[$1]是$1的hash值,在這裡是被作為變數名了,初始值為0,++表示在本行執行完畢之後+1

那麼awk走到第一行時,該變數剛剛初始化則是0,取反後為1,即為true,所以第一行會被print

第二行如果跟第一行的a[$1]一樣,則該變數的值已經在上一行被+1了,所以值不是0了,而是1。則取反之後值為0,即為false,則本行不print

第三行如果還一樣,則變數值為2,由於在C中,只要不是0就是真,所以第三行變數值取反為false,不print

---------------------------------------------------

後來同事又提了更過分的要求,想要把以上命令再結合ssh,從跳板機就直接遠端獲取到處理好的資料並寫入檔案,這樣不用一個個機器去登。

一開始是這樣寫的,會報錯。

ssh xxxxx.beta.cn0 "zgrep -e 'HTTP/1.0\" \(2\|3\)0' /xxxxxx/logs/access.2018-01-08.log | cut -f 7 -d ' '| sed 's/?/ /' | awk '!a[$1]++{print}' " >> valid_access.log 報錯如下: -bash: !a[$1]++: event not found
最後換了個思路,把awk放到本地執行,就解決了:
ssh xxxxx.beta.cn0 "zgrep -e 'HTTP/1.0\" \(2\|3\)0' /xxxxxx/logs/access.2018-01-08.log | cut -f 7 -d ' '| sed 's/?/ /' "
 | awk '!a[$1]++{print}' >> valid_access.log