摘要:Linux以其強(qiáng)大的命令行稱霸江湖,Shell命令是數(shù)據(jù)極客的必修兵器。探索性數(shù)據(jù)分析,在需求和數(shù)據(jù)都不太明確的環(huán)境下,使用各種命令進(jìn)行一次探索與挖掘。從基礎(chǔ)的文件查看到簡單的統(tǒng)計(jì),再到一些常用的探索性分析命令,其目的都只是為了更好的做數(shù)據(jù)分析與挖掘而已。
01 Shell命令行
對于經(jīng)常和數(shù)據(jù)打交道的人來說,數(shù)據(jù)工程師應(yīng)該也是常常和Linux打交道。Linux以其強(qiáng)大的命令行稱霸江湖,因此,Shell命令也是數(shù)據(jù)極客的必修兵器。
利用Linux命令行的幾個(gè)命令,就可以完成一些簡單的統(tǒng)計(jì)分析工作,比如
利用wc命令統(tǒng)計(jì)文件行,單詞數(shù),字符數(shù),利用sort排序和去重,再結(jié)合uniq可以進(jìn)行詞頻統(tǒng)計(jì)。比如:
$ 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命令,了解一下文件的大概格式與內(nèi)容,發(fā)現(xiàn)每行為一個(gè)單詞。現(xiàn)在需要統(tǒng)計(jì)這些單詞出現(xiàn)的頻率,以及顯示出現(xiàn)次數(shù)最多的5個(gè)單詞。
先對文件進(jìn)行排序,這樣相同的單詞在緊挨著的行,再后uniq -c 命令,統(tǒng)計(jì)不同的單詞及各個(gè)單詞出現(xiàn)的次數(shù)。這樣得到的結(jié)果就是次數(shù)后面緊接著單詞,然后使用sort -nr對次數(shù)進(jìn)行排序,并逆序顯示,最后head命令顯示結(jié)果的前5行。
非常簡單的一種方式,讀取文件,排序,統(tǒng)計(jì),再對統(tǒng)計(jì)結(jié)果進(jìn)行逆序,最后只顯示前幾個(gè)結(jié)果。
類似于sql語句:
select word,count(1) cnt
from file
group by word
order by cnt desc
limit 5;
如果對sql語句熟悉的話,上面的形式應(yīng)該更容易理解。雖然實(shí)現(xiàn)的思想和方式非常簡單,但在實(shí)際的探索性數(shù)據(jù)分析中使用卻非常頻繁。
02 探索性分析
比如在日志分析中,有時(shí)并沒有非常明確的目標(biāo),或者即使有明確的目標(biāo),通常各種數(shù)據(jù)也并沒有明確的定義。比如,別人丟給你一個(gè)壓縮文件,說想分析一下里面有哪些是異常的訪問請求。任務(wù)描述就是這樣,沒有更明確的了。
拿到日志文件和這樣的分析任務(wù),就需要進(jìn)行各種可能的探索性分析。先看一下文件的格式,是否壓縮過,使用gzip壓縮還是tar壓縮。解壓后,需要先大概了解一下,文件是什么樣的格式。對于網(wǎng)絡(luò)請求的日志文件,是一行一個(gè)請求和響應(yīng),還是多行一個(gè)請求和響應(yīng)。查看文件有多少行,查看文件占用空間大小。如果解壓后包含多個(gè)目錄或者文件,同樣的一個(gè)命令,更能發(fā)揮強(qiáng)大效果。此時(shí),通常需要如下命令:
gzip/tar:壓縮/解壓
cat/zcat:文件查看
less/more:文件查看,支持gz壓縮格式直接查看
head/tail:查看文件前/后10行
wc:統(tǒng)計(jì)行數(shù)、單詞數(shù)、字符數(shù)
du -h -c -s:查看空間占用
上面有一個(gè)比較有趣的命令組,less和more,這兩個(gè)都可以分頁查看文件。最開始有的more命令,好像是當(dāng)時(shí)more不支持向后翻頁。于是一幫人就在此基礎(chǔ)上進(jìn)行了改進(jìn),直接叫l(wèi)ess,和more同樣的功能只是更強(qiáng)大些。因此,也發(fā)展出了“l(fā)ess is more"的哲學(xué),“少即是多”,而且少比多更好。這種思想,在產(chǎn)品設(shè)計(jì)與代碼優(yōu)化中都有體現(xiàn)。
了解文件的大概信息后,可能需要提取一行中某個(gè)字段的內(nèi)容,或者需要搜索某些行出來,或者需要對某些字符或者行進(jìn)行一定的修改操作,或者需要在眾多的目錄和文件中找出某此天的日志(甚至找到后需要對這些天的日志進(jìn)行統(tǒng)一處理),此時(shí)下面這些命令可以幫你:
awk:命令行下的數(shù)據(jù)庫操作工具
join/cut/paste:關(guān)聯(lián)文件/切分字段/合并文件
fgrep/grep/egrep:全局正則表達(dá)式查找
find:查找文件,并且對查找結(jié)果批量化執(zhí)行任務(wù)
sed:流編輯器,批量修改、替換文件
split:對大文件進(jìn)行切分處理,按多少行一個(gè)文件,或者多少字節(jié)一個(gè)文件
rename:批量重命名(Ubuntu上帶的perl腳本,其它系統(tǒng)需要安裝),使用-n命令進(jìn)行測試
如:
# 解壓縮日志
$ gzip -d a.gz
$ tar zcvf/jcvf one.tar.bz2 one
# 直接查看壓縮日志
$ less a.gz # 無需先解壓
另外,以z開頭的幾個(gè)命令可以簡單處理gzip壓縮文件, 如zcat:直接打印壓縮文件,還有zgrep/zfgrep/zegrep,在壓縮文件中直接查找。
# 查詢字符串,并顯示匹配行的前3行和后3行內(nèi)容
fgrep 'yunjie-talk' -A 3 -B 3 log.txt
# 在當(dāng)前目前(及子目錄)下,所有的log文件中搜索字符串hacked by:
$ find . -name "*.log" | xargs fgrep "hacked by"
fgrep, grep, egrep的一些區(qū)別:
fgrep按字符串的本來意思完全匹配,里面的正則元字符當(dāng)成普通字符解析, 如: fgrep "1.2.3.4" 則只匹配ip地址: 1.2.3.4, 其中的.不會匹配任意字符。fgrep當(dāng)然會比grep快多了。寫起來又簡單,不用轉(zhuǎn)義。
grep只使用普通的一些正則,egrep或者grep -E使用擴(kuò)展的正則,如
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中導(dǎo)出的文件,替換\001
cat 0000* | sed 's/\x1/ /g' > log.txt
03 其它常用命令
如果文件編碼是從windows上傳過來的gb2312編碼,需要處理成utf8的編碼,或者某個(gè)日志被黑客后來修改過了,需要和原來的備份數(shù)據(jù)進(jìn)行對比,這些工作都是需要數(shù)據(jù)工程師自己能熟悉的掌握。
假如日志文件是最近一年的請求日志,那么可能是按天或者按小時(shí)進(jìn)行單獨(dú)存放,此時(shí)如果只需要提取某些天(比如周末)的數(shù)據(jù),很可能需要處理時(shí)間。
因此,下面的一些命令或者工具就很有用了:
date:命令行時(shí)間操作函數(shù)
sort/uniq:排序、去重、統(tǒng)計(jì)
comm:對兩個(gè)排序文件進(jìn)行按行比較(共同行、只出現(xiàn)在左邊文件、只出現(xiàn)在右邊文件)
diff:逐字符比較文件的異同,配合cdiff,類似于github的顯示效果
curl/w3m/httpie:命令行下進(jìn)行網(wǎng)絡(luò)請求
iconv:文件編碼轉(zhuǎn)換,如:iconv -f GB2312 -t UTF8 1.csv > 2.csv
seq:產(chǎn)生連續(xù)的序列,配合for循環(huán)使用
輸出今天/昨天的日期字符串
$ date -d today +%Y%m%d
20160320
$ date -d yesterday +%Y%m%d
20160319
對unix秒的處理
# 當(dāng)前的時(shí)間
$ date +%s
1458484275
$date -d @1458484275
Sun Mar 20 22:31:15 CST 2016
兩個(gè)文件a.txt, b.txt求只出現(xiàn)在a.txt中的數(shù)據(jù):
# 排序兩個(gè)文件
$ sort a.txt > a.txt.sort
$ sort b.txt > b.txt.sort
# 求只出現(xiàn)在c.sh中的內(nèi)容
$ comm -2 -3 a.txt.sort b.txt.sort
04 批量操作
對上面的文件進(jìn)行了一番探索分析后,可能已經(jīng)有一定的線索或者眉目了,需要更進(jìn)一步的處理大量的文件或者字段了。此時(shí)的步驟也許是一個(gè)消耗時(shí)間的過程,也許是一個(gè)需要看緣分的過程。總之,可能需要綜合上面的一些命令,并且對大量的日志進(jìn)行處理。
這也是體現(xiàn)Shell更強(qiáng)大的一面------批量化的功能了。命令比圖形界面的最大優(yōu)勢就是,只需熟悉了,就很容易實(shí)現(xiàn)批量化操作,將這些批量化的命令組合成一個(gè)文件,于是便產(chǎn)生了腳本。
批量化命令或者腳本,熟悉幾個(gè)常用的流程控制,就能發(fā)揮出強(qiáng)大的性能:
if條件判斷:
if [ -d ${base_d} ];
then mkdir -p ${base_d};
fi
while循環(huán):
while
do
do_something;
done < file.list
for循環(huán)(用得很多):
for x in *.log.gz;
do
gzip -d ${x};
done
這幾個(gè)條件判斷與循環(huán),也可以直接在命令行下使用,區(qū)別是多加幾個(gè)分號隔開即可。
另外,執(zhí)行長時(shí)間的任務(wù),最好直接用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 $m91z6oc; cd $dslz1db; rename -n 's/\*\.log\.gz//' *.log.gz ; cd ..;done
測試完成后,使用rename不加-n為真正執(zhí)行重命名操作。
05 結(jié)尾
這兒只是簡單列舉了一些數(shù)據(jù)分析或者數(shù)據(jù)處理相關(guān)的命令,只能算是Linux的Shell那博大精深的命令中的冰山一角。
但如果能把這些相關(guān)的命令融會貫通,并且能實(shí)際使用的話,也算是在數(shù)據(jù)極客之路上多走了一步。
從基礎(chǔ)的文件查看到簡單的統(tǒng)計(jì),再到一些常用的探索性分析命令,其目的都只是為了更好的做數(shù)據(jù)分析與挖掘而已。能綜合這些命令,并組合起來使用,將命令存放到文件,即產(chǎn)生了Shell腳本。Shell腳本本身也是一門強(qiáng)大的學(xué)問了,其中各個(gè)命令還有每個(gè)命令支持的參數(shù),值得慢慢研究。