防偽碼:鋤禾日當(dāng)午,汗滴禾下土。
8.3 awk
awk 是一個處理文本的編程語言工具,能用簡短的程序處理標準輸入或文件、數(shù)據(jù)排序、計算以及
生成報表等等。
在 Linux 系統(tǒng)下默認 awk 是 gawk,它是 awk 的 GNU 版本??梢酝ㄟ^命令查看應(yīng)用的版本:ls -l
/bin/awk
基本的命令語法:awk option 'pattern {action}' file
其中 pattern 表示 AWK 在數(shù)據(jù)中查找的內(nèi)容,而 action 是在找到匹配內(nèi)容時所執(zhí)行的一系列命令。
花括號用于根據(jù)特定的模式對一系列指令進行分組。
awk 處理的工作方式與數(shù)據(jù)庫類似,支持對記錄和字段處理,這也是 grep 和 sed 不能實現(xiàn)的。
在 awk 中,缺省的情況下將文本文件中的一行視為一個記錄,逐行放到內(nèi)存中處理,而將一行中的
某一部分作為記錄中的一個字段。用 1,2,3...數(shù)字的方式順序的表示行(記錄)中的不同字段。用
$后跟數(shù)字,引用對應(yīng)的字段,以逗號分隔,0 表示整個行。
8.3.1 選項
選項 描述
-f program-file 從文件中讀取 awk 程序源文件
-F fs 指定 fs 為輸入字段分隔符
-v var=value 變量賦值
--posix 兼容 POSIX 正則表達式
--dump-variables=[file] 把 awk 命令時的全局變量寫入文件,
默認文件是 awkvars.out
--profile=[file] 格式化 awk 語句到文件,默認是 awkprof.out
8.3.2 模式
常用模式有:
Pattern Description
BEGIN{ } 給程序賦予初始狀態(tài),先執(zhí)行的工作
END{ } 程序結(jié)束之后執(zhí)行的一些掃尾工作
/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
Matahari Broker
# 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
Matahari Broker
# tail -n3 /etc/services |awk -F'[ /]+' '{print $2}'
48619
48619
49000
[]元字符的意思是符號其中任意一個字符,也就是說每遇到一個/或#時就分隔一個字段,當(dāng)用多個
分隔符時,就能更方面處理字段了。
3)變量賦值
# awk -v a=123 'BEGIN{print a}'
123
系統(tǒng)變量作為 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 模式是在處理文件之前執(zhí)行該操作,常用于修改內(nèi)置變量、變量賦值和打印輸出的頁眉或標
題。
例如:打印頁眉
# 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 模式是在程序處理完才會執(zhí)行。
例如:打印頁尾
# 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 Service Protocol
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
如果沒有匹配到,請查看你的 awk 版本(awk --version)是不是 3,因為 4 才支持{}
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
對匹配范圍后記錄再次處理,例如匹配關(guān)鍵字下一行到最后一行:
# seq 5 |awk '/3/,/^$/{printf /3/?"":$0"\n"}'
4
5
另一種判斷真假的方式實現(xiàn):
# seq 5 |awk '/3/{t=1;next}t'
4
5
1 和 2 都不匹配 3,不執(zhí)行后面{},執(zhí)行 t,t 變量還沒賦值,為空,空在 awk 中就為假,就不打印
當(dāng)前行。匹配到 3,執(zhí)行 t=1,next 跳出,不執(zhí)行 t。4 也不匹配 3,執(zhí)行 t,t 的值上次賦值的 1,
為真,打印當(dāng)前行,以此類推。(非 0 的數(shù)字都為真,所以 t 可以寫任意非 0 數(shù)字)
如果想打印匹配行都最后一行,就可以這樣了:
# seq 5 |awk '/3/{t=1}t'
3
4
5
8.3.3 內(nèi)置變量
變量名 描述
FS 輸入字段分隔符,默認是空格或制表符
OFS 輸出字段分隔符,默認是空格
RS 輸入記錄分隔符,默認是換行符\n
ORS 輸出記錄分隔符,默認是換行符\n
NF 統(tǒng)計當(dāng)前記錄中字段個數(shù)
NR 統(tǒng)計記錄編號,每處理一行記錄,編號就會+1
FNR 統(tǒng)計記錄編號,每處理一行記錄,編號也會+1,與 NR 不同的是,處理第二個
文件時,編號會重新計數(shù)。
ARGC 命令行參數(shù)數(shù)量
ARGV 命令行參數(shù)數(shù)組序列數(shù)組,下標從 0 開始,ARGV[0]是 awk
ARGIND 當(dāng)前正在處理的文件索引值。第一個文件是 1,第二個文件是 2,以此類推
ENVIRON 當(dāng)前系統(tǒng)的環(huán)境變量
FILENAME 輸出當(dāng)前處理的文件名
IGNORECASE 忽略大小寫
SUBSEP 數(shù)組中下標的分隔符,默認為"\034"
示例:
1)FS 和 OFS
在程序開始前重新賦值 FS 變量,改變默認分隔符為冒號,與-F 一樣。
# awk 'BEGIN{FS=":"}{print $1,$2}' /etc/passwd |head -n5
root x
bin x
daemon x
adm x
lp x
也可以使用-v 來重新賦值這個變量:
# awk -vFS=':' '{print $1,$2}' /etc/passwd |head -n5 # 中間逗號被換成了 OFS 的默
認值
root x
bin x
daemon x
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
也可以通過字符串拼接實現(xiàn)分隔:
# 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
將輸出的換行符替換為+號:
# seq 10 |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 是字段個數(shù)。
# echo "a b c d e f" |awk '{print NF}'
6
打印最后一個字段:
# echo "a b c d e f" |awk '{print $NF}'
f
打印倒數(shù)第二個字段:
# 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 c d
排除第一個字段:
# echo "a b c d e f" |awk '{$1="";print $0}'
b c d e f
4)NR 和 FNR
NR 統(tǒng)計記錄編號,每處理一行記錄,編號就會+1,F(xiàn)NR 不同的是在統(tǒng)計第二個文件時會重新計數(shù)。
打印行數(shù):
# 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
打印總行數(shù):
# 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 的區(qū)別:
# 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 在處理第二個文件時,編號重新計數(shù)。同時也知道 awk 處理
兩個文件時,是合并到一起處理。
# awk 'FNR==NR{print $0"1"}FNR!=NR{print $0"2"}' a b
a1
b1
c1
c2
d2
e2
當(dāng) FNR==NR 時,說明在處理第一個文件內(nèi)容,不等于時說明在處理第二個文件內(nèi)容。
一般 FNR 在處理多個文件時會用到,下面會講解。
5)ARGC 和 ARGV
ARGC 是命令行參數(shù)數(shù)量
ARGV 是將命令行參數(shù)存到數(shù)組,元素由 ARGC 指定,數(shù)組下標從 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 是當(dāng)前正在處理的文件索引值,第一個文件是 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 調(diào)用系統(tǒng)變量。
# awk 'BEGIN{print ENVIRON["HOME"]}'
/root
如果是設(shè)置的環(huán)境變量,還需要用 export 導(dǎo)入到系統(tǒng)變量才可以調(diào)用:
# awk 'BEGIN{print ENVIRON["a"]}'
# export a
# awk 'BEGIN{print ENVIRON["a"]}'
123
8)FILENAME
FILENAME 是當(dāng)前處理文件的文件名。
# awk 'FNR==NR{print FILENAME"->"$0}FNR!=NR{print FILENAME"->"$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 代表忽略大小寫。
8.3.4 4 操作符
運算符 描述
(....) 分組
$ 字段引用
++ -- 遞增和遞減
+ - ! 加號,減號,和邏輯否定
* / % 乘,除和取余
+ - 加法,減法
| |& 管道,用于 getline,print 和 printf
< > <= >= != == 關(guān)系運算符
~ !~ 正則表達式匹配,否定正則表達式匹配
in 數(shù)組成員
&& || 邏輯 and,邏輯 or
?: 簡寫條件表達式:
expr1 ? expr2 : expr3
第一個表達式為真,執(zhí)行 expr2,否則執(zhí)行 expr3
= += -= *= /= %= ^= 變量賦值運算符
須知:在 k awk 中,有 3 3 種情況表達式為假:數(shù)字是 0 0 ,空字符串和未定義的值
數(shù)值運算 , 未定義變量初始值為 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)截取整數(shù)
# 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)感嘆號
打印奇數(shù)行:
# seq 6 |awk 'i=!i'
1
3
5
讀取第一行,i 是未定義變量,也就是 i=!0,!取反意思。感嘆號右邊是個布爾值,0 或空字符串為
假,非 0 或非空字符串為真,!0 就是真,因此 i=1,條件為真打印當(dāng)前記錄。
沒有 print 為什么會打印呢?因為模式后面沒有動作,默認會打印整條記錄。
讀取第二行,因為上次 i 的值由 0 變成了 1,此時就是 i=!1,條件為假不打印。
讀取第三行,上次條件又為假,i 恢復(fù)初始值 0,取反,繼續(xù)打印。以此類推...
可以看出,運算時并沒有判斷行內(nèi)容,而是利用布爾值真假判斷輸出當(dāng)前行。
打印偶數(shù)行:
# seq 6 |awk '!(i=!i)'
2
4
6
2)不匹配某行
# tail /etc/services |awk '!/blp5/{print $0}'
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems 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 # Matahari Broker
3)乘法和除法
# seq 5 |awk '{print $0*2}'
2
4
6
8
10
# seq 5 |awk '{print $0%2}'
1
0
1
0
1
打印偶數(shù)行:
# seq 5 |awk '$0%2==0{print $0}'
2
4
打印奇數(shù)行:
# 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)判斷數(shù)組成員
# awk 'BEGIN{a["a"]=123}END{if("a" in a)print "yes"}' </dev/null
yes
7)三目運算符
# awk 'BEGIN{print 1==1?"yes":"no"}' # 三目運算作為一個表達式,里面不允許寫 print
yes
# seq 3 |awk '{print $0==2?"yes":"no"}'
no
yes
no
替換換行符為逗號:
# seq 5 |awk '{print n=(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
8.3.5 5 流程控制
1 1 )f if 語句
格式:if (condition) statement [ else statement ]
單分支:
# seq 5 |awk '{if($0==3)print $0}'
3
雙分支:
# seq 5 |awk '{if($0==3)print $0;else print "no"}'
no
no
3
no
no
多分支:
# cat file
1 2 3
4 5 6
7 8 9
# awk '{if($1==4){print "1"} else if($2==5){print "2"} else if($3==6){print "3"} else
{print "no"}}' file
no
1
no
2 2 )e 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 3 )r for 語句 C C 語言風(fēng)格
格式:for (expr1; expr2; expr3) statement
遍歷打印所有字段:
# cat file
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
都換行了,這并不是我們要的結(jié)果。怎么改進呢?
# 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
在這種情況下,是不是就排除第一行和倒數(shù)第一行呢?我們正序打印看下
排除第一行:
# 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 4 )r for 語句遍歷 數(shù)組
格式: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 5 )k break 和 和 e continue 語句
break 跳過所有循環(huán),continue 跳過當(dāng)前循環(huán)。
# 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 6 )刪除數(shù)組和元素
格式:
delete array[index] 刪除數(shù)組元素
delete array 刪除數(shù)組
# 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)print v,a[v]}'
4 str4
5 str5
1 str1
2 str2
7 7 )t exit 語句
格式:exit [ expression ]
exit 退出程序,與 shell 的 exit 一樣。[ expr ]是 0-255 之間的數(shù)字。
# seq 5 |awk '{if($0~/3/)exit (123)}'
# echo $?
123
8.3.6 6 數(shù)組
數(shù)組:存儲一系列相同類型的元素,鍵/值方式存儲,通過下標(鍵)來訪問值。
awk 中數(shù)組稱為關(guān)聯(lián)數(shù)組,不僅可以使用數(shù)字作為下標,還可以使用字符串作為下標。
數(shù)組元素的鍵和值存儲在 awk 程序內(nèi)部的一個表中,該表采用散列算法,因此數(shù)組元素是隨機排
序。
數(shù)組格式:array[index]=value
1)自定義數(shù)組
# awk 'BEGIN{a[0]="test";print a[0]}'
test
2)通過 NR 設(shè)置記錄下標,下標從 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 循環(huán)遍歷數(shù)組
# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(v in a)print a[v],v}'
zabbix 4
user 5
admin 1
systemd-bus-proxy 2
systemd-network 3
# tail -n5 /etc/passwd |awk -F: '{a[NR]=$1}END{for(i=1;i<=NR;i++)print a[i],i}'
admin 1
systemd-bus-proxy 2
systemd-network 3
zabbix 4
user 5
上面打印的 i 是數(shù)組的下標。
第一種 for 循環(huán)的結(jié)果是亂序的,剛說過,數(shù)組是無序存儲。
第二種 for 循環(huán)通過下標獲取的情況是排序正常。
所以當(dāng)下標是數(shù)字序列時,還是用 for(expr1;expr2;expr3)循環(huán)表達式比較好,保持順序不變。
4)通過++方式作為下標
# tail -n5 /etc/passwd |awk -F: '{a[x++]=$1}END{for(i=0;i<=x-1;i++)print a[i],i}'
admin 0
systemd-bus-proxy 1
systemd-network 2
zabbix 3
user 4
x 被 awk 初始化值是 0,沒循環(huán)一次+1
5)使用字段作為下標
# tail -n5 /etc/passwd |awk -F: '{a[$1]=$7}END{for(v in a)print a[v],v}'
/sbin/nologin admin
/bin/bash user
/sbin/nologin systemd-network
/sbin/nologin systemd-bus-proxy
/sbin/nologin zabbix
6)統(tǒng)計相同字段出現(xiàn)次數(shù)
# tail /etc/services |awk '{a[$1]++}END{for(v in a)print a[v],v}'
2 com-bardac-dw
1 3gpp-cbsp
2 iqobject
1 matahari
2 isnetserv
2 blp5
# tail /etc/services |awk '{a[$1]+=1}END{for(v in a)print a[v],v}'
2 com-bardac-dw
1 3gpp-cbsp
2 iqobject
1 matahari
2 isnetserv
2 blp5
# tail /etc/services |awk '/blp5/{a[$1]++}END{for(v in a)print a[v],v}'
2 blp5
第一個字段作為下標,值被++初始化是 0,每次遇到下標(第一個字段)一樣時,對應(yīng)的值就會被
+1,因此實現(xiàn)了統(tǒng)計出現(xiàn)次數(shù)。
想要實現(xiàn)去重的的話就簡單了,只要打印下標即可。
7)統(tǒng)計 TCP 連接狀態(tài)
# netstat -antp |awk '/^tcp/{a[$6]++}END{for(v in a)print a[v],v}'
9 LISTEN
6 ESTABLISHED
6 TIME_WAIT
8)只打印出現(xiàn)次數(shù)大于等于 2 的
# tail /etc/services |awk '{a[$1]++}END{for(v in a) if(a[v]>=2){print a[v],v}}'
2 com-bardac-dw
2 iqobject
2 isnetserv
2 blp5
9)去重
只打印重復(fù)的行:
# 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
不打印重復(fù)的行:
# 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
先明白一個情況,當(dāng)值是 0 是為假,非 0 整數(shù)為真,知道這點就不難理解了。
只打印重復(fù)的行說明:當(dāng)處理第一條記錄時,執(zhí)行了++,初始值是 0 為假,就不打印,如果再遇到
相同的記錄,值就會+1,不為 0,則打印。
不打印重復(fù)的行說明:當(dāng)處理第一條記錄時,執(zhí)行了++,初始值是 0 為假,感嘆號取反為真,打
印,如果再遇到相同的記錄,值就會+1,不為 0 為真,取反為假就不打印。
# 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)統(tǒng)計每個相同字段的某字段總數(shù):
# tail /etc/services |awk -F'[ /]+' '{a[$1]+=$2}END{for(v in a)print v, a[v]}'
com-bardac-dw 97112
3gpp-cbsp 48049
iqobject 97238
matahari 49000
isnetserv 96256
blp5 96258
11)多維數(shù)組
awk 的多維數(shù)組,實際上 awk 并不支持多維數(shù)組,而是邏輯上模擬二維數(shù)組的訪問方式,比如
a[a,b]=1,使用 SUBSEP(默認\034)作為分隔下標字段,存儲后是這樣 a\034b。
示例:
# awk 'BEGIN{a["x","y"]=123;for(v in a) print v,a[v]}'
xy 123
我們可以重新復(fù)制 SUBSEP 變量,改變下標默認分隔符:
# awk 'BEGIN{SUBSEP=":";a["x","y"]=123;for(v in a) print v,a[v]}'
x:y 123
根據(jù)指定的字段統(tǒng)計出現(xiàn)次數(shù):
# cat a
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
本文出自 “一盞燭光” 博客,謝絕轉(zhuǎn)載!
更多建議: