[TOC]
AWK介紹
awk是一個強大的文本分析工具,相對于grep的查找,sed的編輯,awk在其對數(shù)據(jù)分析并生成報告時,顯得尤為強大。簡單來說awk就是把文件逐行的讀入,以空格為默認(rèn)分隔符將每行切片,切開的部分再進行各種分析處理。
awk有3個不同版本: awk、nawk和gawk,未作特別說明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名稱得自于它的創(chuàng)始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。實際上 AWK 的確擁有自己的語言: AWK 程序設(shè)計語言 , 三位創(chuàng)建者已將它正式定義為“樣式掃描和處理語言”。它允許您創(chuàng)建簡短的程序,這些程序讀取輸入文件、為數(shù)據(jù)排序、處理數(shù)據(jù)、對輸入執(zhí)行計算以及生成報表,還有無數(shù)其他的功能。
AWK工作原理
第一步:執(zhí)行BEGIN{action;... }語句塊中的語句
第二步:從文件或標(biāo)準(zhǔn)輸入(stdin)讀取一行,然后執(zhí)行pattern{ action;... }語句塊,它逐行掃描文件,從第一行到最后一行重復(fù)這個過程,直到文件全部被讀取完畢。
第三步:當(dāng)讀至輸入流末尾時,執(zhí)行END{action;...}語句塊BEGIN語句塊在awk開始從輸入流中讀取行之前被執(zhí)行,這是一個可選的語句塊,比如變量初始化、打印輸出表格的表頭等語句通??梢詫懺贐EGIN語句塊中END
語句塊在awk從輸入流中讀取完所有的行之后即被執(zhí)行,比如打印所有行的分析結(jié)果這類信息匯總都是在END
語句塊中完成,它也是一個可選語句塊pattern語句塊中的通用命令是最重要的部分,也是可選的。如果沒有提供
pattern語句塊,則默認(rèn)執(zhí)行{ print },即打印每一個讀取到的行,awk讀取的每一行都會執(zhí)行該語句塊
使用方法
基本格式:awk [options] 'program' file...
program:pattern {action statements;..}
pattern和action:
BEGIN,END
action statements對數(shù)據(jù)進行處理,放在{}內(nèi)指明
print,printf
分割符、域和記錄
awk執(zhí)行時,由分隔符分隔的字段(域)標(biāo)記$1,$2..$n稱為域標(biāo)識。$0為所有域,注意:和shell中變量$符含義不同
文件的每一行稱為記錄
省略action,則默認(rèn)執(zhí)行print $0 的操作
格式和變量
print格式:
print item1, item2, ...
要點:
(1) 逗號分隔符
(2) 輸出的各item可以字符串,也可以是數(shù)值;當(dāng)前記錄的字段、變量或awk的表達(dá)式
(3) 如省略item,相當(dāng)于print $0
awk內(nèi)置變量:
FS或-F:輸入字段分隔符,默認(rèn)為空白字符
FS實例:
[ root@CentOS ~]#awk '{print $1}' score.txt
mage
wang
zhang
li
---------------------------------------------------------------
[ root@CentOS ~]#awk -v FS=':' '{print $1,$3}' score2.txt
mage male
wang male
zhang female
li female
[ root@CentOS ~]#cat score2.txt
mage:100:male
wang:90:male
zhang:80:female
li:100:female
//-v FS=":"以":"分割符,取出文本中的$1(第一列)、$3(第三列)。注意:分割符(":")必須是文本中內(nèi)容有的,如果文中沒有 ":"是沒有結(jié)果的,如下:
[ root@CentOS ~]#awk -v FS=":" '{print $1,$3}' score.txt
mage 100 male
wang 90 male
zhang 80 female
li 100 female
//他就會打印全部
-F:實例
[ root@CentOS ~]#awk -F: '{print $1,$3}' score2.txt
mage male
wang male
zhang female
li female
//跟FS效果一樣,只不過選項格式不一樣
OFS:輸出字段分隔符,默認(rèn)為空白字符
[ root@CentOS ~]#awk -F: -v OFS='-' '{print $1,$3}' score2.txt
mage-male
wang-male
zhang-female
li-female
//先以 ":"分割,OFS='-',指定輸出的分隔符
RS:輸入記錄分隔符,指定輸入時的換行符,原換行符仍有效
[ root@CentOS ~]#awk -v RS=':' '{print $0}' score2.txt
mage
100
male
wang
90
male
zhang
80
female
li
100
female
//-v RS=':' :RS(行處理),在內(nèi)容中碰到':'就換行,輸入時的換行符
ORS:輸出時的換行符
[ root@CentOS ~]#awk -F: -v ORS='--' '{print $0}' score2.txt
mage:100:male--wang:90:male--zhang:80:female--li:100:female--
//先以 ":"分割,輸出時以 '--'的分割符
NF:字段數(shù)量
[ root@CentOS ~]#awk -F: '{print NF}' score2.txt
3
3
3
3
1
//查看文件有幾個字段,如果寫成$NF意義就不一樣了。awk 中聲明的變量在print 里面無需寫'$'
[ root@CentOS ~]#awk -F: '{print $NF}' score2.txt
male
male
female
female
100 female
//打印出score2.txt文件的每行詞尾,因為NF是打印每行的字段數(shù)量,如果寫成$NF那就是NF的結(jié)果,如果NF是2,那么輸入$NF顯示的是2字段的字符串
NR:行號
[ root@CentOS ~]#awk '{print NR}' score2.txt
1
2
3
4
5
[ root@CentOS ~]#cat score2.txt
mage:100:male
wang:90:male
zhang:80:female
li:100:female
li : 100 female
//顯示行號
FNR:各文件分別計數(shù),行號
[ root@CentOS ~]#awk '{print $0,FNR}' score.txt
mage 100 male 1
wang 90 male 2
zhang 80 female 3
li 100 female 4
//后面的數(shù)字顯示的是行數(shù)編號
FILENAME:當(dāng)前文件名
[ root@CentOS ~]#awk '{print FILENAME}' score.txt
score.txt
score.txt
score.txt
score.txt
//因為score.txt里面有5行所有打印了5次,當(dāng)然我們也可以使用一種模式讓他只打印一次
[ root@CentOS ~]#awk 'END{print FILENAME}' score.txt
score.txt
// END后面會講到
ARGC:命令行參數(shù)的個數(shù)
[ root@CentOS ~]#awk '{print ARGC}' score2.txt
2
2
2
2
2
//當(dāng)前有2個參數(shù),一個是awk,一個是score2.txt,打印5次是因為score2文件中有5行
ARGV:數(shù)組,保存的是命令行所給定的各參數(shù)
[ root@CentOS ~]#awk '{print ARGV[0]}' score2.txt
awk
awk
awk
awk
awk
[ root@CentOS ~]#awk '{print ARGV[1]}' score2.txt
score2.txt
score2.txt
score2.txt
score2.txt
score2.txt
//"ARGV[0]":表示數(shù)組中的第一個元素,"ARGV[1]":表示數(shù)組中的第二個元素
BEGIN/END模式
BEGIN{}: 僅在開始處理文件中的文本之前執(zhí)行一次
END{}:僅在文本處理完成之后執(zhí)行一次
AWK自定義變量:
變量(區(qū)分字符大小寫)
(1) -=value
(2) 在program中直接定義
[ root@CentOS ~]#awk -v test='hello word' 'BEGIN{print test}'
hello word
//定義test變量,并打印出來
[ root@CentOS ~]#awk 'BEGIN{test="hello word"; print test}'
hello word
printf命令:
格式化輸出:printf “FORMAT”, item1, item2, ...
(1) 必須指定FORMAT
(2) 不會自動換行,需要顯式給出換行控制符,\n
(3) FORMAT中需要分別為后面每個item指定格式符
格式符:需加"",與item一一對應(yīng)
%c:顯示字符的ASCII碼(專門顯示字符串)
%d,%i:顯示十進制整數(shù)
%e,%E 科學(xué)技術(shù)法數(shù)值顯示
%f:顯示為浮點數(shù)
%g,%G:以科學(xué)計數(shù)法或浮點型式顯示數(shù)值
%s :顯示字符串
%u :無符號整數(shù)
%% :顯示%自身
修飾符:
[.#]:第一個數(shù)字控制顯示的寬度;第二個#表示小數(shù)點后精度,%3.1f
-: 左對齊(默認(rèn)右對齊)%-15s
+:顯示數(shù)值的正負(fù)符號%+d
[ root@CentOS ~]#awk -F: '{printf "%-10s" "%10d\n",$1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
uucp 10
operator 11
games 12
gopher 13
ftp 14
nobody 99
vcsa 69
saslauth 499
postfix 89
sshd 74
admin1 500
// "%-10s" :向左對齊,s是字符串(一定要跟文件中內(nèi)容一樣,/etc/passwd/中的$1是字符串,$3是數(shù)字所以寫的是 "%10d"),\n是換行
-----------------------------------------------------------------------------------------
[ root@CentOS ~]#awk -F: '{printf "NAME: %-10s UID:%-10d\n",$1,$3}' /etc/passwd
NAME: root UID:0
NAME: bin UID:1
NAME: daemon UID:2
NAME: adm UID:3
NAME: lp UID:4
NAME: sync UID:5
NAME: shutdown UID:6
NAME: halt UID:7
NAME: mail UID:8
NAME: uucp UID:10
NAME: operator UID:11
NAME: games UID:12
NAME: gopher UID:13
NAME: ftp UID:14
NAME: nobody UID:99
NAME: vcsa UID:69
NAME: saslauth UID:499
NAME: postfix UID:89
NAME: sshd UID:74
NAME: admin1 UID:500
//也可以添加一些字符串然看起來更美觀點
操作符:
算數(shù)操作符:
x+y , x-y, x*y , x/y , x^y , x%y
-x:整數(shù)轉(zhuǎn)換成負(fù)數(shù)
+:轉(zhuǎn)換為數(shù)值
字符串操作符:沒有符號的操作符,字符串鏈接
賦值操作符:
=, +=, -= , *= , /= , %= ,^=
比較操作符:
>, >=, <, <=, !=, ==
模式匹配符
~ :是否匹配
!~:是否不匹配
邏輯操作符:
&&:并且
||:或者
!:非
條件表達(dá)式(三目表達(dá)式):
selector?if-true-expression:if-false-expression
//后面案例我們都會用到
awk PATTERN
(1)如果未指定:空模式,匹配每一行
(2) /regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來
[ root@CentOS ~]#awk '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
//顯示以行首(^:行首的意思)為root的行
(3) relational expression:
關(guān)系表達(dá)式,結(jié)果為“真”才會被處理
真:結(jié)果為非0值,非空字符串
假:結(jié)果為空字符串或0值
[ root@CentOS ~]#awk -F: '$3>200{print $1,$3}' /etc/passwd
saslauth 499
admin1 500
//匹配/etc/passwd中的$3,第三段是否大于200,如果大于就打印出來,否則不打印
[ root@CentOS ~]#awk -F: '$NF=="/bin/bash"{print $NF}' /etc/passwd
/bin/bash
/bin/bash
//$NF每行字段的最后一位字符串,如果匹配到的是 "/bin/bash"就打印出來
三目表達(dá)式:
[ root@CentOS ~]#awk -F: '{$3<500?user="is system":user="is common";{printf "%-10s %10s\n" ,$1,user}}' /etc/passwd
root is system
bin is system
daemon is system
adm is system
lp is system
sync is system
shutdown is system
halt is system
mail is system
uucp is system
operator is system
games is system
gopher is system
ftp is system
nobody is system
vcsa is system
saslauth is system
postfix is system
sshd is system
admin1 is cmmon
//以":"分割,/etc/passwd中的第三行(uuid)小于500的就輸出"is system ",否則就輸入"is common",
//三目表達(dá)式格式:selector?if-true-expression:if-false-expression
控制語句
if(condition) {statments}
if(condition) {statments} else {statments}
while(conditon) {statments}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break 、continue 、delete arry[index] 、delete arry 、exit {statements}
if else 案例:
[ root@CentOS ~]#awk -F: '{if($3>=200) {printf "Common user: %-10s %8s\n", $1,$3}else{printf "is system %-10s %10s\n",$1,$3}}' /etc/passwd
is system root 0
is system bin 1
is system daemon 2
is system adm 3
is system lp 4
is system sync 5
is system shutdown 6
is system halt 7
is system mail 8
is system uucp 10
is system operator 11
is system games 12
is system gopher 13
is system ftp 14
is system nobody 99
is system vcsa 69
Common user: saslauth 499
is system postfix 89
is system sshd 74
Common user: admin1 500
// if判斷$3(/etc/passwd中的id)>=200就打印"Common user: $1,$3",否則就打印小于200(/etc/passwd中id)小于200的用戶和id("is system $1,$3")
while和for 循環(huán)
[ root@localhost ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) { if(length($i)>=3) {print $i,length($i)};i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-514.el7.x86_64 30
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
crashkernel=auto 16
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-e2b1c08415194abdb3672f06ea53672b 50
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
crashkernel=auto 16
net.ifnames=0 13
//對/etc/grub2.cfg進行操作,以行首為空白開頭最少一次或者多次并且后面跟linux16的行(^[[:space:]]*linux16),"i=1;while(i<=NF)",NF(統(tǒng)計每行的字段),i小于等于每行的字段總數(shù),
if(length($i)>=3:意思是每行的每段的字符串字?jǐn)?shù)大于等于3的打印出來,最后的{print $i,length($i)};i++}}
意思是:打印$i就是每行的段,length($i):將次匹配到的字符串放入這里面,最后的i++每次i的值都會+1,從1一直加到小于等于每行的字段數(shù)(只匹配設(shè)定前面設(shè)定條件的行)
//length 字符串長度
-----------------------------------------------------------------------------------------------
[ root@localhost ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){ if(length($i)>=2) {print $i,length($i)}}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-514.el7.x86_64 30
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-e2b1c08415194abdb3672f06ea53672b 50
root=UUID=25927f3c-9f12-45f7-8995-652b4468aebe 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
//跟上面效果一樣,只不過換成for循環(huán)
數(shù)組:
[ root@CentOS ~]#awk 'BEGIN{user["w"]="wang";user["m"]="ma";print user["m"]}'
ma
// 聲明一個數(shù)組,[]里面聲明了2個元素,元素和值必須加雙引號,打印時也許要輸入雙引號
//注意:var會遍歷array的每個索引
--------------------------------------------------------------------------------------------
[ root@CentOS ~]#awk 'BEGIN{user["w"]="wang";user["m"]="ma";for(i in user) {print user[i]}}'
wang
ma
//通過for循環(huán)打印出數(shù)組中的元素