天天看點

AWK常用技巧

1.1 介紹

awk其名稱得自于它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 AWK 的确擁有自己的語言: AWK 程式設計語言 , 三位建立者已将它正式定義為“樣式掃描和處理語言”。

它允許您建立簡短的程式,這些程式讀取輸入檔案、為資料排序、處理資料、對輸入執行計算以及生成報表,還有無數其他的功能。

awk 是一種很棒的語言,它适合文本處理和報表生成,其文法較為常見,借鑒了某些語言的一些精華,如 C 語言等。在 linux 系統日常處理工作中,發揮很重要的作用,掌握了 awk将會使你的工作變的高大上。

1.1.1 AWK原理

這需要一個例子來說明,你将會見到/etc/passwd 檔案的内容出現在眼前。

awk '{print $0}' /etc/passwd
echo clsn|awk '{print "hello,world"}'
awk '{ print "clsn" }' /etc/passwd      

現在,解釋 awk 做了些什麼。調用 awk時,我們指定/etc/passwd 作為輸入檔案。執行 awk 時,它依次對/etc/passwd 中的每一行執行 print 指令。所有輸出都發送到 stdout,所得到的結果與執行 cat /etc/passwd 完全相同。現在,解釋{ print }代碼塊。在 awk 中,花括号用于将幾塊代碼組合到一起,這一點類

似于C 語言。在代碼塊中隻有一條 print 指令。在awk 中,如果隻出現 print 指令,那麼将列印目前行的全部内容。

再次說明,awk 對輸入檔案中的每一行都執行這個腳本。

AWK常用技巧

1.2 AWK常用速查表

1.2.1 AWK運算符

運算符 說明
指派運算符
= += -= *= /= %= ^= **= 指派語句
邏輯運算符
|| 邏輯或
&& 邏輯與
正則運算符
~ !~ 比對正規表達式和不比對正規表達式
關系運算符
< <= > >= != ==
算術運算符
+ - 加,減
* / & 乘,除與求餘
+ - ! 一進制加,減和邏輯非
^ *** 求幂
++ -- 增加或減少,作為字首或字尾
其他運算符
$ 字段引用
空格 字元串連結符
?: 三目運算符
In 數組中是否存在某鍵值

1.2.2 常用AWK内置變量

變量名 屬性
$0 目前記錄
$1~$n 目前記錄的第 n 個字段
FS 輸入字段分隔符 預設是空格
RS 輸入記錄分割符 預設為換行符
NF 目前記錄中的字段個數,就是有多少列
NR 已經讀出的記錄數,就是行号,從 1 開始
OFS 輸出字段分隔符 預設也是空格
ORS 輸出的記錄分隔符 預設為換行符

1.2.3 awk中的正則

元字元 功能 示例 解釋
^ 行首定位符 /^root/ 比對所有以 root 開頭的行
行尾定位符 /root$/ 比對所有以 root 結尾的行
. 比對任意單個字元 /r..t/

比對字母 r,然後兩個任意字元,再以 l

結尾的行,比如 root,r33l 等

* 比對 0 個或多個前導字元(包括回車) /a*ool/ 比對 0 個或多個 a 之後緊跟着 ool 的行,比如 ool,aaaaool 等
+ 比對 1 個或多個前導字元 /a+b/

比對 1 個或多個 a 加 b 的行,比如

ab,aab 等

? 比對 0 個或 1 個前導字元 /a?b/ 比對 b 或 ab 的行
[] 比對指定字元組内的任意一個字元 /^[abc] 比對以字母 a 或b 或 c 開頭的行
[^] 比對不在指定字元組内任意一個字元 /^[^abc]/ 比對不以字母 a 或 b 或 c 開頭的行
() 子表達式組合 /(rool)+/ 表示一個或多個 rool 組合,當有一些字元需要組合時,使用括号括起來
| 或者的意思 /(root)|B/ 比對root 或者 B 的行
\ 轉義字元 /a\/\// 比對 a//
~,!~ 比對,不比對的條件語句 $1~/root/ 比對第一個字段包含字元root 的所有記錄
x{m} x 重複m 次 /(root){3}/ 需要注意一點的是,root 加括号和不
x{m,} x 重複至少m 次 /(root){3,}/ 加括号的差別,x 可以表示字元串也
X{m,n}

