一敢艰、簡(jiǎn)介
awk是一個(gè)強(qiáng)大的文本分析工具舵匾,相對(duì)于grep的查找俊抵,sed的編輯,awk在其對(duì)數(shù)據(jù)分析并生成報(bào)告時(shí)坐梯,顯得尤為強(qiáng)大徽诲。簡(jiǎn)單來說awk就是把文件逐行的讀入,以空格為默認(rèn)分隔符將每行切片吵血,切開的部分再進(jìn)行各種分析處理谎替。
awk有3個(gè)不同版本: awk、nawk和gawk践瓷,未作特別說明院喜,一般指gawk,gawk 是 AWK 的 GNU 版本晕翠。
二喷舀、命令的用法
命令格式
gawk [options] 'program...' FILE ...
?
常見選項(xiàng)
-F fs:指明字段分隔符;
-v var=value:自定義變量淋肾;
program語句
常見格式:PATTERN{ACTION STATEMENTS} #語句之間用分號(hào)分隔
- 逗號(hào)分隔符
- 輸出的各item可以是字符串硫麻、數(shù)值、記錄的字段樊卓、變量或awk表達(dá)式
- 省略item拿愧,打印全部字段,相當(dāng)于print $0碌尔。
- 變量
- 內(nèi)建變量
FS:輸入字段分隔符浇辜,默認(rèn)為空白字符;
OFS:輸出字段分隔符唾戚,默認(rèn)為空白字符柳洋;
RS:輸入時(shí)的換行符;
ORS:輸出時(shí)的換行符叹坦;
NF:每行的字段數(shù)熊镣,$NF表示最后一個(gè)字段;
NR:表示行數(shù)募书;
FNR:若提供多個(gè)文件绪囱,則分別計(jì)算各文件的行數(shù);
FILENAME:當(dāng)前文件名莹捡;
ARGC:命令行參數(shù)的個(gè)數(shù)鬼吵;
ARGV:參數(shù)數(shù)組,保存的是命令行給定的各參數(shù)道盏; - 自定義變量
(1) -v var=value #變量名區(qū)分字符大小寫而柑;
(2) 在program中直接定義
- printf命令
- 格式化輸出:printf FORMAT, item1, item2, ...
(1) FORMAT必須給出;
(2) 不會(huì)自動(dòng)換行文捶,需要顯式給出換行控制符荷逞,\n
(3) FORMAT中需要分別為后面的每個(gè)item指定一個(gè)格式化符號(hào)媒咳; - 格式符:
%c: 顯示字符的ASCII碼;
%d, %i: 顯示十進(jìn)制整數(shù)种远;
%e, %E: 科學(xué)計(jì)數(shù)法數(shù)值顯示涩澡;
%f:顯示為浮點(diǎn)數(shù);
%g, %G:以科學(xué)計(jì)數(shù)法或浮點(diǎn)形式顯示數(shù)值坠敷;
%s:顯示字符串妙同;
%u:無符號(hào)整數(shù);
%%: 顯示%自身膝迎; - 修飾符:
#[.#]:第一個(gè)數(shù)字控制顯示的寬度粥帚;第二個(gè)#表示小數(shù)點(diǎn)后的精度。 %3.1f
-: 左對(duì)齊
+:顯示數(shù)值的符號(hào)
操作符
算術(shù)操作符: x+y, x-y, x*y, x/y, x^y, x%y限次,-x芒涡,+x
字符串操作符:沒有符號(hào)的操作符,字符串連接
賦值操作符:=, +=, -=, *=, /=, %=, ^=, ++, --
比較操作符:>, >=, <, <=, !=, ==
模式匹配符::是否匹配卖漫,!:是否不匹配
邏輯操作符:&&费尽,||,!
函數(shù)調(diào)用:function_name(argu1, argu2, ...)
條件表達(dá)式:selector?if-true-expression:if-false-expressionPATTERN
(1) empty:空模式羊始,匹配每一行旱幼;
(2) /regular expression/:僅處理能夠被此處的模式匹配到的行;
(3) relational expression: 結(jié)果為真才會(huì)被處理突委;真為非0值柏卤,非空字符串;
(4) line ranges:行范圍匀油,startline,endline:/pat1/,/pat2/
注意: 不支持直接給出數(shù)字的格式
~]# awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
(5) BEGIN/END模式
BEGIN{}: 僅在開始處理文件中的文本之前執(zhí)行一次缘缚;
END{}:僅在文本處理完成之后執(zhí)行一次;常用的action
(1) Expressions
(2) Control statements:if, while等钧唐;
(3) Compound statements:組合語句忙灼;
(4) input statements
(5) output statements控制語句
if(condition) {statments}
if(condition) {statments} else {statements}
while(conditon) {statments}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break
continue
delete array[index]
delete array
exit
{ statements }if-else:if(condition) statement [else statement]
~]# awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
~]# awk '{if(NF>5) print $0}' /etc/fstab
~]# df -h | awk -F[%] '/^/dev/{print $1}' | awk '{if($NF>=20) print $1}'
使用場(chǎng)景:對(duì)awk取得的整行或某個(gè)字段做條件判斷;while:while(condition) statement
條件“真”钝侠,進(jìn)入循環(huán)该园;條件“假”,退出循環(huán)帅韧;
~]# awk '/^[[:space:]]linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
~]# awk '/^[[:space:]]linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
使用場(chǎng)景:對(duì)一行內(nèi)的多個(gè)字段逐一類似處理時(shí)使用里初;對(duì)數(shù)組中的各元素逐一處理時(shí)使用;do-while:do statement while(condition)
意義:至少執(zhí)行一次循環(huán)體-
for:for(expr1;expr2;expr3) statement
for(variable assignment;condition;iteration process) {for-body}
~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg特殊用法:
能夠遍歷數(shù)組中的元素忽舟;
語法:for(var in array) {for-body} switch
switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}break和continue
break [n]
continuenext
提前結(jié)束對(duì)本行的處理而直接進(jìn)入下一行双妨;
~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwdarray
關(guān)聯(lián)數(shù)組:array[index-expression]
index-expression:
(1) 可使用任意字符串淮阐;字符串要使用雙引號(hào);
(2) 如果某數(shù)組元素事先不存在刁品,在引用時(shí)泣特,awk會(huì)自動(dòng)創(chuàng)建此元素,并將其值初始化為“空串”挑随;
若要判斷數(shù)組中是否存在某元素状您,要使用"index in array"格式進(jìn)行;
weekdays[mon]="Monday"
若要遍歷數(shù)組中的每個(gè)元素兜挨,要使用for循環(huán)膏孟;
for(var in array) {for-body}
~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
注意:var會(huì)遍歷array的每個(gè)索引;
如果數(shù)組不存在拌汇,我們引用數(shù)組柒桑,會(huì)創(chuàng)建,值為空噪舀,引用數(shù)組的值為0魁淳。
- 函數(shù)
1.內(nèi)置函數(shù)
數(shù)值處理:
rand():返回0和1之間一個(gè)隨機(jī)數(shù)
字符串處理:
length([s]):返回指定字符串的長(zhǎng)度;
sub(r,s,[t]):以r表示的模式來查找t所表示的字符中的匹配的內(nèi)容傅联,并將其第一次出現(xiàn)替換為s所表示的內(nèi)容先改;t中有沒有r,有替換成s
gsub(r,s,[t]):以r表示的模式來查找t所表示的字符中的匹配的內(nèi)容蒸走,并將其所有出現(xiàn)均替換為s所表示的內(nèi)容仇奶;
split(s,a[,r]):以r為分隔符切割字符s,并將切割后的結(jié)果保存至a所表示的數(shù)組中比驻;
~]# netstat -tan | awk '/^tcp>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
2.自定義函數(shù)
三该溯、入門實(shí)例
1.把文本分片后排列整齊,并加上標(biāo)簽
[root@localhost sctript]# cat awk01
Mike Harrington:(510) 548-1278:250:100:175
Christian Dobbins:(408) 538-2358:155:90:201
Susan Dalsass:(206) 654-6279:250:60:50
Archie McNichol:(206) 548-1348:250:100:175
Jody Savage:(206) 548-1278:15:188:150
Guy Quigley:(916) 343-6410:250:100:175
Dan Savage:(406) 298-7744:450:300:275
Nancy McNeil:(206) 548-1278:250:80:75
John Goldenrod:(916) 348-4278:250:100:175
Chet Main:(510) 548-5258:50:95:135
Tom Savage:(408) 926-3456:250:168:200
Elizabeth Stachelin:(916) 440-1763:175:75:300
[root@localhost sctript]# awk -F: 'BEGIN{printf "%-20s %-15s %-4s %-4s %-4s\n","Name","PHone","Jan","Feb","MAR"}BEGIN{print"--------------------------------------------------"}{printf "%-20s %-15s %-4s %-4s %-4s\n",$1,$2,$3,$4,$5}' awk01
Name PHone Jan Feb MAR
--------------------------------------------------
Mike Harrington (510) 548-1278 250 100 175
Christian Dobbins (408) 538-2358 155 90 201
Susan Dalsass (206) 654-6279 250 60 50
Archie McNichol (206) 548-1348 250 100 175
Jody Savage (206) 548-1278 15 188 150
Guy Quigley (916) 343-6410 250 100 175
Dan Savage (406) 298-7744 450 300 275
Nancy McNeil (206) 548-1278 250 80 75
John Goldenrod (916) 348-4278 250 100 175
Chet Main (510) 548-5258 50 95 135
Tom Savage (408) 926-3456 250 168 200
Elizabeth Stachelin (916) 440-1763 175 75 300
2.刪除行內(nèi)與第一列字符相同的字符
[root@localhost sctript]# cat c
a b c a d a
s d d d x s a
h j s a s h j h
j d f j a s j k j
[root@localhost sctript]# awk '{a=$1;gsub(" ?"a,"");print a""$0}' c
a b c d
s d d d x a
h j s a s j
j d f a s k
a=$1
把第一列賦值變量a别惦。
gsub(" ?"a,"")
用函數(shù)gsub進(jìn)行行內(nèi)全局替換狈茉," ?"a是正則,表示前面的空格可以沒有也可以有一次掸掸,把匹配到的全部替換成空格氯庆。
print a""$0
打印,在行首添加上變量a扰付,也就字段$1堤撵。
3.打印學(xué)生的平均成績(jī)
[root@localhost sctript]# cat d
Sophia huaxue 90
Faye huaxue 80
Sophia wuli 70
Faye wuli 60
[root@localhost sctript]# awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成績(jī)?yōu)?\t%2.2f\n",b[i],y)}}' d
Sophia 平均成績(jī)?yōu)? 80.00
Faye 平均成績(jī)?yōu)? 70.00
分析分兩步:
1.先打印名字
awk '{b[$1]=$1}END{for(i in b)print b[i]}' d
Sophia
Faye
{b[$1]=$1}
以名字為變量b的下標(biāo),并在里面賦值名字羽莺。
END{for(i in b)print b[i]}
在最后实昨,遍歷數(shù)組b,并打印每一個(gè)元素盐固。
2.打印平均分?jǐn)?shù)
]# awk '{a[$1]++;c[$1]+=$3}END{for(i in a){y=c[i]/a[i];print y}}' d
80
70
{a[$1]++;c[$1]+=$3}
以名字為變量a的下標(biāo)荒给,自增計(jì)數(shù)丈挟。以名字為變量c的下標(biāo),拿自己和$3分?jǐn)?shù)的相加在賦值給自己志电。
{y=c[i]/a[i];print y}
總分除以計(jì)數(shù)的到平均分
把兩個(gè)整合
awk '{b[$1]=$1} {a[$1]++;c[$1]+=$3} END {for(i in b);for(i in a);for(i in c){y=c[i]/a[i];printf("%-8s平均成績(jī)?yōu)?\t%2.2f\n",b[i],y)}}' d
4.文本a記錄姓名曙咽、學(xué)號(hào),文本b記錄學(xué)號(hào)溪北、學(xué)科桐绒、成績(jī)夺脾。把a(bǔ)和b組合成一張表格之拨。
[root@localhost sctript]# cat a
Sophia|0001
Faye|0002
[root@localhost sctript]# cat b
0001|Chinese|77
0001|Music|90
0002|Chinese|62
0002|Music|80
[root@localhost sctript]# awk -F \| 'NR==FNR{a[$2]=$1;next}{printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3}' a b
Sophia 0001 Chinese 77
Sophia 0001 Music 90
Faye 0002 Chinese 62
Faye 0002 Music 80
NR表示a數(shù)據(jù)和b數(shù)據(jù)的總行數(shù),F(xiàn)NR表示a和b分別計(jì)數(shù)
NR==FNR為真時(shí)表示讀入的是a數(shù)據(jù)咧叭,執(zhí)行之后的代碼a[$2]=$1;next
a[$2]=$1;next
表示把a(bǔ)文件切片的字段1放入數(shù)組a蚀乔,數(shù)組a下標(biāo)用字段2的值
NR==FNR為假時(shí)表示讀入的是b數(shù)據(jù),執(zhí)行printf中的代碼
printf "%-8s %-6s %-8s %-2s\n",a[$1],$1,$2,$3
格式化輸出b文件切片后的值
a[$1],$1,$2,$3菲茬,其中第一個(gè)a[$1]是讀取a文件時(shí)新建的數(shù)組a吉挣,這是個(gè)關(guān)聯(lián)數(shù)組,文件a中$2的值等于文件b中$1的值婉弹,所以就可以把文件a存入數(shù)組的值和文件b的各字段數(shù)值一起打印出來睬魂。
5.把文本分片后按字母順序輸出相同兩個(gè)字符的字段
[root@localhost sctript]# cat sting
asf|zz|123|bb|q23|cc|py|dd|ab|ee|x6|ff|ffq|gg|aaa|hh|aa
[root@localhost sctript]# awk -F \| '{for(i=1;i<=NF;i++){if($i~/^([a-z]){2}$/) {a=substr($i,1,1);b=substr($i,2,2);if(a==b) print $i | "sort"}}}' sting
aa
bb
cc
dd
ee
ff
gg
hh
zz
文本切割后按照字段個(gè)數(shù)挨個(gè)循環(huán),用模式匹配出只有2個(gè)字母的字段镀赌,對(duì)這個(gè)字段的兩個(gè)字母對(duì)比氯哮,相等時(shí)打印并傳遞到sort命令中排序。
6.把grades中每行的字段分別從小到大排序
[root@localhost sctript]# cat grades
44 55 66 22 77 99
100 22 77 99 33 66
55 66 100 99 88 45
[root@localhost sctript]# vim sorter.awk
#!/bin/awk -f
#這里我用選擇排序算法進(jìn)行排序
{for(i=0;i<NF;i++){array[i]=$(i+1)}}
sort(array)
function sort (temp){
for(i=0;i<length(temp)-1;i++){
min=i
for(j=i+1;j<length(temp);j++){
if(temp[min]>temp[j]){
min=j
}
}
if(min!=i){
temp1=temp[i]
temp[i]=temp[min]
temp[min]=temp1
}
}
for(i=0;i<NF;i++){
printf("%d ", temp[i])
}
printf "\n"
}
[root@localhost sctript]# ./sorter.awk grades
22 44 55 66 77 99
22 33 66 77 99 100
45 55 66 88 99 100