沒想到 Shell 命令竟然還能這么玩?
點擊「閱讀原文」查看良許原創(chuàng)精品視頻。
問題描述
輸入是 json數(shù)據(jù),格式化之后的 json 數(shù)據(jù)主題結構如下所示,rows為數(shù)組,數(shù)組中元素所代表的 object 即描述了獲得北京戶口的同學的各種屬性:例如分數(shù)、排名、身份證號(后四位打碼了)、公司等等信息。
"rows":?[
{
????"id":?62981,
????"idCard":?"32092219721222****",
????"idCardSHA":?"9ef70bde894959a4e4a1d1b2b9592b470294f9e4012a8cf480319665d1a7c1c6",
????"insertTime":?1539518353000,
????"integralQualified":?1,
????"internetAnnual":?{
????????"annual":?2018,
????????"id":?43,
????????"insertTime":?1539518353000,
????????"publicityEnd":?1540224000000,
????????"publicityStart":?1539591600000,
????????"publishResultEndDate":?1541679300000,
????????"publishResultStartDate":?1539591600000,
????????"publishResultStatus":?1,
????????"score":?90.75,
????????"status":?1
????},
????"md5Code":?"54e9ff7ce0b004f7141b157f8afc66db",
????"name":?"楊效豐",
????"pxid":?1,
????"ranking":?1,
????"s1":?51,
????"s10":?0,
????"s2":?12.59,
????"s3":?15,
????"s4":?0,
????"s5":?4,
????"s6":?0,
????"s7":?20,
????"s8":?20,
????"s9":?0,
????"score":?122.59,
????"unit":?"北京利德華福電氣技術有限公司"
},
為了方便大家練習對數(shù)據(jù)進行試驗,我將文中的數(shù)據(jù)附在這里:https://www.tanglei.name/resources/use-shell-to-analysis-the-first-people-of-getting-residence-of-beijing-by-score/jifenluohu.json.gz
拿到這個文件,比如希望你用最快的方法獲得以下信息,你將會怎么做?
獲取取得戶口名額最多的top10公司 獲取取得戶口名額的人中姓氏最多的 獲取戶口名字中叫啥名最流行 獲取年齡分布 獲取取得戶口的同學戶籍地top10 生肖/星座/生日...
當然,方法有很多,比如熟悉各種編程語言的,例如?python, php, java?等等寫個簡單的腳本程序,也能比較快獲取答案。或者把相應的數(shù)據(jù)提取出來,放到 excel 中也可以。
如果你對 Shell 很熟悉,那真的是分分鐘,應該是秒秒鐘就能獲取答案。就算用 Shell 來實現(xiàn),不同的人可能也有不同的寫法,后面我就列舉其中的一種來解決這些問題。
本文不對 Shell 具體每個命令做過多的解釋,不熟悉的同學可以直接?man $cmd?或者?$cmd --help?等等查看。
問題解答
獲取取得戶口名額最多的top10公司
看看想通過積分落戶,最好是進哪些公司,哈哈。
"unit": "北京利德華福電氣技術有限公司"
先通過?grep?得到包含公司名字的一行,然后通過 ":" 分割?cut?取第2列得到公司名字,對結果進行sort排序進行去重uniq統(tǒng)計得到重復次數(shù),次時結果為重復次數(shù) 公司名,再對第一列-k 1重復數(shù)字進行按照數(shù)字排序逆序-nr?即?sort -nr -k 1,最后取結果的前10行?head -n 10。
???積分落戶??>?grep?'unit'?jifenluohu.json|?cut?-f2?-d:?|?sort?|?uniq?-c?|?sort?-nr?-k?1?|?head?-n?10
?137??"北京華為數(shù)字技術有限公司"
??73??"中央電視臺"
??57??"北京首鋼建設集團有限公司"
??55??"百度在線網(wǎng)絡技術(北京)有限公司"
??48??"聯(lián)想(北京)有限公司"
??40??"北京外企人力資源服務有限公司"
??40??"中國民生銀行股份有限公司"
??39??"國際商業(yè)機器(中國)投資有限公司"
??29??"中國國際技術智力合作有限公司"
??27??"華為技術有限公司北京研究所"
獲取取得戶口名額的人中姓氏最多的
看看想通過積分落戶,最好是姓啥,哈哈。
"name": "楊效豐",
套路跟之前差不多的,我這邊就不特別指出了。
下面shell實際上是取到這行后,將真正表示名字之前的所有字符都刪除,就只剩下名字開頭了,取行首第一個字符cut -c 1即得到姓,再按照之前的套路就能拿到了。
其實用什么sed替換冗余的字符都是多余的,因為json的格式都是良好的,可以直接通過?cut -c ??取姓這個字符即可。
也不用挨個去數(shù)到底是第幾個字符,直接 copy出來,然后?echo -n $paste | wc -c?就能數(shù)到第幾個字符了。
看結果還是姓 "張, 王" 之類的最有戲。?
#?或者?grep?'"name":'?jifenluohu.json|?sed?'s|"name":?"||g'?|?sed?'s|[[:space:]]||g'?|?cut?-c?1?|?sort?|?uniq?-c?|?sort?-nr?-k?1?|?head?-n?10
???積分落戶??>?grep?'"name":'?jifenluohu.json|?sed?'s|"name":?"||g'?|?sed?'s|?||g'?|?cut?-c?1?|?sort?|?uniq?-c?|?sort?-nr?-k?1?|?head?-n?10
?541?張
?531?王
?462?李
?376?劉
?205?陳
?193?楊
?166?趙
?132?孫
??95?郭
??95?徐
獲取戶口名字中叫啥名最流行
套路差不多,不做過多解釋了。
???積分落戶??>?grep?'"name":'?jifenluohu.json|?sed?'s|"name":?"||g'?|?sed?'s|[[:space:]]||g'?|?cut?-c?2-4?|?sort?|?uniq?-c?|?sort?-nr?-k?1?|?head?-n?10
??51?偉",
??39?靜",
??38?濤",
??36?勇",
??36?軍",
??32?敏",
??31?穎",
??30?鵬",
??28?杰",
??28?峰",
#?取名字,?必須包含2個字
???積分落戶??>?grep?'"name":'?jifenluohu.json|?sed?'s|"name":?"||g'?|?sed?'s|[[:space:]]||g'?|?cut?-c?2-3?|?sed??'/"/d'?|?sort?|?uniq?-c?|?sort?-nr?-k?1?|?head?-n?10
??19?海濤
??19?曉東
??12?志強
??11?海燕
??11?永強
??11?建華
??10?雪梅
???9?海龍
???9?麗娜
???8?洪濤
作為碼農(nóng),必須得養(yǎng)成對自己得到結果進行自測的習慣,所以如果對自己的結果不夠自信,可以正向去計算一下最終的結果。
例如可以簡單grep一下進行驗證,叫 "海濤" 的是不是19個。
???積分落戶??>?grep?'海濤'?jifenluohu.json?|?wc?-l
??????19??
獲取年齡分布
思路是截取身份證號碼中代表出生年的4位數(shù),然后拿當前年份2019減出生年得到年齡,后面的套路又一樣了。
bc?一個簡單的計算器程序,了解下?
???shell-train??>?echo?"3+2-5/5"?|?bc
4
???shell-train??>?echo?"3.141592*5-4"?|?bc
11.707960
#思路1:?`cut?-c?9-12`?獲取出生年,?拼接表達式?`2019-出生年`?得到年齡.
???積分落戶??>?grep?'"idCard":'?jifenluohu.json|?cut?-f2?-d:?|?cut?-c?9-12?|?xargs?-n1?echo?2019?-|bc?|?sort?|?uniq?-c
???3?34
??13?35
??39?36
?109?37
?162?38
?302?39
?507?40
?773?41
?799?42
?813?43
?757?44
?586?45
?507?46
?378?47
?238?48
???4?49
???9?50
???1?51
???4?52
???3?53
???2?54
???5?55
???1?56
???1?58
???1?59
???1?60
???1?61
awk?是個好東西,多練練。
#?拿到出生年后,?直接通過?awk?計算結果輸出
???積分落戶??>?grep?'"idCard":'?jifenluohu.json|?cut?-f2?-d:?|?cut?-c?9-12?|awk?'{print?2019-$1}'?|?sort?|?uniq?-c
???3?34
??13?35
??39?36
?109?37
?162?38
?302?39
?507?40
?773?41
?799?42
?813?43
?757?44
?586?45
?507?46
?378?47
?238?48
???4?49
???9?50
???1?51
???4?52
???3?53
???2?54
???5?55
???1?56
???1?58
???1?59
???1?60
???1?61???
獲取取得戶口的同學戶籍地top10
有時候,我們在寫Shell的時候,為了debug方便,可能會將一些中間結果緩存到文件中,后續(xù)以該文件為基礎進行后續(xù)的計算。
比如先拿到top10的身份證中代表的戶籍地的四位編碼,這里需要借助另外的一個表示身份證戶籍地的編碼來進行對應。
借此機會解釋下?join?這個命令。
#?身份證前4位為例,?拿到戶籍地
grep?'"idCard":'?jifenluohu.json|?cut?-f2?-d:?|?cut?-c?3-6?|?sort?|?uniq?-c?|?sort?-nr?-k?1?>topcity.code
#?城市列表
???積分落戶??>?more?city.csv
11,北京市
1101,北京市市轄區(qū)
110101,北京市東城區(qū)
110102,北京市西城區(qū)
110103,北京市崇文區(qū)
110104,北京市宣武區(qū)
110105,北京市朝陽區(qū)
#?grep?-E?'^[0-9]{4},'?city.csv?|?sed?'s|,|?|g'?>?city.code4
??shell-train??>?head?-n?2?city.code4
1101?北京市市轄區(qū)
1102?北京市市轄縣
??shell-train??>?head?-n?2?topcity.code
?197?1201
?156?1302
??shell-train??>?join
usage:?join?[-a?fileno?|?-v?fileno?]?[-e?string]?[-1?field]?[-2?field]
????????????[-o?list]?[-t?char]?file1?file2
其實,join?就類似sql中的?...inner join ...on ...,?-t?分隔符,默認為空格或tab。
#?未排序,?所以沒有將所有的導出(join需要排序)
??shell-train??>?join?-1?1?-2?2?city.code4?topcity.code
1201?天津市市轄區(qū)?197
1302?河北省唐山市?156
2301?黑龍江哈爾濱市?123
4201?湖北省武漢市?118
6101?陜西省西安市?100
6201?甘肅省蘭州市?59
6501?新疆烏魯木齊市?29
6523?新疆昌吉回族自治州?11
一定需要將結果輸出到文件,然后再進行嗎?
其實也不一定。用管道的方式?|?可以將上一個命令的輸出結果作為下一個命令的輸入,可以通過?<(command)?的方式,將command?的輸出作為一個文件輸入。
#?需要排序
??shell-train??>?join?-1?1?-2?2?city.code4?<(head?-n?10?topcity.code?|?sort?-k?2)
1201?天津市市轄區(qū)?197
1301?河北省石家莊市?114
1302?河北省唐山市?156
1324?河北省保定地區(qū)?103
1501?內(nèi)蒙古呼和浩特市?88
2101?遼寧省沈陽市?109
2201?吉林省長春市?113
2301?黑龍江哈爾濱市?123
4201?湖北省武漢市?118
6101?陜西省西安市?100
舉個例子paste用來將兩個文件按列合并在一起:
???shell-train??>?cat?paste.f1
hello,?i?am
world,?you?are
???shell-train??>?cat?paste.f2
tanglei,?wechat?is:?tangleithu
?,?hahaha
???shell-train??>?paste?paste.f1?paste.f2
hello,?i?am?tanglei,?wechat?is:?tangleithu
world,?you?are??,?hahaha
以上用paste將兩個文件合并在一起了,實際上通過?<(cmd)的方式,可以不借助外部文件也能做到。
方法如下:
???shell-train??>?paste?<(echo?"hello,?i?am?\nworld,?you?are")?<(echo?"tanglei,?wechat?is:?tangleithu\n?,?hahaha")
hello,?i?am??tanglei,?wechat?is:?tangleithu
world,?you?are??,?hahaha
其他的任務交給你了
剩下的問題,要不你動手試試?
比如看看生日最多的,再試試獲取 生肖/星座 最多的top10。
套路是一樣的,這里就不重復多講了。
彩蛋
最后,推薦一個shell處理json的工具——jq工具,可以減少很多過濾命令,讓操作更加簡潔、高效。

推薦閱讀:
優(yōu)麒麟20.10終極預告,界面比較養(yǎng)眼
吳恩達給 74 歲老父親發(fā)證了!8 年完成 146 門課程!
5T技術資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復「1024」,即可免費獲取!!


