首页 > 代码库 > AWK运用
AWK运用
awk:报告生成器,格式化文本输出
有多种版本:New awk (nawk ),GNU awk(gawk)
gawk:模式扫描和处理语言
基本用法:
awk [options] ‘program’ var=value file…
awk [options] -f programfile var=value file…
awk [options] ‘BEGIN{ action;… } pattern{ action;… } END{action;… }’ file …
awk 程序通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
program 通常是被单引号或双引号;尽量使用单引号
选项:
-F 指明输入时用到的字段分隔符
-v var=value:
基本格式:awk [options] ‘program’ file…
program:pattern{action statements;..}
pattern 和action: :
? pattern 部分决定动作语句何时触发及触发事件BEGIN,END
? action statements 对数据进行处理,放在{} 内指明print, printf分割符、域和记录
? awk 执行时,由分隔符分隔的字段(域)标记$1,$2..$n称
为域标识。$0 为所有域,注意:和shell 中变量$ 符含义不同
? 文件的每一行称为记录
? 省略action行 ,则默认执行 print $0的操作
awk工作原理:
第一步:执行BEGIN{action;… } 语句块中的语句
第二步:从文件或标准输入(stdin) 读取一行,然后执行pattern{action;… } 语句块,
它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块BEGIN 语句块在awk开始从输
入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表
格的表头等语句通常可以写在BEGIN 语句块中
END 语句块在awk 从输入流中读取完所有的行之后即被执行,比如打印所有行的分析
结果这类信息汇总都是在END 语句块中完成,它也是一个可选语句块
pattern 语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern
语句块,则默认执行{ print } ,即打印每一个读取到的行,
awk 读取的每一行都会执行该语句块
print 格式:print item1, item2, …
要点:
? (1) 逗号分隔符
? (2) 输出的各item 可以字符串,也可以是数值;当前记录的字段、
变量或awk 的表达式
? (3) 如省略item ,相当于print $0
示例:
awk ‘{print “hello,awk”}’ file
awk –F: ‘{print}’ /etc/passwd
awk –F: ‘{print “wang”}’ /etc/passwd
awk –F: ‘{print $1}’ /etc/passwd
awk –F: ‘{print $0}’ /etc/passwd
awk –F: ‘{print $1”\t”$3}’ /etc/passwd
tail –3 /etc/fstab |awk ‘{print $2,$4}’
awk变量:内置和自定义变量
FS :输入字段分隔符,默认为空白字符
awk -v FS=’:’ ‘{print $1,FS,$3}’ /etc/passwd
awk –F: ‘{print $1,$3,$7}’ /etc/passwd
OFS :输出字段分隔符,默认为空白字符
awk -v FS=‘:’ -v OFS=‘:’ ‘{print $1,$3,$7}’ /etc/passwd
RS :输入记录分隔符,指定输入时的换行符,原换行符仍有效
awk -v RS=’ ‘ ‘{print }’ /etc/passwd
ORS :输出记录分隔符,输出时用指定符号代替换行符
awk -v RS=’ ‘ -v ORS=’###’‘{print }’ /etc/passwd
NF :字段数量
awk -F:‘{print NF}’ /etc/fstab, 引用内置变量不用$
awk -F: ‘{print $(NF-1)}’ /etc/passwd
NR :行号
awk ‘{print NR}’ /etc/fstab ; awk END‘{print NR}’ /etc/fstab
FNR :各文件分别计数, 行号
awk ‘{print FNR}’ /etc/fstab /etc/inittab
FILENAME :当前文件名
awk ‘{print FILENAME}’ /etc/fstab
ARGC :命令行参数的个数
awk ‘{print ARGC}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab
ARGV :数组,保存的是命令行所给定的各参数
awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab
awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab
printf命令:
格式化输出:printf “FORMAT ”, item1, item2, …
(1) 必须指定FORMAT
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT 中需要分别为后面每个item 指定格式符
格式符:与item 一一对应
%c: 显示字符的ASCII码 码
%d, %i: 显示十进制整数
%e, %E: 显示科学计数法数值
%f :显示为浮点数
%g, %G :以科学计数法或浮点形式显示数值
%s :显示字符串
%u :无符号整数
%%: 显示% 自身
修饰符:
[.#] :第一个数字控制显示的宽度;第二个# 表示小数点后精度,%3.1f
– : 左对齐(默认右对齐) %-15s #加上负号表示左对齐
+ :显示数值的正负符号 %+d
exam:
? awk -F: ‘{printf “%s”,$1}’ /etc/passwd
? awk -F: ‘{printf “%s\n”,$1}’ /etc/passwd
? awk -F: ‘{printf “%-20s %10d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %s\n”,$1}’ /etc/passwd
? awk -F: ‘{printf “Username: %s,UID:%d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %15s,UID:%d\n”,$1,$3}’ /etc/passwd
? awk -F: ‘{printf “Username: %-15s,UID:%d\n”,$1,$3}’ /etc/passwd
算术操作符:
x+y, x-y, xy, x/y, x^y, x%y
-x: 转换为负数
+x: 转换为数值
字符串操作符:没有符号的操作符,字符串连接
赋值操作符:
=, +=, -=, *=, /=, %=, ^=
++, —
比较操作符:
==, !=, >, >=, <, <=
模式匹配符:~ :左边是否和右边匹配包含 !~ :是否不匹配
awk –F: ‘$0 ~ /root/{print $1}‘ /etc/passwd #整行包括root就打印
awk ‘$0~“^root”‘ /etc/passwd #行首包含root
awk ‘$0 !~ /root/‘ /etc/passwd
awk –F: ‘$3==0’ /etc/passwd
awk -F: ‘$3==1000{print $1,$3}’ /etc/passwd #一个等号是赋值,2个比较
逻辑 操作符:与&& ,或|| ,非!
示例:
? awk –F: ‘$3>=0 && $3<=1000 {print $1}’ /etc/passwd
? awk -F: ‘$3==0 || $3>=1000 {print $1}’ /etc/passwd
? awk -F: ‘!($3==0) {print $1}’ /etc/passwd
? awk -F: ‘!($3>=500) {print $3}’ /etc/passwd #取反需要小括号
函数调用: function_name(argu1, argu2, …)
条件表达式(三目表达式):
selector?if-true-expression:if-false-expression
#为真就执行第一个命名,为假执行第二个命令
? 示例:
awk -F: ‘{$3>=1000?usertype=”Common User”:usertype=”Sysadmin”;printf
“%15s:%-s\n”,$1,usertype}’ /etc/passwd
PATTERN: 根据pattern 条件,过滤匹配的行,再做处理
(1) 如果未指定:空模式,匹配每一行
(2) /regular expression/ :仅处理能够模式匹配到的行,需要用/ /括起来
awk ‘/^UUID/{print $1}’ /etc/fstab
awk ‘!/^UUID/{print $1}’ /etc/fstab
(3) relational expression: 关系表达式,结果为“真”才会被处理
真:结果为非0 值,非空字符串 #“0”表示有字符串0,为真
假:结果为空字符串或0值 #0为假 ,0以外的都是真
示例:
? awk -F: ‘i=1;j=1{print i,j}’ /etc/passwd #i=1;分号单独打印了,用,就代表一起
? awk ‘!0’ /etc/passwd ; awk ‘!1’ /etc/passwd
? awk –F: ‘$3>=1000{print $1,$3}’ /etc/passwd
? awk -F: ‘$3<1000{print $1,$3}’ /etc/passwd
? awk -F: ‘$NF==”/bin/bash”{print $1,$NF}’ /etc/passwd
? awk -F: ‘$NF ~ /bash$/{print $1,$NF}’ /etc/passw
(4) line ranges : 行范围 也支持贪婪模式
startline,endline :/pat1/,/pat2/ 不支持直接给出数字格式
awk -F: ‘/^root\>/,/^nobody\>/{print $1}’ /etc/passwd
awk -F: ‘(NR>=10&&NR<=20){print NR,$1}’ /etc/passwd
(5) BEGIN/END 模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{} :仅在文本处理完成之后执行一次
seq 10|awk ‘i=!i’ :当i=1的时候打印第一行,循环到第二行时候1取反等于0
不打印第二行,第三行0取反为真,打印第三行,打印奇数行
seq 10 |sed -n ‘1~2p’
awk控制语句:
{ statements;… } 组合语句
if(condition) {statements;…}
if(condition) {statements;…} else {statements;…}
while(conditon) {statments;…}
do {statements;…} while(condition)
for(expr1;expr2;expr3) {statements;…}
break
continue
delete array[index]
delete array
exit
语法:if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}
else{statement3} #条件和语句一起使用,条件用()
使用场景:对awk 取得的整行或某个字段做条件判断
完整的语句之间使用不要用使用;号 {}中使用;号
示例:
awk -F: ‘{if($3>=1000)print $1,$3}’ /etc/passwd
awk -F: ‘{if($NF==”/bin/bash”) print $1}’ /etc/passwd
awk ‘{if(NF>5) print $0}’ /etc/fstab
awk -F: ‘{if($3>=1000) {printf “Common user: %s\n”,$1} else
{printf “root or Sysuser: %s\n”,$1}}’ /etc/passwd
awk -F: ‘{if($3>=1000) printf “Common user: %s\n”,$1;
else printf “root or Sysuser: %s\n”,$1}’ /etc/passwd
df -h|awk -F% ‘/^\/dev/{print $1}’|awk ‘$NF>=80{print $1,$5}‘
awk ‘BEGIN{ test=100;if(test>90){print “very good”}
else if(test>60){ print “good”}else{print “no pass”}}’
while 循环
语法:while(condition){statement;…}
条件“真”,进入循环;条件“假”,退出循环
使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用
示例:
awk ‘/^[[:space:]]linux16/{i=1;while(i<=NF) #NF字段数
{print $i,length($i); i++}}’ /etc/grub2.cfg
awk ‘/^[[:space:]]linux16/{i=1;while(i<=NF) {if(length($i)>=10)
{print $i,length($i)}; i++}}’ /etc/grub2.cfg
length:统计有几个字符,awk自带的函数‘
akw ‘BEGIN{print length(“aaaaaaaaa”)}’ #统计有几个a
do-while 循环
语法:do {statement;…}while(condition)
意义:无论真假,至少执行一次循环体 #意思是先执行再循环
示例:
? awk ‘BEGIN{ total=0;i=0;do{
total+=i;i++;}while(i<=100);print total}‘
思考:下面两语句有何不同?
? awk ‘BEGIN{i=0;print ++i,i}’ #先赋值,再打印
? awk ‘BEGIN{i=0;print i++,i}’ #先打印i 再赋值
for 循环
语法:for(expr1;expr2;expr3) {statement;…}
常见用法:
for(variable assignment;condition;iteration process){for-body}
特殊用法:能够遍历数组中的元素;
语法:for(var in array) {for-body}
示例:
awk ‘/^[[:space:]]linux16/{for(i=1;i<=NF;i++) {print$i,length($i)}}’
/etc/grub2.cfg
1到100相加的几种方法:awk效率最高
time (awk ‘BEGIN{
total=0;for(i=0;i<=10000;i++){total+=i;};print total;}’)
time(total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
time(for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
time(seq –s ”+” 10000|bc) #-s指定分隔符,默认是换行
switch 语句
语法:switch(expression) {case VALUE1 or /REGEXP/:
statement1; case VALUE2 or /REGEXP2/: statement2; …; default: statementn}
break 和continue
? awk ‘BEGIN{sum=0;for(i=1;i<=100;i++) {if(i%2==0)continue;sum+=i}print sum}‘
? awk ‘BEGIN{sum=0;for(i=1;i<=100;i++) {if(i==66)break;sum+=i}print sum}‘
break [n]
continue [n]
next: 提前结束对本行处理而直接进入下一行处理(awk 自身循环)
awk -F: ‘{if($3%2!=0) next; print $1,$3}’ /etc/passwd
关联数组:array[index-expression]
index-expression:
? (1) 可使用任意字符串;字符串要使用双引号括起来
? (2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,
并将其值初始化为“空串”
? 若要判断数组中是否存在某元素,要使用“index in array”格 式进行遍历
示例:
? weekdays[“mon”]=”Monday“
? awk ‘BEGIN{weekdays[“mon”]=”Monday”;
weekdays[“tue”]=”Tuesday”;print weekdays[“mon”]}‘
? awk ‘!arr[$0]++’ dupfile #出现重复的$0 加1但是不再打印 和uniq -c 类似
? awk ‘{!arr[$0]++;print $0, arr[$0]}’ dupfile
若要遍历数组中的每个元素,要使用for 循环
for(var in array) {for-body}
注意:var 会遍历array 的每个索引
示例:
? awk ‘BEGIN{weekdays[“mon”]=”Monday”;weekdays[“tue”]=”Tuesday”;for(i in
weekdays) {print weekdays[i]}}‘
? netstat -tan | awk ‘/^tcp/{state[$NF]++}END {for(i in state)
{ print i,state[i]}}’
? awk ‘{ip[$1]++}END{for(i in ip) {print i,ip[i]}}’ /var/log/httpd/access_log
数值处理:
rand() :返回0 和1 之间一个随机数
awk ‘BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }’
字符串处理:
? length([s]) : 返回指定字符串的长度
? sub(r,s,[t]) : 对t 字符串进行搜索r 表示的模式匹配的内容,
并将第一个匹配的内容替换为s
echo “2008:08:08 08:08:08” | awk ‘sub(/:/,“-“,$1)’
? gsub(r,s,[t]) :对t 字符串进行搜索r 表示的模式匹配的内容,并全部替换
为s 所表示的内容
echo “2008:08:08 08:08:08” | awk ‘gsub(/:/,“-“,$0)’
? split(s,array,[r]) :以r 为分隔符 ,切割字符串s ,并将切割后的结果保存
至array所表示的 数组中,第一个索引值为1, 第二个索引值为2,…
netstat -tan | awk ‘/^tcp\>/{split($5,ip,”:”);count[ip[1]]++}
END{for (i in count) {print i,count[i]}}’
自定义函数
格式:
function name ( parameter, parameter, … ) {
statements
return expression
}
示例:
cat fun.awk
function max(v1,v2) { #函数名和参数(形参)
v1>v2?var=v1:var=v2 #三目表达式
return var #函数的值就是var
}
BEGIN{a=3;b=2;print max(a,b)} #实参
awk –f fun.awk #-f 调用awk函数
system 命令 #awk中可以调用shell命令
空格是awk 中的字符串连接符,如果system 中需要使用awk中
的变量可以使用空格分隔,或者说除了awk 的变量外其他一律用”” 引用起来。
awk BEGIN‘{system(“hostname”) }’
awk ‘BEGIN{score=100; system(“echo your score is ” score) }’
将awk 程序写成脚本,直接调用或执行
示例:
cat f1.awk
if($3>=1000)print $1,$3}
awk -F: -f f1.awk /etc/passwd
cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}
chmod +x f2.awk
f2.awk –F: /etc/passwd
格式:
awkfile var=value var2=value2… Inputfile #在脚本外面传递参数
注意 :在BEGIN 过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v参数,
让awk 在执行BEGIN 之前得到变量的值。命令行中每一个指定的变量都需要一个-v 参数
示例:
cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
#chmod +x test.awk
#test.awk -F: min=100 max=200 /etc/passw
这里的赋值可以再print里面使用,再BEGIN和END里面需要-v min=200
但是建议习惯使用-v不管如何都能使用,免得遗忘
AWK运用