知識點:
1)數組
數組是用來存儲一系列值的變量,可通過索引來通路數組的值。
Awk中數組稱為關聯數組,因為它的下标(索引)可以是數字也可以是字元串。
下标通常稱為鍵,數組元素的鍵和值存儲在Awk程式内部的一個表中,該表采用雜湊演算法,是以數組元素是随機排序。
數組格式:array[index]=value
1、Nginx日志分析
日志格式:'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'
日志記錄:27.189.231.39 - - [09/Apr/2016:17:21:23 +0800] "GET /Public/index/images/icon_pre.png HTTP/1.1" 200 44668 "http://www.test.com/Public/index/css/global.css" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36" "-"
1)統計日志中通路最多的10個IP
思路:對第一列進行去重,并輸出出現的次數
方法1:$ awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
方法2:$ awk '{print $1}' access.log |sort |uniq -c |sort -k1 -nr |head -n10
說明:a[$1]++ 建立數組a,以第一列作為下标,使用運算符++作為數組元素,元素初始值為0。處理一個IP時,下标是IP,元素加1,處理第二個IP時,下标是IP,元素加1,如果這個IP已經存在,則元素再加1,也就是這個IP出現了兩次,元素結果是2,以此類推。是以可以實作去重,統計出現次數。
2)統計日志中通路大于100次的IP
方法1:$ awk '{a[$1]++}END{for(i in a){if(a[i]>100)print i,a[i]}}' access.log
方法2:$ awk '{a[$1]++;if(a[$1]>100){b[$1]++}}END{for(i in b){print i,a[i]}}' access.log
說明:方法1是将結果儲存a數組後,輸出時判斷符合要求的IP。方法2是将結果儲存a數組時,并判斷符合要求的IP放到b數組,最後列印b數組的IP。
3)統計2016年4月9日一天内通路最多的10個IP
思路:先過濾出這個時間段的日志,然後去重,統計出現次數
方法1:$ awk '$4>="[9/Apr/2016:00:00:01" && $4<="[9/Apr/2016:23:59:59" {a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
方法2:$ sed -n '/\[9\/Apr\/2016:00:00:01/,/\[9\/Apr\/2016:23:59:59/p' access.log |sort |uniq -c |sort -k1 -nr |head -n10 #前提開始時間與結束時間日志中必須存在
4)統計目前時間前一分鐘的通路數
思路:先擷取目前時間前一分鐘對應日志格式的時間,再比對統計
$ date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$0~date{c++}END{print c}' access.log $ date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$4>="["date":00" && $4<="["date":59"{c++}END{print c}' access.log $ grep -c $(date -d '-1 minute' +%d/%b/%Y:%H:%M) access.log
說明:date +%d/%b/%Y:%H:%M --> 09/Apr/2016:01:55
5)統計通路最多的前10個頁面($request)
$ awk '{a[$7]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log
6)統計每個URL通路内容的總大小($body_bytes_sent)
$ awk '{a[$7]++;size[$7]+=$10}END{for(i in a)print a[i],size[i],i}' access.log
7)統計每個IP通路狀态碼數量($status)
$ awk '{a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log
8)統計通路狀态碼為404的IP及出現次數
$ awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log
2、兩個檔案對比
檔案内容如下:
$ cat a
1
2
3
4
5
6
$ cat b
7
8
1)找出相同記錄
方法1:$ awk 'FNR==NR{a[$0];next}($0 in a)' a b
3
4
5
6
解釋前,先看下FNR和NR差別:
$ awk '{print NR,$0}' a b
1 1
2 2
3 3
4 4
5 5
6 6
7 3
8 4
9 5
10 6
11 7
12 8
$ awk '{print FNR,$0}' a b
1 3
2 4
3 5
4 6
5 7
6 8
可以看出NR是處理一行記錄,編号就會加1,同時也可以看到awk将兩個檔案當成一個合并後的檔案處理。
而FNR則是處理一行記錄,編号也會加1,但是,處理到第二個檔案時,編号重新計數
說明:FNR和NR是内置變量。FNR==NR常用于對兩個檔案處理,這個例子可以了解為awk将兩個檔案當成一個檔案處理。
處理a檔案時,FNR是等于NR的,條件為真,執行a[$0],next表達式,意思是将每條記錄存放到a數組作為下标(無元素),next是跳出,類似于continue,不執行後面表達式。
執行過程以此類推,直到處理b件時,FNR不等于NR(FNR重新計數是1,NR繼續加1是7),條件為假,不執行後面a[$0],next表達式,直接執行($0 in a)表達式,這句意思是處理b檔案第一條繼續判斷是否在a數組中,如果在則列印這條記錄,以此類推。
這樣可能更好了解些:
$ awk 'FNR==NR{a[$0]}NR>FNR{if($0 in a)print $0}' a b
方法2:
$ awk 'FNR==NR{a[$0]=1;next}(a[$0])' a b #小括号可以不加
$ awk 'FNR==NR{a[$0]=1;next}(a[$0]==1)' a b$ awk 'FNR==NR{a[$0]=1;next}{if(a[$0]==1)print}' a b
$ awk 'FNR==NR{a[$0]=1}FNR!=NR&&a[$0]==1' a b
說明:先要知道後面的a[$0]不是一個數組,而是通過下标(b檔案每條記錄)來通路a數組元素。如果a[b的一行記錄]擷取的a數組元素是1,則為真,也就是等于1,列印這條記錄,否則擷取不到元素,則為假。
方法3:
$ awk 'ARGIND==1{a[$0]=1}ARGIND==2&&a[$0]==1' a b
$ awk 'FILENAME=="a"{a[$0]=1}FILENAME=="b"&&a[$0]==1' a b
說明:ARGIND内置變量,處理檔案辨別符,第一個檔案為1,第二個檔案為2。FILENAME也是内置變量,表示輸入檔案的名字
方法4:$ sort a b |uniq -d
方法5:$ grep -f a b
2)找不同記錄(同上,取反)
$ awk 'FNR==NR{a[$0];next}!($0 in a)' a b $ awk 'FNR==NR{a[$0]=1;next}!a[$0]' a b $ awk 'ARGIND==1{a[$0]=1}ARGIND==2&&a[$0]!=1' a b $ awk 'FILENAME=="a"{a[$0]=1}FILENAME=="b"&&a[$0]!=1' a b 7 8 方法2:$ sort a b |uniq -d 方法3:$ grep -vf a b
3、合并兩個檔案
1)将d檔案性别合并到c檔案
$ cat c
zhangsan 100
lisi 200
wangwu 300
$ cat d
zhangsan man
lisi woman
方法1:$ awk 'FNR==NR{a[$1]=$0;next}{print a[$1],$2}' c d
zhangsan 100 man
lisi 200 woman
wangwu 300 man
方法2:$ awk 'FNR==NR{a[$1]=$0}NR>FNR{print a[$1],$2}' c d
說明:NR==FNR比對第一個檔案,NR>FNR比對第二個檔案,将$1為數組下标
方法3:$ awk 'ARGIND==1{a[$1]=$0}ARGIND==2{print a[$1],$2}' c d
2)将a.txt檔案中服務名稱合并到一個IP中
$ cat a.txt
192.168.2.100 : httpd
192.168.2.100 : tomcat
192.168.2.101 : httpd
192.168.2.101 : postfix
192.168.2.102 : mysqld
192.168.2.102 : httpd
$ awk -F: -vOFS=":" '{a[$1]=a[$1] $2}END{for(i in a)print i,a[i]}' a.txt
$ awk -F: -vOFS=":" '{a[$1]=$2 a[$1]}END{for(i in a)print i,a[i]}' a.txt
192.168.2.100 : httpd tomcat
192.168.2.101 : httpd postfix
192.168.2.102 : mysqld httpd
說明:a[$1]=$2 第一列為下标,第二個列是元素,後面跟的a[$1]是通過第一列取a數組元素(服務名),結果是$1=$2 $2,并作為a數組元素
3)将第一行附加給下面每行開頭
$ cat a.txt
xiaoli
a 100
b 110
c 120
$ awk 'NF==1{a=$0;next}{print a,$0}' a.txt
$ awk 'NF==1{a=$0}NF!=1{print a,$0}' a.txt
xiaoli a 100
xiaoli b 110
xiaoli c 120
4、倒叙列列印文本
xiaoli a 100
xiaoli b 110
xiaoli c 120
$ awk '{for(i=NF;i>=1;i--){printf "%s ",$i}print s}' a.txt
100 a xiaoli
110 b xiaoli
120 c xiaoli
$ awk '{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i" "}' a.txt
說明:利用NF降序輸出,把最後一個域作為第一個輸出,然後自減,print s或print ""列印一個換行符
5、從第二列列印到最後
方法1:$ awk '{for(i=2;i<=NF;i++)if(i==NF)prin
tf $i"\n";else printf $i" "}' a.txt
方法2:$ awk '{$1=""}{print $0}' a.txt
6、将c檔案中第一列放到到d檔案中的第三列
$ cat c
a
b
c
$ cat d
1 one
2 two
3 three
方法1:$ awk 'FNR==NR{a[NR]=$0;next}{$3=a[FNR]}1' c d
說明:以NR編号為下标,元素是每行,當處理d檔案時第三列等于擷取a資料FNR(重新計數1-3)編号作為下标。
方法2:$ awk '{getline f<"c";print $0,f}' d
1 one a
2 two b
3 three c
1)替換第二列
$ awk '{getline f<"c";gsub($2,f,$2)}1' d 1 a 2 b 3 c2)替換第二列的two
$ awk '{getline f<"c";gsub("two",f,$2)}1' d
7、數字求和
方法1:$ seq 1 100 |awk '{sum+=$0}END{print sum}'
方法2:$ awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++}print sum}'
方法3:$ awk 'BEGIN{for(i=1;i<=100;i++)sum+=i}END{print sum}' /dev/null
方法4:$ seq -s + 1 100 |bc
8、每隔三行添加一個換行符或内容
方法1:$ awk '$0;NR%3==0{printf "\n"}' a
方法2:$ awk '{print NR%3?$0:$0"\n"}' a
方法3:$ sed '4~3s/^/\n/' a
9、字元串拆分
方法1:
$ echo "hello" |awk -F '' '{for(i=1;i<=NF;i++)print $i}' $ echo "hello" |awk -F '' '{i=1;while(i<=NF){print $i;i++}}' h e l o
$ echo "hello" |awk '{split($0,a,"''");for(i in a)print a[i]}' #無序
10、統計字元串中每個字母出現的次數
$ echo a,b.c.a,b.a |tr "[,. ]" "\n" |awk -F '' '{for(i=1;i<=NF;i++)a[$i]++}END{for(i in a)print i,a[i]|"sort -k2 -rn"}' a 3 b 2 c 1
11、第一列排序
$ awk '{a[NR]=$1}END{s=asort(a,b);for(i=1;i<=s;i++){print i,b[i]}}' a.txt
說明:以每行編号作為下标值為$1,并将a數組值放到數組b,a下标丢棄,并将asort預設傳回值(原a數組長度)指派給s,使用for循環小于s的行号,從1開始到數組長度列印排序好的數組。
12、删除重複行,順序不變
$ awk '!a[$0]++' file
13、删除指定行
删除第一行:
$ awk 'NR==1{next}{print $0}' file #$0可省略 $ awk 'NR!=1{print}' file $ sed '1d' file $ sed -n '1!p' file
14、在指定行前後加一行
在第二行前一行加txt:
$ awk 'NR==2{sub('/.*/',"txt\n&")}{print}' a.txt $ sed'2s/.*/txt\n&/' a.txt
在第二行後一行加txt:
$ awk 'NR==2{sub('/.*/',"&\ntxt")}{print}' a.txt $ sed'2s/.*/&\ntxt/' a.txt
15、通過IP擷取網卡名
$ ifconfig |awk -F'[: ]' '/^eth/{nic=$1}/192.168.18.15/{print nic}'
16、浮點數運算(數字46保留小數點)
$ awk 'BEGIN{print 46/100}' $ awk 'BEGIN{printf "%.2f\n",46/100}' $ echo 46|awk '{print $0/100}' $ echo 'scale=2;46/100' |bc|sed 's/^/0/' $ printf "%.2f\n" $(echo "scale=2;46/100" |bc) 結果:0.46
17、替換換行符為逗号
$ cat a.txt 替換後:1,2,3
$ awk '{s=(s?s","$0:$0)}END{print s}' a.txt
說明:三目運算符(a?b:c),第一個s是變量,s?s","$0:$0,第一次處理1時,s變量沒有指派初值是0,0為假,結果列印1,第二次處理2時,s值是1,為真,結果1,2。以此類推,小括号可以不寫。
$ tr '\n' ',' < a.txt
$ sed ':a;N;s/\n/,/;$!b a' a.txt
說明:第一個标簽a,先讀取第一行記錄1追加到模式空間,此時模式空間内容是1$,執行$!b($!最後一行不跳轉,b是控制流跳轉指令)跳轉到a标簽,繼續讀取第二行記錄2追加到模式空間,因為使用N指令,每個記錄以換行符(\n)分割,此時模式空間内容是1\n2$,執行将換行符替換逗号指令,繼續跳轉到a标簽...
方法4:
$ sed ':a;$!N;s/\n/,/;t a' a.txt
說明:與上面類似,其中t是測試指令,當上一個指令(替換)執行成功才跳轉。
方法5:
$ awk '{if($0!=3)printf "%s,",$0;else print $0}' a.txt
說明:3是文本最後一個數
方法6:
while read line; do a+=($line) done < a.txt echo ${a[*]} |sed 's/ /,/g'
說明:将每行放到數組,然後替換
18、把奇數換行符去掉
$ cat b.txt string number $ awk 'ORS=NR%2?"\t":"\n"' b.txt #把奇數行換行符去掉 $ xargs -n2 < a.txt #将兩個字段作為一行 string number a 1
19、費用統計
姓名 費用 數量 zhangsan 8000 1 zhangsan 5000 1 lisi 1000 1 lisi 2000 1 wangwu 1500 1 zhaoliu 6000 1 zhaoliu 2000 1 zhaoliu 3000 1 統計每人總費用、總數量: $ awk '{name[$1]++;number[$1]+=$3;money[$1]+=$2}END{for(i in name)print i,number[i],money[i]}' a.txt zhaoliu 3 11000 zhangsan 2 13000 wangwu 1 1500 lisi 2 3000
20、列印乘法口訣
$ awk 'BEGIN{for(n=0;n++<9;){for(i=0;i++<n;)printf i"x"n"="i*n" ";print ""}}' 1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
#!/bin/bash for ((i=1;i<=9;i++)); do for ((j=1;j<=i;j++)); do result=$(($i*$j)) #let "result=i*j" echo -n "$i*$j=$result " done echo done
21、隻列印奇數或偶數行
列印奇數行:
$ seq 1 5 |awk 'i=!i'
說明:先知道對于數值運算,未定義變量初值為0,對于字元運算,未定義變量初值為空字元串。
讀取第一行記錄,然後進行模式比對,i是未定義變量,也就是i=!0,!取反意思。感歎号右邊是個布爾值,0或空字元串為假,非0或非空字元串為真,!0就是真,是以i=1,條件為真列印第一條記錄。
沒有print為什麼會列印呢?因為模式後面沒有動作,預設會列印整條記錄。
讀取第二行記錄,進行模式比對,因為上次i的值由0變成了1,此時就是i=!1,條件為假不列印。
讀取第三行記錄,因為上次條件為假,i恢複初值為0,繼續列印。以此類推...
可以看出,運算時并沒有判斷記錄,而是利用布爾值真假判斷
$ seq 1 5 |awk 'NR%2!=0'
$ seq 1 5 |sed -n '1~2p'
說明:步長,每隔一行列印一次
$ seq 1 5 |sed -n 'p;n'
說明:先列印第一行,執行n指令讀取目前行的下一行2,放到模式空間,後面再沒有列印模式空間行操作,是以隻儲存不列印,同等方式繼續列印第三行。
列印偶數行:
$ seq 1 5 |awk '!(i=!i)' $ seq 1 5 |awk 'NR%2==0' $ seq 1 5 |sed -n '0~2p' $ seq 1 5 |sed -n 'n;p' 本文轉自liujing0751CTO部落格,原文連結: http://blog.51cto.com/13281352/1983553 ,如需轉載請自行聯系原作者