導讀:Linux以其強大的命令行稱霸江湖,Shell命令是數據極客的必修兵器。探索性數據分析,在需求和數據都不太明確的環境下,使用各種命令進行一次探索與挖掘。從基礎的文件查看到簡單的統計,再到一些常用的探索性分析命令,其目的都只是爲了更好的做數據分析與挖掘而已。
01 Shell命令行
對於經常和數據打交道的人來說,數據工程師應該也是常常和Linux打交道。Linux以其強大的命令行稱霸江湖,因此,Shell命令也是數據極客的必修兵器。
利用Linux命令行的幾個命令,就可以完成一些簡單的統計分析工作,比如利用wc命令統計文件行,單詞數,字符數,利用sort排序和去重,再結合uniq可以進行詞頻統計。比如:
$ 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語句:
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命令進行測試
如:
# 解壓縮日誌
$ gzip -d a.gz
$ tar zcvf/jcvf one.tar.bz2 one
# 直接查看壓縮日誌
$ less a.gz
# 無需先解壓
另外,以z開頭的幾個命令可以簡單處理gzip壓縮文件, 如zcat:直接打印壓縮文件,還有zgrep/zfgrep/zegrep,在壓縮文件中直接查找。
# 查詢字符串,並顯示匹配行的前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使用擴展的正則,如
egrep "one|two", 匹配one或者two
grep -E -v ".jpg|.png|.gif|.css|.js" log.txt |wc -l
查找所有來自日本的ip的請求,先把所有來源ip取出來,去重,找出日本的ip,放入文件japan.ip,再使用命令:
$ cat log.gz | gzip -d | fgrep -f japan.ip > japan.log
對hive中導出的文件,替換01
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循環使用
輸出今天/昨天的日期字符串
$ date -d today +%Y%m%d
20160320
$ date -d yesterday +%Y%m%d
20160319
對unix秒的處理
# 當前的時間
$ date +%s
1458484275
$date -d @1458484275
Sun Mar 20 22:31:15 CST 2016
兩個文件a.txt, b.txt求只出現在a.txt中的數據:
# 排序兩個文件
$ 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條件判斷:
if [ -d ${base_d} ];
then mkdir -p ${base_d};
fi
while循環:
while
do
do_something;
done
for循環(用得很多):
for x in *.log.gz;
do
gzip -d ${x};
done
這幾個條件判斷與循環,也可以直接在命令行下使用,區別是多加幾個分號隔開即可。
另外,執行長時間的任務,最好直接用nohup來操作。
生成過去8天的日期序列:
$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
有目錄和文件如下:
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相同的語法。
$ 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腳本本身也是一門強大的學問了,其中各個命令還有每個命令支持的參數,值得慢慢研究。