x 重複至少 m 次,

但不超過 n 次

/(root){5,6}/

可以隻是一個字元,是以/root\{5\}/

表示比對roo 再加上5 個t,及roottttt

需要指定參數:

-posix         或者

--re-interval    沒 有該參數不能使用該模式

cat   rex.txt smierth,harry smierth,reru robin,tom

/\(root\)\{2,\}/ 則   表 示 匹 配

rootrootrootroot 等

awk -posix '/er\{1,2\}/' rex.text smierth,harry

smierth,reru

1.2.4 awk 常用函數表

函數
gsub( Ere, Repl, [ In ] ) 除了正規表達式所有具體值被替代這點,它和 sub 函數完全一樣地執行,。
sub( Ere, Repl, [ In ] ) 用 Repl 參數指定的字元串替換 In 參數指定的字元串中的由 Ere參數指定的擴充正規表達式的第一個具體值。sub 函數傳回替換的數量。出現在 Repl 參數指定的字元串中的 &(和符号)由 In 參數指定的與 Ere 參數的指定的擴充正規表達式比對的字元串替換。如果未指定 In 參數,預設值是整個記錄($0 記錄變量)。
index( String1, String2 ) 在由 String1 參數指定的字元串(其中有出現 String2 指定的參數)中,傳回位置,從 1 開始編号。如果 String2 參數不在 String1  參數中出現,則傳回 0(零)。
length [(String)]

傳回 String  參數指定的字元串的長度(字元形式)。如果未給出

String  參數,則傳回整個記錄的長度($0  記錄變量)。

blength [(String)] 傳回 String  參數指定的字元串的長度(以位元組為機關)。如果未給出
substr( String, M, [ N ] )

傳回具有 N 參數指定的字元數量子串。子串從 String 參數指定的字元串取得,其字元以 M 參數指定的位置開始。M 參數指定為将

String 參數中的第一個字元作為編号 1。如果未指定 N 參數,則子串的長度将是 M 參數指定的位置到 String 參數的末尾 的長度。

match( String, Ere ) 在 String  參數指定的字元串(Ere  參數指定的擴充正規表達式出現在其中)中傳回位置(字元形式),從 1  開始編号,或如果 Ere  參數不出現,則傳回 0(零)。RSTART  特殊變量設定為傳回值。RLENGTH特殊變量設定為比對的字元串的長度,或如果未找到任何比對,則設定為 -1(負一)。
split( String, A, [Ere] ) 将 String 參數指定的參數分割為數組元素 A[1], A[2], . . ., A[n],并傳回 n 變量的值。此分隔可以通過 Ere  參數指定的擴充正規表達式進行,或用目前字段分隔符(FS 特殊變量)來進行(如果沒有給出 Ere參數)。除非上下文指明特定的元素還應具有一個數字值,否則 A  數

1.3 AWK實踐

1.3.1 函數的簡單使用

[root@clsn6 ~]# awk '{gsub(/[0-9]+/,"");print}' /tmp/passwd 
root:x:::root:/root:/bin/bash
bin:x:::bin:/bin:/sbin/nologin
daemon:x:::daemon:/sbin:/sbin/nologin
adm:x:::adm:/var/adm:/sbin/nologin
lp:x:::lp:/var/spool/lpd:/sbin/nologin
sync:x:::sync:/sbin:/bin/sync
···      

1.3.2 統計系統中的secure檔案中誰在破解你的密碼

1、找到誰在破解密碼

[root@clsn6 awk]# awk '/Failed/{print $(NF-3)}' secure-20161219 |sort |uniq -c  |sort -nk1
      1 103.237.144.68
      1 122.228.238.66
      1 195.20.3.210
      1 85.93.5.71
···      

2、過濾出包含Failed password的行 并統計每個ip位址出現的次數

[root@clsn6 awk]# awk '/Failed/{fa[$(NF-3)]++}END{for(pol in fa)print pol,fa[pol]}' secure-20161219 |sort -rnk2
218.65.30.25 68652
218.65.30.53 34326
218.87.109.154 21201
112.85.42.103 18065
112.85.42.99 17164
218.87.109.151 17163      

