-
简介
- awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出
- 有多种版本:New awk(nawk),GNU awk( gawk )
-
awk的用法和格式
Linux系统所使用的是GUN awk- 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通常是被单引号或双引号中 - 选项:
$0:整行
$1,$2..... : 位置参数
-F : 指定输入时用到的字段分隔符
-v var=value : 自定义变量
-f filename : 调用awk脚本文件
-
基本格式
awk [options] 'program' file… program: pattern{action statements;..}
- pattern:
pattern部分决定动作语句何时触发及触发事件
地址定界: /pat1/,/pat2/ 一个范围
/pattern/ 被匹配到的行
experssion 表达式=,<,<=,==,!=,~(模式匹配)
BEGIN: 执行前操作
END: 执行后操作,收尾动作 - action:
print
printf- printf : 格式化输出命令:
printf "FORMAT", item1, item2, ...
FORMAT:格式符,为每个item按位占一个位留一个特殊符号格式符,为每个item按位占一个位留一个特殊符号,所以item最终会显示在format指定格式符号的位置上;
(1) FORMAT必须给出;
(2) 在显示多行文本时,不会自动换行,需要显式给出换行控制符,\n;
(3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
格式符:
%c: 显示字符的ASCII码
%d,%i: 显示十进制整数
%e,%E: 显示科学计数法数值
%f: 显示为浮点数
%g,%G: 以科学计数法或浮点形式显示数值
%s: 显示字符串
%u: 无符号整数
%%: 显示%自身
修饰符:
#[.#]:第一个数字控制显示的总宽度;第二个#表示小数点后精度,%3.1f
-: 左对齐(默认右对齐) %-15s
+:显示数值的正负符号 %+d
- printf : 格式化输出命令:
- pattern:
-
awk操作符:
- 算数操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x: 转换为负数
+x: 转换为数值 - 赋值操作符:
=, +=, -=, *=, /=, %=, ^=, ++, --
例:
(1)awk 'BEGIN{i=0;print ++i,i}' #++i,++在前,先加再赋值
输出:1 1
(2)awk 'BEGIN{i=0;print i++,i}' #i++,++在后,先赋值在加(这里相当于先输出i)
输出:0 1 - 比较操作符:
==, !=, >, >=, <, <= -
模式匹配符:
~:左边是否和右边匹配包含
!~:是否不匹配 - 逻辑操作符:
&& 与运算
|| 或运算
! 非运算
函数调用: function_name(argu1, argu2, ...)
条件表达式(三目表达式):
selector?if-true-expression:if-false-expression
例:
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
输出:
root:Sysadmin or SysUser
mysql:Sysadmin or SysUser
zabbix:Common User
- 算数操作符:
-
awk pattern:
- PATTERN:根据pattern条件,过滤匹配的行,再做处理
- (1)如果未指定:空模式,匹配每一行
- (2)/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
awk '/^UUID/{print $1}' /etc/fstab
awk '!/^UUID/{print $1}' /etc/fstab - (3)relational expression: 关系表达式,结果为“真”才会被处理
真:结果为非0值,非空字
假:结果为空字符串或0值 - (4)line ranges:行范围
startline,endline:/pat1/,/pat2/ 不支持直接给出数字格式
例:
awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwd - (5) BEGIN/END模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
- PATTERN:根据pattern条件,过滤匹配的行,再做处理
-
awk action:
- 常用的action分类:
(1) Expressions:算术,比较表达式等
(2) Control statements:if, while等
(3) Compound statements:组合语句
(4) input statements
(5) output statements:print等
- 常用的action分类:
-
awk控制语句:
-
if-else控制语句:
- 语法:
- 单分支: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($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}'
- 例:
-
while循环:
- 语法:while(condition){statement;…}
- 条件“真”,进入循环;条件“假”,退出循环
- 使用场景:
- 对一行内的多个字段逐一类似处理时使用
- 对数组中的各元素逐一处理时使用
- 例:
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)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg
- 例:
- do-while循环:
- 语法:do {statement;…}while(condition)
- 意义:无论真假,至少执行一次循环体
- 例:
awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}‘
- for循环:
- 语法格式:for(expr1;expr2;expr3) statement
expr1:控制变量初始化;
expr2:条件判断;
expr3:控制变量的数值修正表达式;
for(variable assignment;condition;iteration process) {for-body}
#即:for(变量赋值;条件判断表达式;变量修正表达式) {循环体语句} -
例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
#以空格开头0次或多次,后跟linux16的行,以空格为分隔符,显示每行中各字段的长度;同上while语句;awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {if(length($i)>=7) {print $i,length($i)}}}' /etc/grub2.cfg #以空格开头0次或多次,后跟linux16的行,以空格为分隔符,只显示每行中字段的长度>=7的字段;同上while语句;
- for循环特殊用法:
- 语法格式:for(var in array) {for-body}
-
即:for(变量名 in 数组名) {循环体语句}
- 变量名会遍历数组的下标,然后就能引用数组的元素;
- 意义:能够遍历数组中的元素;
- witch语句(在awk中用的不多)
-
语法格式:
switch(expression) {case VALUE1 or /REGEXP1/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statement}
#即:switch(表达式) {case 值1或/正则式1/: 语句1; case 值1或/正则式1/: 语句2; ...; default: 语句n}- 判断表达式的值符合case里面的哪个条件,可以等于case里的值,也可以是被case里的正则表达式匹配;如果符合条件就执行该case里的语句,不会往下判断了;类似case语句,只不过是关键字写法不同;
-
break和continue:
- break [n]:退出n层循环;
- continue:提前结束本轮循环,直接进入下一轮循环(即下一个字段);
- next:
- 在awk中能实现2重循环,awk本身可对文件每行循环,使用循环语句是为了遍历一行中的每个字段,或数组中的每个元素;
-
next同continue一样,也是控制循环的,但是是控制awk的本身循环的;
- 即提前结束对本行的处理而直接进入下一行;
- 例:
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
#显示用户ID号为偶数的行;即判断id号是否能被2整除,如果不等于0,直接结束本行,直接进行下一行;
-
-
awk数组:
- 关联数组:array[index-expression]
-
index-expression:索引表达式
- (1) 可使用任意字符串;字符串要使用双引号;不能随便使用单引号;
-
(2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空”;允许未经声明直接引用;
-
若要判断数组中是否存在某元素,要使用"index in array"格式进行;
- 例:
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
#注意:字符串要使用双引号;引用数组中的元素不需使用$符号;
- 例:
-
若要遍历数组中的每个元素,要使用for循环;
for(var in array) {for-body}
#即for(变量名 in 数组名) {循环体}
#变量名中保存的是数组的索引,而不是数组中的元素;
#注意:不要使用$符号;- 例:
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
#遍历数组中的每个元素;要使用for循环;显示的次序可能不太和想象的一样;
#注意:对应的变量var会遍历array数组的每个索引;
#某个数组元素不存在,直接引用后会被创建且为空,做数值操作时当做0使用;
- 例:
- 使用数组统计每一类数值各自出现次数时非常有用;
- 例:
state["LISTEN"]++
state["ESTABLISHED"]++ - 显示tcp每个状态分别出现的次数;
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
输出:
LISTEN 2
- 例:
-
awk函数:
- 数值处理:
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]}}'
- 数值处理:
-
awk中调用shell命令:
- system命令
- 空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来。
- 例:
awk BEGIN'{system("hostname") }'
awk 'BEGIN{score=100; system("echo your score is " score) }'
-
练习:
- awk打印奇数行:
seq 10 | awk 'i=!i' -
awk打印偶数行:
seq 10 | awk '!(i=!1)'
seq 10 | awk -v i=1 'i=!1' - awk取出文件中的重复行:
awk '!line[$0]++' file -
awk取出nginx访问成功的ip的次数
awk '{ip[$1]++}END{for (i in ip){print i,ip[i]}}' /usr/local/nginx/log/access.log
awk '{ip[$1]++}END{for(i in ip)if(ip[i]>1000)print i}' access_log |while read line;do
iptables -A INPUT -s $line -j REJECT
done -
统计/etc/fstab文件中每个文件系统类型出现的次数
awk '/^[^#]/{type[$3]++}END{for(i in type){print i,type[i]}}' /etc/fstab -
统计/etc/fstab文件中每个单词出现的次数
awk '{for(i=1;i<=NF;i++)word[$i]++}END{for(i in word)print word[i],i}' /etc/fstab -
提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字
echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' |awk -F "" '{for(i=1;i<=NF;i++){if($i ~ /[0-9]/)sum[$i]=$i}}END{for(i in sum)print sum[i]}' - 解决DOS***生产案例:根据web日志或者或者网络连接数,监控当某个IP并发连接数或者短时内PV达到100,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT
ss -tan | awk -F "[ :]+" '/^ESTAB/{ip[$(NF-2)]++}END{for(i in ip)if(ip[i]>1)system("iptables -A INPUT -s "i" -j REJECT")}'
- awk打印奇数行: