您好,登錄后才能下訂單哦!
awk是一個處理文本的編程語言工具,能用簡短的程序處理標準輸入或文件、數據排序、計算以及生成報表等等。
在Linux系統下默認awk是gawk,它是awk的GNU版本。可以通過命令查看應用的版本:ls -l /bin/awk
基本的命令語法:awk option 'pattern {action}' file
其中pattern表示AWK在數據中查找的內容,而action是在找到匹配內容時所執行的一系列命令。花括號用于根據特定的模式對一系列指令進行分組。
awk處理的工作方式與數據庫類似,支持對記錄和字段處理,這也是grep和sed不能實現的。
在awk中,缺省的情況下將文本文件中的一行視為一個記錄,逐行放到內存中處理,而將一行中的某一部分作為記錄中的一個字段。用1,2,3...數字的方式順序的表示行(記錄)中的不同字段。用$后跟數字,引用對應的字段,以逗號分隔,0表示整個行。
選項 | 描述 |
-f program-file | 從文件中讀取awk程序源文件 |
-F fs | 指定fs為輸入字段分隔符 |
-v var=value | 變量賦值 |
--posix | 兼容POSIX正則表達式 |
--dump-variables=[file] | 把awk命令時的全局變量寫入文件, 默認文件是awkvars.out |
--profile=[file] | 格式化awk語句到文件,默認是awkprof.out |
常用模式有:
Pattern | Description |
BEGIN{ } | 給程序賦予初始狀態,先執行的工作 |
END{ } | 程序結束之后執行的一些掃尾工作 |
/regular expression/ | 為每個輸入記錄匹配正則表達式 |
pattern && pattern | 邏輯and,滿足兩個模式 |
pattern || pattern | 邏輯or,滿足其中一個模式 |
! pattern | 邏輯not,不滿足模式 |
pattern1, pattern2 | 范圍模式,匹配所有模式1的記錄,直到匹配到模式2 |
而動作呢,就是下面所講的print、流程控制、I/O語句等。
示例:
1)從文件讀取awk程序處理文件
# vi test.awk {print$2} # tail -n3 /etc/services |awk -f test.awk 48049/tcp 48128/tcp 49000/tcp
2)指定分隔符,打印指定字段
打印第二字段,默認以空格分隔: # tail -n3 /etc/services |awk '{print $2}' 48049/tcp 48128/tcp 48128/udp 指定冒號為分隔符打印第一字段: # awk-F ':' '{print $1}' /etc/passwd root bin daemon adm lp sync ......
還可以指定多個分隔符,作為同一個分隔符處理:
# tail -n3 /etc/services |awk -F'[/#]' '{print $3}' iqobject iqobject MatahariBroker # tail -n3 /etc/services |awk -F'[/#]' '{print $1}' iqobject 48619 iqobject 48619 matahari 49000 # tail -n3 /etc/services |awk -F'[/#]' '{print $2}' tcp udp tcp # tail -n3 /etc/services |awk -F'[/#]' '{print $3}' iqobject iqobject MatahariBroker # tail -n3 /etc/services |awk -F'[ /]+' '{print $2}' 48619 48619 49000
[]元字符的意思是符號其中任意一個字符,也就是說每遇到一個/或#時就分隔一個字段,當用多個分隔符時,就能更方面處理字段了。
3)變量賦值
# awk-v a=123 'BEGIN{print a}' 123 系統變量作為awk變量的值: #a=123 # awk-v a=$a 'BEGIN{print a}' 123 或使用單引號 # awk'BEGIN{print '$a'}' 123
4)輸出awk全局變量到文件
# seq 5|awk --dump-variables '{print $0}' 1 2 3 4 5 # cat awkvars.out ARGC:number (1) ARGIND:number (0) ARGV:array, 1 elements BINMODE:number (0) CONVFMT:string ("%.6g") ERRNO:number (0) FIELDWIDTHS:string ("") FILENAME:string ("-") FNR:number (5) FS:string (" ") IGNORECASE:number (0) LINT:number (0) NF:number (1) NR:number (5) OFMT:string ("%.6g") OFS:string (" ") ORS:string ("\n") RLENGTH:number (0) RS:string ("\n") RSTART:number (0) RT:string ("\n") SUBSEP:string ("\034") TEXTDOMAIN:string ("messages")
5)BEGIN和END
BEGIN模式是在處理文件之前執行該操作,常用于修改內置變量、變量賦值和打印輸出的頁眉或標題。
例如:打印頁眉
# tail /etc/services |awk 'BEGIN{print"Service\t\tPort\t\t\tDescription\n==="}{print $0}' Service Port Description === 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Services isnetserv 48128/udp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw com-bardac-dw 48556/udp # com-bardac-dw iqobject 48619/tcp #iqobject iqobject 48619/udp # iqobject matahari 49000/tcp # Matahari Broker
END模式是在程序處理完才會執行。
例如:打印頁尾
# tail /etc/services |awk '{print $0}END{print "===\nEND......"}' 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Services isnetserv 48128/udp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw com-bardac-dw 48556/udp # com-bardac-dw iqobject 48619/tcp # iqobject iqobject 48619/udp # iqobject matahari 49000/tcp # Matahari Broker === END......
6)格式化輸出awk命令到文件
# tail /etc/services |awk --profile 'BEGIN{print"Service\t\tPort\t\t\tDescription\n==="}{print $0}END{print"===\nEND......"}' Service Port Description === nimgtw 48003/udp # Nimbus Gateway 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast ServiceProtocol isnetserv 48128/tcp # Image Systems Network Services isnetserv 48128/udp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw com-bardac-dw 48556/udp # com-bardac-dw iqobject 48619/tcp # iqobject iqobject 48619/udp # iqobject === END...... # cat awkprof.out # gawk profile, created Sat Jan 7 19:45:22 2017 # BEGIN block(s) BEGIN { print"Service\t\tPort\t\t\tDescription\n===" } # Rule(s) { print $0 } # END block(s) END { print "===\nEND......" }
7)/re/正則匹配
匹配包含tcp的行: # tail /etc/services |awk '/tcp/{print $0}' 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw iqobject 48619/tcp # iqobject matahari 49000/tcp # Matahari Broker 匹配開頭是blp5的行: # tail /etc/services |awk '/^blp5/{print $0}' blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator 匹配第一個字段是8個字符的行: # tail /etc/services |awk '/^[a-z0-9]{8} /{print $0}' iqobject 48619/tcp # iqobject iqobject 48619/udp # iqobject matahari 49000/tcp # Matahari Broker
8)邏輯and、or和not
匹配記錄中包含blp5和tcp的行: #tail /etc/services |awk '/blp5/ && /tcp/{print $0}' blp5 48129/tcp # Bloomberg locator 匹配記錄中包含blp5或tcp的行: #tail /etc/services |awk '/blp5/ || /tcp/{print $0}' 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw iqobject 48619/tcp # iqobject matahari 49000/tcp # Matahari Broker 不匹配開頭是#和空行: # awk'! /^#/ && ! /^$/{print $0}' /etc/httpd/conf/httpd.conf 或 # awk'! /^#|^$/' /etc/httpd/conf/httpd.conf 或 # awk'/^[^#]|"^$"/' /etc/httpd/conf/httpd.conf
9)匹配范圍
# tail /etc/services |awk '/^blp5/,/^com/' blp5 48129/tcp # Bloomberg locator blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
變量名 | 描述 |
FS | 輸入字段分隔符,默認是空格或制表符 |
OFS | 輸出字段分隔符,默認是空格 |
RS | 輸入記錄分隔符,默認是換行符\n |
ORS | 輸出記錄分隔符,默認是換行符\n |
NF | 統計當前記錄中字段個數 |
NR | 統計記錄編號,每處理一行記錄,編號就會+1 |
FNR | 統計記錄編號,每處理一行記錄,編號也會+1,與NR不同的是,處理第二個文件時,編號會重新計數。 |
ARGC | 命令行參數數量 |
ARGIND | 當前正在處理的文件索引值。第一個文件是1,第二個文件是2,以此類推 |
ARGV | 命令行參數數組序列數組,下標從0開始,ARGV[0]是awk |
ENVIRON | 當前系統的環境變量 |
FILENAME | 輸出當前處理的文件名 |
IGNORECASE | 忽略大小寫 |
SUBSEP | 數組中下標的分隔符,默認為"\034" |
示例:
1)FS和OFS
在程序開始前重新賦值FS變量,改變默認分隔符為冒號,與-F一樣。
# awk 'BEGIN{FS=":"}{print $1,$2}' /etc/passwd |head -n5 rootx bin x daemonx adm x lp x 也可以使用-v來重新賦值這個變量: # awk -vFS=':' '{print $1,$2}' /etc/passwd |head -n5 # 中間逗號被換成了OFS的默認值 rootx bin x daemonx adm x lp x 由于OFS默認以空格分隔,反向引用多個字段分隔的也是空格,如果想指定輸出分隔符這樣: # awk 'BEGIN{FS=":";OFS=":"}{print $1,$2}' /etc/passwd |head -n5 root:x bin:x daemon:x adm:x lp:x 也可以通過字符串拼接實現分隔: # awk 'BEGIN{FS=":"}{print $1"#"$2}' /etc/passwd |head -n5 root#x bin#x daemon#x adm#x lp#x
2)RS和ORS
RS默認是\n分隔每行,如果想指定以某個字符作為分隔符來處理記錄:
# echo "www.baidu.com/user/test.html" |awk'BEGIN{RS="/"}{print $0}' www.baidu.com user test.html RS也支持正則,簡單演示下: # seq-f "str%02g" 10 |sed 'n;n;a\-----' |awk 'BEGIN{RS="-+"}{print$1}' str01 str04 str07 str10 將輸出的換行符替換為+號: # seq10 |awk 'BEGIN{ORS="+"}{print $0}' 1+2+3+4+5+6+7+8+9+10+ 替換某個字符: #tail -n2 /etc/services |awk 'BEGIN{RS="/";ORS="#"}{print$0}' iqobject 48619#udp # iqobject matahari 49000#tcp # Matahari Broker
3)NF
NF是打印字段個數。
# echo "a b c d e f" |awk '{print NF}' 6 打印最后一個字段: # echo "a b c d e f" |awk '{print $NF}' f 打印倒數第二個字段: # echo "a b c d e f" |awk '{print $(NF-1)}' e 排除最后兩個字段: # echo "a b c d e f" |awk '{$NF="";$(NF-1)="";print$0}' a b cd 排除第一個字段: # echo "a b c d e f" |awk '{$1="";print $0}' bc d e f
4)NR和FNR
NR統計記錄編號,每處理一行記錄,編號就會+1,FNR不同的是在統計第二個文件時會重新計數。
打印行數: # tail -n5 /etc/services |awk '{print NR,$0}' 1 com-bardac-dw 48556/tcp # com-bardac-dw 2 com-bardac-dw 48556/udp # com-bardac-dw 3 iqobject 48619/tcp # iqobject 4 iqobject 48619/udp # iqobject 5 matahari 49000/tcp # Matahari Broker 打印總行數: # tail -n5 /etc/services |awk 'END{print NR}' 5 打印第三行: # tail -n5 /etc/services |awk 'NR==3' iqobject 48619/tcp # iqobject 打印第三行第二個字段: # tail -n5 /etc/services |awk 'NR==3{print $2}' 48619/tcp 打印前三行: # tail -n5 /etc/services |awk 'NR<=3{print NR,$0}' 1 com-bardac-dw 48556/tcp # com-bardac-dw 2 com-bardac-dw 48556/udp # com-bardac-dw 3 iqobject 48619/tcp # iqobject
看下NR和FNR的區別:
# cat a a b c # cat b c d e # awk'{print NR,FNR,$0}' a b 1 1 a 2 2 b 3 3 c 4 1 c 5 2 d 6 3 e
可以看出NR每處理一行就會+1,而FNR在處理第二個文件時,編號重新計數。同時也知道awk處理兩個文件時,是合并到一起處理。
# awk 'FNR==NR{print $0"1"}FNR!=NR{print $0"2"}' a b a1 b1 c1 c2 d2 e2
當FNR==NR時,說明在處理第一個文件內容,不等于時說明在處理第二個文件內容。
一般FNR在處理多個文件時會用到,下面會講解。
5)ARGC和ARGV
ARGC是命令行參數數量
ARGV是將命令行參數存到數組,元素由ARGC指定,數組下標從0開始
# awk 'BEGIN{print ARGC}' 1 2 3 4 # awk 'BEGIN{print ARGV[0]}' awk # awk 'BEGIN{print ARGV[1]}' 1 2 1 # awk 'BEGIN{print ARGV[2]}' 1 2 2
6)ARGIND
ARGIND是當前正在處理的文件索引值,第一個文件是1,第二個文件是2,以此類推,從而可以通過這種方式判斷正在處理哪個文件。
# awk '{print ARGIND,$0}' a b 1 a 1 b 1 c 2 c 2 d 2 e # awk 'ARGIND==1{print "a->"$0}ARGIND==2{print "b->"$0}'a b a->a a->b a->c b->c b->d b->e
7)ENVIRON
ENVIRON調用系統變量。
# awk 'BEGIN{print ENVIRON["HOME"]}' /root 如果是設置的環境變量,還需要用export導入到系統變量才可以調用: # awk'BEGIN{print ENVIRON["a"]}' # export a # awk 'BEGIN{print ENVIRON["a"]}' 123
8)FILENAME
FILENAME是當前處理文件的文件名。
# awk 'FNR==NR{print FILENAME"->"$0}FNR!=NR{printFILENAME"->"$0}' a b a->a a->b a->c b->c b->d b->e 9)忽略大小寫 # echo "A a b c" |xargs -n1 |awk 'BEGIN{IGNORECASE=1}/a/' A a
等于1代表忽略大小寫。
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
運算符 | 描述 |
(....) | 分組 |
$ | 字段引用 |
++ -- | 遞增和遞減 |
+ - ! | 加號,減號,和邏輯否定 |
* / % | 乘,除和取余 |
+ - | 加法,減法 |
| |& | 管道,用于getline,print和printf |
< > <= >= != == | 關系運算符 |
~ !~ | 正則表達式匹配,否定正則表達式匹配 |
in | 數組成員 |
&& || | 邏輯and,邏輯or |
?: | 簡寫條件表達式: expr1 ? expr2 : expr3 第一個表達式為真,執行expr2,否則執行expr3 |
= += -= *= /= %= ^= | 變量賦值運算符 |
須知:在awk中,有3種情況表達式為假:數字是0,空字符串和未定義的值
數值運算,未定義變量初始值為0。字符運算,未定義變量初始值為空。
舉例測試:
# awk 'BEGIN{n=0;if(n)print"true";else print "false"}' false # awk'BEGIN{s="";if(s)print "true";else print"false"}' false # awk'BEGIN{if(s)print "true";else print "false"}' false
示例:
1)截取整數
# echo "123abc abc123 123abc123"|xargs -n1 | awk '{print +$0}' 123 0 123 # echo "123abc abc123 123abc123"|xargs -n1 | awk '{print -$0}' -123 0 -123
2)感嘆號
打印奇數行: # seq 6 |awk 'i=!i' 1 3 5 讀取第一行,i是未定義變量,也就是i=!0,!取反意思。感嘆號右邊是個布爾值,0或空字符串為假,非0或非空字符串為真,!0就是真,因此i=1,條件為真打印當前記錄。 沒有print為什么會打印呢?因為模式后面沒有動作,默認會打印整條記錄。 讀取第二行,因為上次i的值由0變成了1,此時就是i=!1,條件為假不打印。 讀取第三行,上次條件又為假,i恢復初始值0,取反,繼續打印。以此類推... 可以看出,運算時并沒有判斷行內容,而是利用布爾值真假判斷輸出當前行。 打印偶數行: # seq 6 |awk '!(i=!i)' 2 4 6
2)不匹配某行
# tail /etc/services |awk '!/blp5/{print$0}' 3gpp-cbsp 48049/tcp # 3GPPCell Broadcast Service isnetserv 48128/tcp # Image Systems NetworkServices isnetserv 48128/udp # ImageSystems Network Services com-bardac-dw 48556/tcp # com-bardac-dw com-bardac-dw 48556/udp # com-bardac-dw iqobject 48619/tcp # iqobject iqobject 48619/udp # iqobject matahari 49000/tcp # MatahariBroker
3)乘法和除法
# seq 5 |awk '{print $0*2}' 2 4 6 8 10 # seq 5 |awk '{print $0%2}' 1 0 1 0 1 打印偶數行: # seq 5 |awk '$0%2==0{print $0}' 2 4 打印奇數行: # seq 5 |awk '$0%2!=0{print $0}' 1 3 5
4)管道符使用
# seq 5 |shuf |awk '{print$0|"sort"}' 1 2 3 4 5
5)正則表達式匹配
# seq 5 |awk '$0~3{print $0}' 3 # seq 5 |awk '$0!~3{print $0}' 1 2 4 5 # seq 5 |awk '$0~/[34]/{print $0}' 3 4 # seq 5 |awk '$0!~/[34]/{print $0}' 1 2 5 # seq 5 |awk '$0~/[^34]/{print $0}' 1 2 5
6)判斷數組成員
# awk'BEGIN{a["a"]=123}END{if("a" in a)print "yes"}'</dev/null yes
7)三目運算符
# awk 'BEGIN{print1==1?"yes":"no"}' # 三目運算作為一個表達式,里面不允許寫print yes # seq 3 |awk '{print$0==2?"yes":"no"}' no yes no 替換換行符為逗號: # seq 5 |awk '{printn=(n?n","$0:$0)}' 1 1,2 1,2,3 1,2,3,4 1,2,3,4,5 # seq 5 |awk'{n=(n?n","$0:$0)}END{print n}' 1,2,3,4,5 說明:讀取第一行時,n沒有變量,為假輸出$0也就是1,并賦值變量n,讀取第二行時,n是1為真,輸出1,2 以此類推,后面會一直為真。 每三行后面添加新一行: # seq 10 |awk '{print NR%3?$0:$0"\ntxt"}' 1 2 3 txt 4 5 6 txt 7 8 9 txt 10 在 兩行合并一行: # seq 6 |awk '{printf NR%2!=0?$0"":$0" \n"}' 1 2 3 4 5 6 # seq 6 |awk 'ORS=NR%2?"":"\n"' 1 2 3 4 5 6 # seq 6 |awk '{if(NR%2)ORS="";else ORS="\n";print}'
8)變量賦值
字段求和: # seq 5 |awk '{sum+=1}END{print sum}' 5 # seq 5 |awk '{sum+=$0}END{print sum}' 15
1)if語句
格式:if(condition) statement [ else statement ]
單分支: # seq5 |awk '{if($0==3)print $0}' 3 雙分支: # seq5 |awk '{if($0==3)print $0;else print "no"}' no no 3 no no 多分支: # catfile 1 2 3 4 5 6 7 8 9 # awk'{if($1==4){print "1"} else if($2==5){print "2"} elseif($3==6){print "3"} else {print "no"}}' file no 1 no
2)while語句
格式:while(condition) statement
遍歷打印所有字段: # awk'{i=1;while(i<=NF){print $i;i++}}' file 1 2 3 4 5 6 7 8 9 awk是按行處理的,每次讀取一行,并遍歷打印每個字段。
3)for語句C語言風格
格式:for(expr1; expr2; expr3) statement
遍歷打印所有字段: # catfile 1 2 3 4 5 6 7 8 9 # awk'{for(i=1;i<=NF;i++)print $i}' file 1 2 3 4 5 6 7 8 9 倒敘打印文本: # awk'{for(i=NF;i>=1;i--)print $i}' file 3 2 1 6 5 4 9 8 7 都換行了,這并不是我們要的結果。怎么改進呢? # awk'{for(i=NF;i>=1;i--){printf $i" "};print ""}' file # print本身就會新打印一行 3 2 1 6 5 4 9 8 7 或 # awk'{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i""}' file 3 2 1 6 5 4 6 5 4 9 8 7 在這種情況下,是不是就排除第一行和倒數第一行呢?我們正序打印看下 排除第一行: # awk'{for(i=2;i<=NF;i++){printf $i" "};print ""}' file 2 3 5 6 8 9 排除第二行: # awk'{for(i=1;i<=NF-1;i++){printf $i" "};print ""}' file 1 2 4 5 7 8 IP加單引號: #echo '10.10.10.1 10.10.10.2 10.10.10.3' |awk '{for(i=1;i<=NF;i++)printf"\047"$i"\047"} '10.10.10.1' '10.10.10.2' '10.10.10.3' \047是ASCII碼,可以通過showkey -a命令查看。 4)for語句遍歷數組 格式:for(var in array) statement # seq-f "str%.g" 5 |awk '{a[NR]=$0}END{for(v in a)print v,a[v]}' 4 str4 5 str5 1 str1 2 str2 3 str3
5)break和continue語句
break跳過所有循環,continue跳過當前循環。
# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){break};print i}}' 1 2 # awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){continue};print i}}' 1 2 4 5
6)刪除數組和元素
格式:
deletearray[index] 刪除數組元素
deletearray 刪除數組
# seq-f "str%.g" 5 |awk '{a[NR]=$0}END{delete a;for(v in a)print v,a[v]}' 空的… # seq-f "str%.g" 5 |awk '{a[NR]=$0}END{delete a[3];for(v in a)printv,a[v]}' 4 str4 5 str5 1 str1 2 str2
7)exit語句
格式:exit[ expression ]
exit退出程序,與shell的exit一樣。[ expr]是0-255之間的數字。
# seq5 |awk '{if($0~/3/)exit (123)}' # echo $? 123
數組是用來存儲一系列值的變量,通過下標(索引)來訪問值。
awk中數組稱為關聯數組,不僅可以使用數字作為下標,還可以使用字符串作為下標。
數組元素的鍵和值存儲在awk程序內部的一個表中,該表采用散列算法,因此數組元素是隨機排序。
數組格式:array[index]=value
1)自定義數組
# awk 'BEGIN{a[0]="test";print a[0]}' test
2)通過NR設置記錄下標,下標從1開始
# tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[1]}' systemd-network # tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[2]}' zabbix # tail -n3 /etc/passwd |awk -F: '{a[NR]=$1}END{print a[3]}' user
3)通過for循環遍歷數組
# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(v in a)print a[v],v}' zabbix4 user5 admin1 systemd-bus-proxy2 systemd-network3 # tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(i=1;i<=NR;i++)printa[i],i}' admin1 systemd-bus-proxy2 systemd-network3 zabbix4 user5
上面打印的i是數組的下標。
第一種for循環的結果是亂序的,剛說過,數組是無序存儲。
第二種for循環通過下標獲取的情況是排序正常。
所以當下標是數字序列時,還是用for(expr1;expr2;expr3)循環表達式比較好,保持順序不變。
4)通過++方式作為下標
# tail -n5 /etc/passwd |awk -F: '{a[x++]=$1}END{for(i=0;i<=x-1;i++)printa[i],i}' admin0 systemd-bus-proxy1 systemd-network2 zabbix3 user4
x被awk初始化值是0,沒循環一次+1
5)使用字段作為下標
# tail -n5 /etc/passwd |awk -F: '{a[$1]=$7}END{for(v in a)print a[v],v}' /sbin/nologinadmin /bin/bashuser /sbin/nologinsystemd-network /sbin/nologinsystemd-bus-proxy /sbin/nologinzabbix
6)統計相同字段出現次數
# tail /etc/services |awk '{a[$1]++}END{for(v in a)print a[v],v}' 2com-bardac-dw 13gpp-cbsp 2iqobject 1matahari 2isnetserv 2blp5 # tail /etc/services |awk '{a[$1]+=1}END{for(v in a)print a[v],v}' 2com-bardac-dw 13gpp-cbsp 2iqobject 1matahari 2isnetserv 2blp5 #tail /etc/services |awk '/blp5/{a[$1]++}END{for(v in a)print a[v],v}' 2blp5
第一個字段作為下標,值被++初始化是0,每次遇到下標(第一個字段)一樣時,對應的值就會被+1,因此實現了統計出現次數。
想要實現去重的的話就簡單了,只要打印下標即可。
7)統計TCP連接狀態
# netstat -antp |awk '/^tcp/{a[$6]++}END{for(v in a)print a[v],v}' 9LISTEN 6ESTABLISHED 6TIME_WAIT
8)只打印出現次數大于等于2的
# tail /etc/services |awk '{a[$1]++}END{for(v in a) if(a[v]>=2){printa[v],v}}' 2com-bardac-dw 2iqobject 2isnetserv 2blp5
9)去重
只打印重復的行: # tail /etc/services |awk 'a[$1]++' isnetserv 48128/udp # Image Systems Network Services blp5 48129/udp # Bloomberg locator com-bardac-dw 48556/udp # com-bardac-dw iqobject 48619/udp # iqobject 去重: # tail /etc/services |awk '!a[$1]++' 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Services blp5 48129/tcp # Bloomberg locator com-bardac-dw 48556/tcp # com-bardac-dw iqobject 48619/tcp # iqobject matahari 49000/tcp # Matahari Broker
只打印重復的行說明:先明白一個情況,當值是0是為假,1為真,知道這點就不難理解了。由于執行了++當處理第一條記錄時,初始值是0為假,就不打印,如果再遇到相同的記錄,值就會+1,不為0,打印。
去重說明:初始值是0為假,感嘆號取反為真,打印,也就是說,每個記錄的第一個值都是為0,所以都會打印,如果再遇到相同的記錄+1,值就會為真,取反為假就不打印。
# tail /etc/services |awk '{if(a[$1]++)print $1}' isnetserv blp5 com-bardac-dw iqobject 使用三目運算: # tail /etc/services |awk '{print a[$1]++?$1:"no"}' no no isnetserv no blp5 no com-bardac-dw no iqobject no # tail /etc/services |awk '{if(!a[$1]++)print $1}' 3gpp-cbsp isnetserv blp5 com-bardac-dw iqobject matahari
10)統計每個相同字段的某字段總數:
# tail /etc/services |awk -F'[ /]+' '{a[$1]+=$2}END{for(v in a)print v, a[v]}' com-bardac-dw97112 3gpp-cbsp48049 iqobject97238 matahari49000 isnetserv96256 blp596258
11)多維數組
awk的多維數組,實際上awk并不支持多維數組,而是邏輯上模擬二維數組的訪問方式,比如a[a,b]=1,使用SUBSEP(默認\034)作為分隔下標字段,存儲后是這樣a\034b。
示例:
# awk 'BEGIN{a["x","y"]=123;for(v in a) print v,a[v]}' xy123 我們可以重新復制SUBSEP變量,改變下標默認分隔符: # awk 'BEGIN{SUBSEP=":";a["x","y"]=123;for(v in a)print v,a[v]}' x:y123 根據指定的字段統計出現次數: # cata A 192.168.1.1 HTTP B 192.168.1.2 HTTP B 192.168.1.2 MYSQL C 192.168.1.1 MYSQL C 192.168.1.1 MQ D 192.168.1.4 NGINX # awk 'BEGIN{SUBSEP="-"}{a[$1,$2]++}END{for(v in a)print a[v],v}' a 1 D-192.168.1.4 1 A-192.168.1.1 2 C-192.168.1.1 2 B-192.168.1.2
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
函數 | 描述 |
int(expr) | 截斷為整數 |
sqrt(expr) | 平方根 |
rand() | 返回一個隨機數N,0和1范圍,0 < N < 1 |
srand([expr]) | 使用expr生成隨機數,如果不指定,默認使用當前時間為種子,如果前面有種子則使用生成隨機數 |
asort(a, b) | 對數組a的值進行排序,把排序后的值存到新的數組b中,新排序的數組下標從1開始 |
asorti(a,b) | 對數組a的下標進行排序,同上 |
sub(r, s [, t]) | 對輸入的記錄用s替換r,t可選針對某字段替換 ,但只替換第一個字符串 |
gsub(r,s [, t]) | 對輸入的記錄用s替換r,t可選針對某字段替換,替換所有字符串 |
index(s, t) | 返回s中字符串t的索引位置,0為不存在 |
length([s]) | 返回s的長度 |
match(s, r [, a]) | 測試字符串s是否包含匹配r的字符串 |
split(s, a [, r [, seps] ]) | 根據分隔符seps將s分成數組a |
substr(s, i [, n]) | 截取字符串s從i開始到長度n,如果n沒指定則是剩余部分 |
tolower(str) | str中的所有大寫轉換成小寫 |
toupper(str) | str中的所有小寫轉換成大寫 |
systime() | 當前時間戳 |
strftime([format [, timestamp[, utc-flag]]]) | 格式化輸出時間,將時間戳轉為字符串 |
示例:
1)int()
# echo "123abc abc123 123abc123"|xargs -n1 | awk '{print int($0)}' 123 0 123 # awk 'BEGIN{print int(10/3)}' 3
2)sqrt()
獲取9的平方根:
# awk 'BEGIN{print sqrt(9)}' 3
3)rand()和srand()
rand()并不是每次運行就是一個隨機數,會一直保持一個不變: # awk 'BEGIN{print rand()}' 0.237788 當執行srand()函數后,rand()才會發生變化,所以一般在awk著兩個函數結合生成隨機數,但是也有很大幾率生成一樣: # awk 'BEGIN{srand();print rand()}' 0.31687 如果想生成1-10的隨機數可以這樣: # awk 'BEGIN{srand();print int(rand()*10)}' 4
如果想更完美生成隨機數,還得做相應的處理!
4)asort()和asorti()
# seq -f "str%.g" 5 |awk'{a[x++]=$0}END{s=asort(a,b);for(i=1;i<=s;i++)print b[i],i}' str1 1 str2 2 str3 3 str4 4 str5 5 # seq -f "str%.g" 5 |awk'{a[x++]=$0}END{s=asorti(a,b);for(i=1;i<=s;i++)print b[i],i}' 0 1 1 2 2 3 3 4 4 5
asort將a數組的值放到數組b,a下標丟棄,并將數組b的總行號賦值給s,新數組b下標從1開始,然后遍歷。
5)sub()和gsub()
# tail /etc/services |awk'/blp5/{sub(/tcp/,"icmp");print $0}' blp5 48129/icmp #Bloomberg locator blp5 48129/udp #Bloomberg locator # tail /etc/services |awk'/blp5/{gsub(/c/,"9");print $0}' blp5 48129/t9p #Bloomberg lo9ator blp5 48129/udp #Bloomberg lo9ator # echo "1 2 2 3 4 5" |awk 'gsub(2,7,$2){print$0}' 1 7 2 3 4 5 # echo "1 2 3 a b c" |awk'gsub(/[0-9]/, '0'){print $0}' 0 0 0 a b c
在指定行前后加一行:
# seq 5 | awk'NR==2{sub('/.*/',"txt\n&")}{print}' 1 txt 2 3 4 5 # seq 5 | awk'NR==2{sub('/.*/',"&\ntxt")}{print}' 1 2 txt 3 4 5
6)index()
# tail -n 5 /etc/services |awk '{printindex($2,"tcp")}' 7 0 7 0 7
7)length()
# tail -n 5 /etc/services |awk '{printlength($2)}' 9 9 9 9 9 統計數組的長度: # tail -n 5 /etc/services |awk'{a[$1]=$2}END{print length(a)}' 3
8)split()
# echo -e "123#456#789\nabc#cde#fgh"|awk '{split($0,a);for(v in a)print a[v],v}' 123#456#789 1 abc#cde#fgh 1 # echo -e"123#456#789\nabc#cde#fgh" |awk '{split($0,a,"#");for(v ina)print a[v],v}' 123 1 456 2 789 3 abc 1 cde 2 fgh 3
9)substr()
# echo -e "123#456#789\nabc#cde#fgh"|awk '{print substr($0,4)}' #456#789 #cde#fgh # echo -e"123#456#789\nabc#cde#fgh" |awk '{print substr($0,4,5)}' #456# #cde#
10)tolower()和toupper()
# echo -e"123#456#789\nABC#cde#fgh" |awk '{print tolower($0)}' 123#456#789 abc#cde#fgh # echo -e"123#456#789\nabc#cde#fgh" |awk '{print toupper($0)}' 123#456#789 ABC#CDE#FGH
11)時間處理
返回當前時間戳: # awk 'BEGIN{print systime()}' 1483297766 將時間戳轉為日期和時間 # echo "1483297766" |awk '{printstrftime("%Y-%m-%d %H:%M:%S",$0)}' 2017-01-01 14:09:26
語句 | 描述 |
getline | 設置$0來自下一個輸入記錄 |
getline var | 設置var來自下一個輸入記錄 |
command | getline [var] | 運行命令管道輸出到$0或var |
next | 停止當前處理的輸入記錄 |
打印當前記錄 | |
printf fmt, expr-list | 格式化輸出 |
printf fmt, expr-list >file | 格式輸出和寫到文件 |
system(cmd-line) | 執行命令和返回狀態 |
print ... >> file | 追加輸出到文件 |
print ... | command | 打印輸出作為命令輸入 |
示例:
1)getline
獲取匹配的下一行: # seq 5 |awk'/3/{getline;print}' 4 # seq 5 |awk'/3/{print;getline;print}' 3 4 在匹配的下一行加個星號: # seq 5 |awk'/3/{getline;sub(".*","&*");print}' 4* # seq 5 |awk'/3/{print;getline;sub(".*","&*")}{print}' 1 2 3 4* 5
2)getline var
把a文件的行追加到b文件的行尾: # cat a a b c # cat b 1 one 2 two 3 three # awk '{getlineline<"a";print $0,line}' b 1 one a 2 two b 3 three c 把a文件的行替換b文件的指定字段: # awk '{getlineline<"a";gsub($2,line,$2);print}' b 1 a 2 b 3 c 把a文件的行替換b文件的對應字段: # awk '{getlineline<"a";gsub("two",line,$2);print}' b 1 one 2 b 3 three 3)command| getline [var] 獲取執行shell命令后結果的第一行: # awk 'BEGIN{"seq 5"|getline var;print var}' 1 循環輸出執行shell命令后的結果: # awk 'BEGIN{while("seq 5"|getline)print}' 1 2 3 4 5
4)next
不打印匹配行: # seq 5 |awk '{if($0==3){next}else{print}}' 1 2 4 5 刪除指定行: # seq 5 |awk 'NR==1{next}{print $0}' 2 3 4 5 如果前面動作成功,就遇到next,后面的動作不再執行,跳過。 或者: # seq 5 |awk 'NR!=1{print}' 2 3 4 5 把第一行內容放到每行的前面: # cat a hello 1 a 2 b 3 c # awk 'NR==1{s=$0;next}{print s,$0}' a hello 1 a hello 2 b hello 3 c # awk 'NR==1{s=$0}NF!=1{print s,$0}' a hello 1 a hello 2 b hello 3 c
5)system()
執行shell命令判斷返回值: # awk 'BEGIN{if(system("grep root /etc/passwd &>/dev/null")==0)print"yes";else print "no"}' yes
6)打印結果寫到文件
# tail -n5 /etc/services |awk '{print $2 > "a.txt"}' # cat a.txt 48049/tcp 48128/tcp 48128/udp 48129/tcp 48129/udp
7)管道連接shell命令
將結果通過grep命令過濾: # tail -n5 /etc/services |awk '{print $2|"grep tcp"}' 48556/tcp 48619/tcp 49000/tcp
格式化輸出,默認打印字符串不換行。
格式:printf [format] arguments
Format | 描述 |
%s | 一個字符串 |
%d,%i | 一個小數 |
%f | 一個浮點數 |
%.ns | 輸出字符串,n是輸出幾個字符 |
%ni | 輸出整數,n是輸出幾個數字 |
%m.nf | 輸出浮點數,m是輸出整數位數,n是輸出的小數位數 |
%x | 不帶正負號的十六進制,使用a至f表示10到15 |
%X | 不帶正負號的十六進制,使用A至F表示10至15 |
%% | 輸出單個% |
%-5s | 左對齊,對參數每個字段左對齊,寬度為5 |
%-4.2f | 左對齊,寬度為4,保留兩位小數 |
%5s | 右對齊,不加橫線表示右對齊 |
示例:
將換行符換成逗號: # seq 5 |awk'{if($0!=5)printf "%s,",$0;else print $0}' 1,2,3,4,5 小括號中的5是最后一個數字。 輸出一個字符: # awk 'BEGIN{printf"%.1s\n","abc"}' a 保留一個小數點: # awk 'BEGIN{printf "%.2f\n",10/3}' 3.33 格式化輸出: # awk 'BEGIN{printf"user:%s\tpass:%d\n","abc",123}' user:abc pass:123 左對齊寬度10: # awk 'BEGIN{printf "%-10s %-10s%-10s\n","ID","Name","Passwd"}' ID Name Passwd 右對齊寬度10: # awk 'BEGIN{printf "%10s %10s %10s\n","ID","Name","Passwd"}' ID Name Passwd 打印表格: # vi test.awk BEGIN{ print"+--------------------+--------------------+"; printf"|%-20s|%-20s|\n","Name","Number"; print"+--------------------+--------------------+"; } # awk -f test.awk +--------------------+--------------------+ |Name |Number | +--------------------+--------------------+ 格式化輸出: # awk -F: 'BEGIN{printf"UserName\t\tShell\n-----------------------------\n"}{printf"%-20s %-20s\n",$1,$7}END{print "END...\n"}' /etc/passwd 打印十六進制: # awk 'BEGIN{printf "%x %X",123,123}' 7b 7B
格式:function name(parameter list) { statements }
示例:
# awk 'function myfunc(a,b){returna+b}BEGIN{print myfunc(1,2)}' 3
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
1)分析Nginx日志
日志格式:'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"'
統計訪問IP次數: # awk '{a[$1]++}END{for(v in a)printv,a[v]}' access.log 統計訪問訪問大于100次的IP: # awk '{a[$1]++}END{for(v ina){if(a[v]>100)print v,a[v]}}' access.log 統計訪問IP次數并排序取前10: # awk '{a[$1]++}END{for(v in a)print v,a[v]|"sort -k2 -nr |head -10"}' access.log 統計時間段訪問最多的IP: # awk'$4>="[02/Jan/2017:00:02:00" &&$4<="[02/Jan/2017:00:03:00"{a[$1]++}END{for(v in a)print v,a[v]}'access.log 統計上一分鐘訪問量: # date=$(date -d '-1 minute'+%d/%d/%Y:%H:%M) # awk -vdate=$date '$4~date{c++}END{printc}' access.log 統計訪問最多的10個頁面: # awk '{a[$7]++}END{for(vin a)print v,a[v]|"sort -k1 -nr|head -n10"}' access.log 統計每個URL數量和返回內容總大小: # awk '{a[$7]++;size[$7]+=$10}END{for(v ina)print a[v],v,size[v]}' access.log 統計每個IP訪問狀態碼數量: # awk '{a[$1" "$9]++}END{for(v ina)print v,a[v]}' access.log 統計訪問IP是404狀態次數: # awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)printv,a[v]}' access.log
2)兩個文件對比
找出b文件在a文件相同記錄:
# seq 1 5 > a # seq 3 7 > b 方法1: # awk 'FNR==NR{a[$0];next}{if($0 in a)print$0}' a b 3 4 5 # awk 'FNR==NR{a[$0];next}{if($0 in a)printFILENAME,$0}' a b b 3 b 4 b 5 # awk 'FNR==NR{a[$0]}NR>FNR{if($0 ina)print $0}' a b 3 4 5 # awk 'FNR==NR{a[$0]=1;next}(a[$0]==1)' a b # a[$0]是通過b文件每行獲取值,如果是1說明有 # awk 'FNR==NR{a[$0]=1;next}{if(a[$0]==1)print}' a b 3 4 5 方法2: # awk 'FILENAME=="a"{a[$0]}FILENAME=="b"{if($0 in a)print $0}' ab 3 4 5 方法3: # awk 'ARGIND==1{a[$0]=1}ARGIND==2 &&a[$0]==1' a b 3 4 5 找出b文件在a文件不同記錄: 方法1: # awk 'FNR==NR{a[$0];next}!($0 in a)' ab 6 7 # 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 6 7 方法2: # awk'FILENAME=="a"{a[$0]=1}FILENAME=="b" && a[$0]!=1' ab 方法3: # awk 'ARGIND==1{a[$0]=1}ARGIND==2&& a[$0]!=1' a b
3)合并兩個文件
將a文件合并到b文件:
# cat a zhangsan 20 lisi 23 wangwu 29 # cat b zhangsan man lisi woman wangwu man # awk 'FNR==NR{a[$1]=$0;next}{printa[$1],$2}' a b zhangsan 20 man lisi 23 woman wangwu 29 man # awk 'FNR==NR{a[$1]=$0}NR>FNR{printa[$1],$2}' a b zhangsan 20 man lisi 23 woman wangwu 29 man
將a文件相同IP的服務名合并:
# cat a 192.168.1.1: httpd 192.168.1.1: tomcat 192.168.1.2: httpd 192.168.1.2: postfix 192.168.1.3: mysqld 192.168.1.4: httpd # awk 'BEGIN{FS=":";OFS=":"}{a[$1]=a[$1] $2}END{for(v in a)printv,a[v]}' a 192.168.1.4: httpd 192.168.1.1: httpd tomcat 192.168.1.2: httpd postfix 192.168.1.3: mysqld
說明:數組a存儲是$1=a[$1] $2,第一個a[$1]是以第一個字段為下標,值是a[$1] $2,也就是$1=a[$1] $2,值的a[$1]是用第一個字段為下標獲取對應的值,但第一次數組a還沒有元素,那么a[$1]是空值,此時數組存儲是192.168.1.1=httpd,再遇到192.168.1.1時,a[$1]通過第一字段下標獲得上次數組的httpd,把當前處理的行第二個字段放到上一次同下標的值后面,作為下標192.168.1.1的新值。此時數組存儲是192.168.1.1=httpd tomcat。每次遇到相同的下標(第一個字段)就會獲取上次這個下標對應的值與當前字段并作為此下標的新值。
4)將第一列合并到一行
# cat file 1 2 3 4 5 6 7 8 9 # awk '{for(i=1;i<=NF;i++)a[i]=a[i]$i" "}END{for(vin a)print a[v]}' file 1 4 7 2 5 8 3 6 9
說明:
for循環是遍歷每行的字段,NF等于3,循環3次。
讀取第一行時:
第一個字段:a[1]=a[1]1" " 值a[1]還未定義數組,下標也獲取不到對應的值,所以為空,因此a[1]=1 。
第二個字段:a[2]=a[2]2" " 值a[2]數組a已經定義,但沒有2這個下標,也獲取不到對應的值,為空,因此a[2]=2 。
第三個字段:a[3]=a[3]3" " 值a[2]與上面一樣,為空,a[3]=3 。
讀取第二行時:
第一個字段:a[1]=a[1]4" " 值a[2]獲取數組a的2為下標對應的值,上面已經有這個下標了,對應的值是1,因此a[1]=1 4
第二個字段:a[2]=a[2]5" " 同上,a[2]=2 5
第三個字段:a[3]=a[3]6" " 同上,a[2]=3 6
讀取第三行時處理方式同上,數組最后還是三個下標,分別是1=1 4 7,2=2 5 8,3=36 9。最后for循環輸出所有下標值。
5)字符串拆分,統計出現的次數
字符串拆分:
方法1: # echo "hello world" |awk -F '''{print $1}' h # echo "hello" |awk -F '''{for(i=1;i<=NF;i++)print $i}' h e l l o 方法2: # echo "hello" |awk'{split($0,a,"''");for(v in a)print a[v]}' l o h e l
統計字符串中每個字母出現的次數:
# echo "a.b.c,c.d.e" |awk -F'[.,]' '{for(i=1;i<=NF;i++)a[$i]++}END{for(v in a)print v,a[v]}' a 1 b 1 c 2 d 1 e 1
5)費用統計
# cat a 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]++;cost[$1]+=$2;number[$1]+=$3}END{for(v in name)printv,cost[v],number[v]}' a zhangsan 5000 1 lisi 3000 2 wangwu 1500 1 zhaoliu 11000 3
6)獲取數字字段最大值
# cat a a b 1 c d 2 e f 3 g h 3 i j 2 獲取第三字段最大值: # awk 'BEGIN{max=0}{if($3>max)max=$3}END{printmax}' a 3 打印第三字段最大行: # awk 'BEGIN{max=0}{a[$0]=$3;if($3>max)max=$3}END{for(v in a)print v,a[v],max}' a g h 3 3 3 e f 3 3 3 c d 2 2 3 a b 1 1 3 i j 2 2 3 # awk 'BEGIN{max=0}{a[$0]=$3;if($3>max)max=$3}END{for(v in a)if(a[v]==max)print v}'a g h 3 e f 3
7)去除第一行和最后一行
# seq 5 |awk'NR>2{print s}{s=$0}' 2 3 4
讀取第一行,NR=1,不執行print s,s=1
讀取第二行,NR=2,不執行print s,s=2 (大于為真)
讀取第三行,NR=3,執行print s,此時s是上一次p賦值內容2,s=3
最后一行,執行print s,打印倒數第二行,s=最后一行
獲取Nginx負載均衡配置端IP和端口:
# cat a upstreamexample-servers1 { server 127.0.0.1:80 weight=1 max_fails=2fail_timeout=30s; } upstreamexample-servers2 { server 127.0.0.1:80 weight=1 max_fails=2fail_timeout=30s; server 127.0.0.1:82 backup; } # awk '/example-servers1/,/}/{if(NR>2){print s}{s=$2}}' a 127.0.0.1:80 # awk '/example-servers1/,/}/{if(i>1)print s;s=$2;i++}' a # awk '/example-servers1/,/}/{if(i>1){print s}{s=$2;i++}}' a 127.0.0.1:80
讀取第一行,i初始值為0,0>1為假,不執行print s,x=example-servers1,i=1
讀取第二行,i=1,1>1為假,不執行prints,s=127.0.0.1:80,i=2
讀取第三行,i=2,2>1為真,執行prints,此時s是上一次s賦值內容127.0.0.1:80,i=3
最后一行,執行print s,打印倒數第二行,s=最后一行。
這種方式與上面一樣,只是用i++作為計數器。
好了,AWK大部分知識點都在這了,能否提高你逼格呢?
如有問題歡迎加入Python運維開發群(249171211)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。