1.3.3 統計secure檔案中每個使用者,被同一ip破解多少次

1、進破解使用的使用者及位址定向到檔案中

[root@clsn6 awk]# awk '/Failed/{print $(NF-5),$(NF-3)}' secure-20161219 >user-ip.log      

2、對檔案進行去重排序測

[root@clsn6 awk]# awk '{h[$1" "$2]++}END{for(p in h) print p,h[p]}' user-ip.log |head 
export 209.126.122.70 3
cvsadmin 209.126.122.70 3
user1 209.126.122.70 1
dasusr1 209.126.122.70 4
1 118.100.251.170 1
git 209.126.122.70 5
boss 195.154.50.61 1
user1 46.139.219.84 2
www 123.31.34.165 2
webmaster 195.154.50.61 1      

3、記錄檔檔案,取出資料

[root@clsn6 awk]# awk '/Failed/{h[$(NF-5)" "$(NF-3)]++}END{for(p in h) print p,h[p]}'  secure-20161219 |head
export 209.126.122.70 3
cvsadmin 209.126.122.70 3
user1 209.126.122.70 1
dasusr1 209.126.122.70 4
1 118.100.251.170 1      

1.3.4 統計nginx access.log檔案中對ip位址去重并統計重複數

[root@clsn6 awk]# awk '{h[$1]++}END{for(i in h) print i,h[i]}' access.log |sort -nrk2 |head 
58.220.223.62 12049
112.64.171.98 10856
114.83.184.139 1982      

1.3.5 統計access.log檔案中每個ip位址使用了多少流量

1、流量總計

[root@clsn6 awk]# awk '{sum=sum+$10}END{print sum}' access.log 
2478496663
[root@clsn6 awk]# awk '{sum=sum+$10}END{print sum/1024^3}' access.log 
2.30828      

2、每個ip使用的流量

[root@clsn6 awk]# awk '{h[$1]=h[$1]+$10}END{for(p in h) print p,h[p]/1024^2 }' access.log |sort -rnk2|column -t |head 
114.83.184.139   29.91
117.136.66.10    21.3922
116.216.30.47    20.4716
223.104.5.197    20.4705
116.216.0.60     18.2584      

1.3.6 統計access.log檔案中每個ip位址使用了多少流量和每個ip位址的出現次數

[root@clsn6 awk]# awk '{count[$1]++;sum[$1]=sum[$1]+$10}END{for(pol in sum)print pol,count[pol],sum[pol]}' access.log |sort -nrk2 |column -t |head 
58.220.223.62    12049  12603075
112.64.171.98    10856  15255013
114.83.184.139   1982   31362956
117.136.66.10    1662   22431302
115.29.245.13    1318   1161468
223.104.5.197    961    21464856
116.216.0.60     957    19145329      

格式化指令

awk '{
  count[$1]++
  sum[$1]+=$10
}END{
  for(pol in sum)
  print pol,count[pol],sum[pol]
}' access.log |sort -nrk3 |head      

格式化後執行

[root@clsn6 awk]# awk '{
  count[$1]++
  sum[$1]+=$10
}END{
  for(pol in sum)
  print pol,count[pol],sum[pol]
}' access.log |column -t|  sort -nrk3 |head 
114.83.184.139   1982   31362956
117.136.66.10    1662   22431302
116.216.30.47    506    21466000
223.104.5.197    961    21464856
116.216.0.60     957    19145329
114.141.164.180  695    17219553
114.111.166.22   753    17121524
223.104.5.202    871    16911512
116.228.21.187   596    15969887
112.64.171.98    10856  15255013      

1.3.7 按要求得到最後面的格式的結果

檔案内容

cat >next2.txg<< EOF
web01[192.168.2.100]
httpd            ok
tomcat            ok
sendmail          ok
web02[192.168.2.101]
httpd            ok
postfix           ok
web03[192.168.2.102]
mysqld           ok
httpd            ok
EOF      

想要的結果:

web01[192.168.2.100] httpd            ok
web01[192.168.2.100] tomcat           ok
web01[192.168.2.100] sendmail          ok
web02[192.168.2.101] httpd            ok
web02[192.168.2.101] postfix           ok
web03[192.168.2.102] mysqld           ok
web03[192.168.2.102] httpd            ok      

