導讀:Linux以其強大的命令列稱霸江湖,Shell命令是資料極客的必修兵器。探索性資料分析,在需求和資料都不太明確的環境下,使用各種命令進行一次探索與挖掘。從基礎的檔案檢視到簡單的統計,再到一些常用的探索性分析命令,其目的都只是為了更好的做資料分析與挖掘而已。
01 Shell命令列
對於經常和資料打交道的人來說,資料工程師應該也是常常和Linux打交道。Linux以其強大的命令列稱霸江湖,因此,Shell命令也是資料極客的必修兵器。
利用Linux命令列的幾個命令,就可以完成一些簡單的統計分析工作,比如利用wc命令統計檔案行,單詞數,字元數,利用sort排序和去重,再結合uniq可以進行詞頻統計。比如:
1 2 3 4 5 6 7 8 9 10 11 |
$ cat file.txt yunjie yunjie-talk yunjie-yun yunjie yunjie-shuo $ sort file.txt | uniq -c | sort -nr | head -5 2 yunjie 1 yunjie-shuo 1 yunjie-talk 1 yunjie-yun |
先用cat命令,瞭解一下檔案的大概格式與內容,發現每行為一個單詞。現在需要統計這些單詞出現的頻率,以及顯示出現次數最多的5個單詞。
先對檔案進行排序,這樣相同的單詞在緊挨著的行,再後uniq -c 命令,統計不同的單詞及各個單詞出現的次數。這樣得到的結果就是次數後面緊接著單詞,然後使用sort -nr對次數進行排序,並逆序顯示,最後head命令顯示結果的前5行。
非常簡單的一種方式,讀取檔案,排序,統計,再對統計結果進行逆序,最後只顯示前幾個結果。
類似於sql語句:
1 2 3 4 5 |
select word,count(1) cnt from file group by word order by cnt desc limit 5; |
如果對sql語句熟悉的話,上面的形式應該更容易理解。雖然實現的思想和方式非常簡單,但在實際的探索性資料分析中使用卻非常頻繁。
02 探索性分析
比如在日誌分析中,有時並沒有非常明確的目標,或者即使有明確的目標,通常各種資料也並沒有明確的定義。比如,別人丟給你一個壓縮檔案,說想分析一下里面有哪些是異常的訪問請求。任務描述就是這樣,沒有更明確的了。
拿到日誌檔案和這樣的分析任務,就需要進行各種可能的探索性分析。先看一下檔案的格式,是否壓縮過,使用gzip壓縮還是tar壓縮。解壓後,需要先大概瞭解一下,檔案是什麼樣的格式。對於網路請求的日誌檔案,是一行一個請求和響應,還是多行一個請求和響應。檢視檔案有多少行,檢視檔案佔用空間大小。如果解壓後包含多個目錄或者檔案,同樣的一個命令,更能發揮強大效果。此時,通常需要如下命令:
gzip/tar:壓縮/解壓
cat/zcat:檔案檢視
less/more:檔案檢視,支援gz壓縮格式直接檢視
head/tail:檢視檔案前/後10行
wc:統計行數、單詞數、字元數
du -h -c -s:檢視空間佔用
上面有一個比較有趣的命令組,less和more,這兩個都可以分頁檢視檔案。最開始有的more命令,好像是當時more不支援向後翻頁。於是一幫人就在此基礎上進行了改進,直接叫less,和more同樣的功能只是更強大些。因此,也發展出了“less is more”的哲學,“少即是多”,而且少比多更好。這種思想,在產品設計與程式碼優化中都有體現。
瞭解檔案的大概資訊後,可能需要提取一行中某個欄位的內容,或者需要搜尋某些行出來,或者需要對某些字元或者行進行一定的修改操作,或者需要在眾多的目錄和檔案中找出某此天的日誌(甚至找到後需要對這些天的日誌進行統一處理),此時下面這些命令可以幫你:
awk:命令列下的資料庫操作工具
join/cut/paste:關聯檔案/切分欄位/合併檔案
fgrep/grep/egrep:全域性正規表示式查詢
find:查詢檔案,並且對查詢結果批量化執行任務
sed:流編輯器,批量修改、替換檔案
split:對大檔案進行切分處理,按多少行一個檔案,或者多少位元組一個檔案
rename:批量重新命名(Ubuntu上帶的perl指令碼,其它系統需要安裝),使用-n命令進行測試
如:
1 2 3 4 5 |
# 解壓縮日誌 $ gzip -d a.gz $ tar zcvf/jcvf one.tar.bz2 one # 直接檢視壓縮日誌 $ less a.gz # 無需先解壓 |
另外,以z開頭的幾個命令可以簡單處理gzip壓縮檔案, 如zcat:直接列印壓縮檔案,還有zgrep/zfgrep/zegrep,在壓縮檔案中直接查詢。
1 2 3 4 |
# 查詢字串,並顯示匹配行的前3行和後3行內容 fgrep 'yunjie-talk' -A 3 -B 3 log.txt # 在當前目前(及子目錄)下,所有的log檔案中搜尋字串hacked by: $ find . -name "*.log" | xargs fgrep "hacked by" |
fgrep, grep, egrep的一些區別:
fgrep按字串的本來意思完全匹配,裡面的正則元字元當成普通字元解析, 如: fgrep “1.2.3.4” 則只匹配ip地址: 1.2.3.4, 其中的.不會匹配任意字元。fgrep當然會比grep快多了。寫起來又簡單,不用轉義。
grep只使用普通的一些正則,egrep或者grep -E使用擴充套件的正則,如
12 egrep "one|two", 匹配one或者twogrep -E -v ".jpg|.png|.gif|.css|.js" log.txt |wc -l
查詢所有來自日本的ip的請求,先把所有來源ip取出來,去重,找出日本的ip,放入檔案japan.ip,再使用命令:
1 |
$ cat log.gz | gzip -d | fgrep -f japan.ip > japan.log |
對hive中匯出的檔案,替換01
1 |
cat 0000* | sed 's/x1/ /g' > log.txt |
03 其它常用命令
如果檔案編碼是從windows上傳過來的gb2312編碼,需要處理成utf8的編碼,或者某個日誌被黑客後來修改過了,需要和原來的備份資料進行對比,這些工作都是需要資料工程師自己能熟悉的掌握。
假如日誌檔案是最近一年的請求日誌,那麼可能是按天或者按小時進行單獨存放,此時如果只需要提取某些天(比如週末)的資料,很可能需要處理時間。
因此,下面的一些命令或者工具就很有用了:
date:命令列時間操作函式
sort/uniq:排序、去重、統計
comm:對兩個排序檔案進行按行比較(共同行、只出現在左邊檔案、只出現在右邊檔案)
diff:逐字元比較檔案的異同,配合cdiff,類似於github的顯示效果
curl/w3m/httpie:命令列下進行網路請求
iconv:檔案編碼轉換,如:iconv -f GB2312 -t UTF8 1.csv > 2.csv
seq:產生連續的序列,配合for迴圈使用
輸出今天/昨天的日期字串
1 2 3 4 |
$ date -d today +%Y%m%d 20160320 $ date -d yesterday +%Y%m%d 20160319 |
對unix秒的處理
1 2 3 4 5 |
# 當前的時間 $ date +%s 1458484275 $date -d @1458484275 Sun Mar 20 22:31:15 CST 2016 |
兩個檔案a.txt, b.txt求只出現在a.txt中的資料:
1 2 3 4 5 |
# 排序兩個檔案 $ sort a.txt > a.txt.sort $ sort b.txt > b.txt.sort # 求只出現在c.sh中的內容 $ comm -2 -3 a.txt.sort b.txt.sort |
04 批量操作
對上面的檔案進行了一番探索分析後,可能已經有一定的線索或者眉目了,需要更進一步的處理大量的檔案或者欄位了。此時的步驟也許是一個消耗時間的過程,也許是一個需要看緣分的過程。總之,可能需要綜合上面的一些命令,並且對大量的日誌進行處理。
這也是體現Shell更強大的一面——批量化的功能了。命令比圖形介面的最大優勢就是,只需熟悉了,就很容易實現批量化操作,將這些批量化的命令組合成一個檔案,於是便產生了指令碼。
批量化命令或者指令碼,熟悉幾個常用的流程控制,就能發揮出強大的效能:
if條件判斷:
1 2 3 |
if [ -d ${base_d} ]; then mkdir -p ${base_d}; fi |
while迴圈:
1 2 3 4 |
while do do_something; done |
for迴圈(用得很多):
1 2 3 4 |
for x in *.log.gz; do gzip -d ${x}; done |
這幾個條件判斷與迴圈,也可以直接在命令列下使用,區別是多加幾個分號隔開即可。
另外,執行長時間的任務,最好直接用nohup來操作。
生成過去8天的日期序列:
1 2 3 4 5 6 7 8 9 |
$for num in `seq 8 -1 1`;do dd=`date --date="${num} day ago" +%Y%m%d`;echo ${dd};done 20160312 20160313 20160314 20160315 20160316 20160317 20160318 20160319 |
有目錄和檔案如下:
1 2 3 4 5 6 7 |
20160320 目錄 10.1.0.1_20160320*.log.gz 目錄 201603200000.log.gz 檔案 201603200010.log.gz 檔案 10.1.0.2_20160320*.log.gz 目錄 201603200000.log.gz 檔案 201603200010.log.gz 檔案 |
需求:去掉目錄中的*.log.gz,這樣很容易讓人誤解為檔案。 rename -n為測試,rename使用和sed相同的語法。
1 |
$ for d in 201603??;do echo ${d}; cd ${d}; rename -n 's/*.log.gz//' *.log.gz ; cd ..;done |
測試完成後,使用rename不加-n為真正執行重新命名操作。
05 結尾
這兒只是簡單列舉了一些資料分析或者資料處理相關的命令,只能算是Linux的Shell那博大精深的命令中的冰山一角。
但如果能把這些相關的命令融會貫通,並且能實際使用的話,也算是在資料極客之路上多走了一步。
從基礎的檔案檢視到簡單的統計,再到一些常用的探索性分析命令,其目的都只是為了更好的做資料分析與挖掘而已。能綜合這些命令,並組合起來使用,將命令存放到檔案,即產生了Shell指令碼。Shell指令碼本身也是一門強大的學問了,其中各個命令還有每個命令支援的引數,值得慢慢研究。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式