因爲公司的問題排查需求,須要將公網服務器上佔用帶寬流量較高的進程或者端口進行記錄和保存,以便之後查詢問題時能夠進行覈實。python
這個問題糾結了一、2天,使用python應該是能夠知足需求,奈何本菜鳥的python實在是拿不出手,因此只能依靠Linux成品的小工具與Shell來實現了。數組
查找了網上介紹的iftop與nethogs兩個工具比較符合要求,可是這兩個工具都沒法完美的將內容記錄下來,若是使用重定向的方式記錄內容,那麼記錄下來的東西並非文本格式,使用cat查看是沒問題的,可是想要進行編輯或者過濾,就不可行了,有興趣的能夠本身試試看。bash
後來同事發現了iftop的新版本,iftop-1.0pre3(http://freecode.com/projects/iftop),可能須要×××才能夠訪問,而且安裝iftop-1.0pre3須要安裝一些依賴,yum與apt-get都沒有辦法獲取到,只能本身去搜索編譯了。 服務器
CentOS Redhat 系統安裝方法(以CentOS 6.x爲例):ide
# wget此步是設置yum源爲阿里雲,此步可選。 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo yum install -y libpcap-devel yum install -y ncurses-devel wget -q http://www.ex-parrot.com/~pdw/iftop/download/iftop-1.0pre3.tar.gz tar xf iftop-1.0pre3.tar.gz cd iftop-1.0pre3 ./configure make && make install
Ubuntu Debian 系統安裝方法(以Debian 7.x爲例):工具
# 編輯文件/etc/apt/sources.list(須要sudo),將下面4行內容粘貼到此文件首部。此步是設置apt-get源爲阿里雲,此步可選。 deb http://mirrors.aliyun.com/debian/ wheezy main non-free contrib deb http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ wheezy main non-free contrib deb-src http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contrib yes | apt-get install libpcap-dev yes | apt-get install libncurses5-dev wget -q wget http://www.ex-parrot.com/~pdw/iftop/download/iftop-1.0pre3.tar.gz tar xf iftop-1.0pre3.tar.gz cd iftop-1.0pre3 ./configure make && make install
若是是CentOS、Redhat系統等,執行腳本用sh和bash都同樣,可是若是是debian系統,必須使用bash執行腳本,用sh會有問題。優化
安裝好iftop-1.0pre3以後,使用-t參數便可將內容打印到屏幕,此時重定向就能夠正常的輸出內容。
阿里雲
建議,腳本中不要有中文,我寫上中文註釋純粹是怕本身忘了…… 用的時候刪掉!
es5
#!/bin/bash # 按照終端顯示寬度打印一行短橫線 line() { cols=`tput cols` for k in `seq 1 ${cols}` do echo -n "-" done } define() { # 指定工做目錄 work_path="/tmp/net_status/" # 獲取當前用戶名 USER=`whoami` # 獲取當前日期和時間 timestamp=`date +%F_%T` # 指定工做目錄下以日期爲名字的目錄 days="${work_path}`date +%F`" iftop_info="${work_path}iftop_info" tidy_info="${work_path}tidy_info" # 指定工做目錄下以日期爲名字的目錄下,以當前的小時爲名的文件,沒有擴展名。 net_status="${days}/`date +%H`" net1="${work_path}net1" net2="${work_path}net2" net3="${work_path}net3" net4="${work_path}net4" net5="${work_path}net5" net6="${work_path}net6" # 設置iftop收集的默認鏈接初始條目數爲100。 top_num=100 # 用ip a命令顯示出本地網卡上全部IP地址,過濾出不是內網地址和本地迴環地址的IP。 ipaddr=`ip a | grep "inet" | awk -F '[\/| ]+' '{print $3}' | egrep -v "^192.|^10.|^172.|^127."` if [ ! -z $1 ];then top_num="$1" fi } tidy() { # 使用iftop命令收集eth0網卡鏈接信息,條目數默認100,能夠輸入腳本傳參1控制條目數,收集10秒信息,以10秒內的帶寬平均數排序,將收集到的內容重定向給指定文件。 sudo /usr/local/sbin/iftop -L ${top_num} -s 10 -o 10s -N -P -n -t > ${iftop_info} # 給收集iftop信息的文件受權666,對以公共權限目錄做爲工做目錄的狀況來講沒有意義。 sudo chmod 666 ${iftop_info} # iftop收集的文件,首3行和尾9行用不到,經過wc -l計數來過濾掉,重定向給指定文件保存。 line_num=`cat ${iftop_info}|wc -l` line_head=`echo $((${line_num}-9))` line_tail=`echo $((${line_head}-3))` cat ${iftop_info} | head -${line_head} | tail -${line_tail} > ${tidy_info} # 收集到eth0網卡全局帶寬流量。 total_rate=`cat ${iftop_info} | grep "Total send and receive rate:" | awk '{print $7}'` } check() { # 若是工做目錄中已日期爲名字的目錄不存在,用root權限建立此目錄,並將屬主設置爲當前執行腳本用戶。 if [ ! -d ${days} ];then sudo mkdir -p $days sudo chown -R ${USER} ${days} fi } output1() { # 設置計數器m和n,賦予初始值。 m=-1 n=0 # 開始for循環,循環次數爲iftop收集條目數。收集iftop統計出的鏈接的源地址和端口、目的地址和端口、發包帶寬佔用、回包帶寬佔用,將這些信息追加劇定向到指定文件中保存。 for i in `seq 1 ${top_num}` do m=$(($m+2)) n=$(($n+2)) net_sou=`cat $tidy_info | sed -n "${m},${n}p" | awk 'NR==1{print $2}'` net_des=`cat $tidy_info | sed -n "${m},${n}p" | awk 'NR==2{print $1}'` rate_sou=`cat $tidy_info | sed -n "${m},${n}p" | awk 'NR==1{print $6}'` rate_des=`cat $tidy_info | sed -n "${m},${n}p" | awk 'NR==2{print $5}'` sudo echo "${net_sou} ${net_des} ${rate_sou} ${rate_des}" >> ${net1} done } output2() { # 複製一份指定文件。 cp ${net1} ${net2} # 開始for循環,對指定文件中的全部內容進行字符串比對,若是這些內容中含有關鍵字「Mb」,則將其前面的數字乘以1024,並將結果在複製的另外一份文件中進行批量替換。若是內容中含有關鍵字「Kb」,只取其前面的數字,而且一樣在另外一份文件中進行批量替換。若是內容中含有關鍵字「b」,將其總體改成0,也在另外一份文件中進行批量替換。 # 將全部的帶寬顯示內容轉換爲Kb單位,若是不足1K,統一標記爲0,忽略不計。(不顯示單位) for i in `cat ${net1}` do if [[ $i =~ Mb ]];then Mb_sou_num=`echo $i | awk -F 'Mb' '{print $1}'` Mb_fin_num=`echo "${Mb_sou_num}*1024" | bc` sed -i "s#${i}#${Mb_fin_num}#g" ${net2} elif [[ $i =~ Kb ]];then Kb_num=`echo $i | awk -F 'Kb' '{print $1}'` sed -i "s#${i}#${Kb_num}#g" ${net2} elif [[ $i =~ b ]];then sed -i "s#$i#0#g" ${net2} fi done } output3() { # awk數組將相同源地址與源端口的鏈接合併,而且將對應的帶寬佔用相加。重定向到指定文件中保存。 awk '{a[$1]+=$3;b[$1]+=$4}END{for(i in a){print i" "a[i]" "b[i];}}' ${net2} > ${net3} # 開始while循環,對指定文件的每一行進行循環,取其發包帶寬和回報帶寬,將其相加算成雙向帶寬,在將本來的內容後面追加上雙向帶寬,追加劇定向到指定文件保存。 while read line do lie2=`echo $line | awk '{print $2}'` lie3=`echo $line | awk '{print $3}'` lie4=`echo "${lie2}+${lie3}" | bc` echo "${line} ${lie4}" >> ${net4} done < ${net3} # for循環中嵌套while循環,在保存好的信息中進行過濾,只保留IP地址中包含本地服務器網卡地址的信息,追加劇定向到指定文件。 for i in $ipaddr do while read line do if [[ $line =~ $i ]];then echo $line >> ${net5} fi done < ${net4} done } output4() { # 將指定文件按照第4列內容(雙向流量)進行由大到小排序。 cat ${net5} | sort -k 4 -rn > ${net6} # 打印空行到最終文件。 echo "" >> ${net_status} echo "" >> ${net_status} # 打印腳本開始時收集的日期和時間到最終文件。 echo "$timestamp" >> ${net_status} # 打印iftop直接統計的eth0網卡雙向總帶寬佔用打印到最終文件。 echo "Total send and receive rate: ${total_rate}" >> ${net_status} line >> ${net_status} # 打印須要顯示內容的表頭到最終文件。 echo -e "Number \t Address & Port \t Command \t User \t\t Pid \t\t Send \t\t Receive \t Total" >> ${net_status} echo "" >> ${net_status} # 設置計數器,初始值爲0。 number=0 # 開始while循環。 while read line do # 計數器加1。 number=`echo ${number}+1 | bc` # 從當前行內容中,取以冒號和空格爲分隔符的第2列。 serport=`echo $line | awk -F '[:| ]' '{print $2}'` # 從當前行內容中,取以空格爲分隔符的第一列。 souraddr=`echo $line | awk '{print $1}'` # 從當前行內容中,取發包帶寬Kb的數字。 Kb_num_send=`echo $line | awk '{print $2}'` # 從當前行內容中,取回包帶寬Kb的數字。 Kb_num_receive=`echo $line | awk '{print $3}'` # 從當前內容航中,取雙向帶寬Kb的數字。 Kb_num_total=`echo $line | awk '{print $4}'` # 取發包、回包、雙向帶寬數字的整數。 int_Kb_num_send=`echo ${Kb_num_send} | awk -F '.' '{print $1}'` int_Kb_num_receive=`echo ${Kb_num_receive} | awk -F '.' '{print $1}'` int_Kb_num_total=`echo ${Kb_num_total} | awk -F '.' '{print $1}'` # 給發包、回包、雙向帶寬數字加上Kb單位。 service_send=`echo "${Kb_num_send} Kb"` service_receive=`echo "${Kb_num_receive} Kb"` service_total=`echo "${Kb_num_total} Kb"` # 根據地址端口查詢其服務名稱(由於時間差問題,有的端口可能已經釋放,查不到了) service=`sudo lsof -i:${serport} | tail -1` # 根據取到的信息,分別取出其進程名、pid、運行用戶。 service_cmd=`echo ${service} | awk '{print $1}'` service_pid=`echo ${service} | awk '{print $2}'` service_user=`echo ${service} | awk '{print $3}'` # 判斷若是進程名取到了,統計進程名長度,若是進程名沒有取到,直接將進程名改成一個tab。 if [ ! -z ${service_cmd} ];then length_cmd=`expr length "${service_cmd}"` elif [ -z ${service_cmd} ];then service_cmd=`echo -e "\t"` fi # 判斷進程名長度若是小於6,則在後面補上一個tab。 if [ ${length_cmd} -lt 6 ];then service_cmd=`echo -e "${service_cmd}\t"` fi if [ ! -z ${service_user} ];then length_user=`expr length "${service_user}"` elif [ -z ${service_user} ];then service_user=`echo -e " "` fi if [ ${length_user} -lt 6 ];then service_user=`echo -e "${service_user}\t"` fi # 若是發包、回包、雙向帶寬數字大於1024,將其除以1024,保留兩位小數,單位變爲Mb。 if [ ${int_Kb_num_send} -gt 1024 ];then Mb_num_send=`echo "scale=2;${Kb_num_send}/1024" | bc` service_send=`echo "${Mb_num_send} Mb"` fi if [ ${int_Kb_num_receive} -gt 1024 ];then Mb_num_receive=`echo "scale=2;${Kb_num_receive}/1024" | bc` service_receive=`echo "${Mb_num_receive} Mb"` fi if [ ${int_Kb_num_total} -gt 1024 ];then Mb_num_total=`echo "scale=2;${Kb_num_total}/1024" | bc` service_total=`echo "${Mb_num_total} Mb"` fi # 統計發包、回包、雙向帶寬(帶單位和空格)長度,若是小於6,後面補上2個空格,由於其最少是4。 length_send=`expr length "${service_send}"` if [ ${length_send} -lt 6 ];then service_send=`echo "${service_send} "` fi length_receive=`expr length "${service_receive}"` if [ ${length_receive} -lt 6 ];then service_receive=`echo "${service_receive} "` fi length_total=`expr length "${service_total}"` if [ ${length_total} -lt 6 ];then service_total=`echo "${service_total} "` fi # 將收集完成的內容,根據須要,進行追加劇定向到最終文件中。 echo -e "${number} \t ${souraddr} \t ${service_cmd} \t ${service_user} \t ${service_pid} \t\t ${service_send} \t ${service_receive} \t ${service_total}" >> ${net_status} done < ${net6} line >> ${net_status} echo "" >> ${net_status} echo "" >> ${net_status} } # 刪除腳本執行過程當中建立的臨時使用文件。 del_tmp() { rm -f ${net1} rm -f ${net2} rm -f ${net3} rm -f ${net4} rm -f ${net5} rm -f ${net6} rm -f ${iftop_info} rm -f ${tidy_info} } main() { del_tmp define check tidy output1 output2 output3 output4 del_tmp } main
腳本很冗長,由於趕着用,因此沒有進行優化,有興趣的朋友自行精簡,不喜勿噴,謝謝。spa
還有…… python真的很好用,抓緊學python。
腳本尚有BUG和待優化內容,正式業務不要使用。