方法一:

[root@clsn6 awk]# awk '/^web/{tmp=$0} !/^web/{print tmp,$0}' next.txt 
web01[192.168.2.100] httpd            ok
web01[192.168.2.100] tomcat           ok
web01[192.168.2.100] sendmail         ok
web02[192.168.2.101] httpd            ok
web02[192.168.2.101] postfix          ok
web03[192.168.2.102] mysqld           ok
web03[192.168.2.102] httpd            ok      

方法二:

[root@clsn6 awk]# awk '/^web/{tmp=$0;next}{print tmp,$0}' next.txt 
web01[192.168.2.100] httpd            ok
web01[192.168.2.100] tomcat           ok
web01[192.168.2.100] sendmail         ok
web02[192.168.2.101] httpd            ok
web02[192.168.2.101] postfix          ok
web03[192.168.2.102] mysqld           ok
web03[192.168.2.102] httpd            ok      

next:停止處理目前行,從頭開始處理下一行

[root@clsn6 awk]# seq 5 |awk 'NR==3{next}{print NR,$0}'
1 1
2 2
4 4
5 5
[root@clsn6 awk]# seq 5 |awk 'NR==1{next}{print NR,$0}'
2 2
3 3
4 4
5 5      

跳過偶數行

[root@clsn6 awk]# seq 5 |awk 'NR%2==0{next}{print NR,$0}'
1 1
3 3
5 5      

跳過奇數行

[root@clsn6 awk]# seq 5 |awk 'NR%2==1{next}{print NR,$0}'
2 2
4 4      

1.3.8 統計每個學生的總成績和平均成績

成績檔案

cat > chengji.txt <<EOF
cc 90 98 98 96 96 92
ll 70 77 85 83 70 89
ss 85 92 78 94 88 91
nn 89 90 85 94 90 95
bb 84 88 80 92 84 82
gg 64 80 60 60 61 62
EOF      

算出總和

[root@clsn6 awk]#  awk '{sum=0;for(i=2;i<=NF;i++)sum+=$i;print $1,sum}' chengji.txt 
cc 570
ll 474
ss 528
nn 543
bb 510
gg 387      

算出平均數

[root@clsn6 awk]#  awk '{sum=0;for(i=2;i<=NF;i++)sum+=$i;avg=sum/(NF-1);print $1,sum,avg}' chengji.txt 
cc 570 95
ll 474 79
ss 528 88
nn 543 90.5
bb 510 85
gg 387 64.5      
awk '{
    sum=0
    for(i=2;i<=NF;i++)
    sum+=$i
    avg=sum/(NF-1)
    print $1,sum,avg
}' chengji.txt      

1.3.9 列印下面語句中字元數小于6的單詞

echo "I am clsn ops,I very like linux hahahaha." > text.txt      

shell方法

[root@clsn6 awk]# for i in `sed  's#,# #g' text.txt`
> do 
>    [ ${#i} -lt 6 ] && echo $i
> done
I
am
clsn
ops
I
very
like
linux      

grep 方法

[root@clsn6 awk]# egrep -wo '[a-Z]{,6}' text.txt 
I
am
clsn
ops
I
very
like
linux      

awk方法

[root@clsn6 awk]# echo clsn |awk '{print length($1)}'
4
[root@clsn6 awk]# awk -F "[, ]" '{for(i=1;i<=NF;i++) if (length($i)<6) print $i}'   text.txt 
I
am
clsn
ops
I
very
like
linux      

1.4 附錄

1.4.1 sort指令的使用

[root@clsn6 awk]# du -sh /* 2>/dev/null|sort -hr
1.1G    /usr
214M    /lib
120M    /var
109M    /root
38M    /boot
···      

1.4.2 統計流量使用的指令

[root@clsn6 ~]# yum install htop iotop iftop atop nethogs -y
# iftop 總體流量使用情況
# nethogs 顯示進行級别的流量使用      

1.4.3 其他日志分析方法

awstat,piwiki,elk      

1.5 參考文獻

[1]  https://www.gnu.org/software/gawk/manual/gawk.html [2]  https://www.cnblogs.com/ginvip/p/6352157.html

作者:

慘綠少年

出處:

https://www.nmtui.com

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