awk實現類sql的join操作

awk、grep和sed被稱爲linux三劍客,事實上grep和awk我在日常工作中也常用到(sed用的比較少),可能有些人對awk瞭解比較少,我先大概介紹下。 很多人以爲awk只是一個文本處理工具,實際上他們也是這麼用的。但事實上它其實是一門語言,擁有數學運算符、進程控制語句,甚至針對於文本處理封裝了很多內置變量和函數,這造就了它強大的文本處理能力。 如果grep只能做到數據的篩選,那awk還能做到數據的處理、分析甚至生成報表,畢竟它是一門完整的編程語言。

因爲這篇文章不是awk的入門教程,如果想入門的話我推薦阮一峯老師的awk 入門教程 和 左耳朵耗子的AWK 簡明教程

回到我今天的正題,今天我給大家看個我常使用awk的場景。因爲做後端開發,經常在做數據分析的時候會有這樣的問題,1.面對幾十萬條的數據,需要篩選出幾百幾千個特定key的數據。2.對這幾百萬條數據,針對其中的id字段補齊其他字段。

這時候可能有精通excel的同學跳出來說 “就這,so easy,vlookup就搞定!” ,事實上,excel確實可以解決問題,但有點重,甚至有些時候我們在服務器上還用不了excel。還有啥其他方法?針對我說的這倆場景,其實仔細想一下,是不是sql中倆表join就能解決問題。實際上你並不需要真正把文件灌到數據庫裏,只需要用awk一條命令就能解決。

實例

我們把問題具像下,假設有兩個文件,score.txt存着學號+成績的數據,另外一個name.txt存着學號+姓名的數據,你現在想知道每個人都烤了多少分。

score.txt

id score 
1 87
2 67
3 68
4 75
5 90
6 100
7 0

name.txt

id name 
1 張三
2 李四
3 王五
4 趙二
5 劉能
6 熊大

你想得到一份包含學號 姓名和成績的數據,就像下面這樣。

id score name 
1 87 張三
2 67 李四
3 68 王五
4 75 趙二
5 90 劉能
6 100 熊大
7 0

用awk生成這樣的數據有多簡單?只需要一行代碼,你可以保存name.txt和score.txt,然後執行下面命令嘗試下。

awk 'ARGV[1]==FILENAME {map[$1]=$2} ARGV[2]==FILENAME {print $0, map[$1]}' name.txt score.txt  

簡簡單單就實現了name.txt和score.txt在id之上的right join。

解釋下上面的代碼,ARGV和FILENAME是awk內置的變量,ARGV裏存放在awk所接受的參數列表,像上面 ARGV[1]就是"name.txt",ARGV[2]就是"score.txt"。awk是面向行的,所以針對每一行數據都會執行ARGV[1]==FILENAME {map[$1]=$2} ARGV[2]==FILENAME {print $0, map[$1]},每一行數據都會屬於某個文件,FILENAME標識出當前行所屬的文件名,像在括號{}前的ARGV[1]==FILENAME 你可以看做是其他語法中的條件判斷,你可以認爲它就是省略了if,但和if的功能是一致的。

所以上述代碼的含義就是 如果當前行是輸入name.txt的,就把學號和姓名存在map裏(awk裏的變量不用預先聲明)。 如果當前行是屬於score.txt,就從map裏把姓名找出來,然後把數據輸出。

結語

awk 'ARGV[1]==FILENAME {map[$1]=$2} ARGV[2]==FILENAME {print $0, map[$1]}' name.txt score.txt  

針對於不同的數據,只需要調整下$後面的具體值,可以實現用不同的列作爲key來做join,其實這裏並不比sql的join複雜,但在linux服務器上卻很方便。上網中我只是實現了right join,如果在print $0, map[$1]前加上if (length(map[$1]) > 0) 就可以實現inner join。left join的話也只需要把文件名換一下。

知道了這些,awk實現多文件的交集、差集等操作都不在話下。另外不要忘記了awk其實也是一門編程語言,所以它也可以實現很多很複雜的邏輯,你可以把代碼在某個文件裏然後用-f參數調起,比如我之前老師用awk做一些簡單的統計工作,比如計算均值、總和…… awk簡直堪稱後端工程師提效利器

本文來自https://blog.csdn.net/xindoo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章