awk
1. 简介
- 来由与作用
awk是由Aho, Weinberger, Kernighan三位大牛开发的程序,用于报告生成器,格式化文本输出,可接收标准输入 版本
- New awk(nawk)
- GNU awk( gawk),centos使用的为gawk
运行原理
分割符、域和记录
- awk执行时,由分隔符分隔的字段(域)标记1,1,0为所有域,注意:和shell中变量$符含义不同
- 文件读入内存空间的每一行称为记录,文本中的一行可以为多个记录
- 省略action,则默认执行 print $0 的操作
工作原理
- 执行BEGIN{action;… }语句块中的语句
- 从文件或标准输入(stdin)读取一个记录到内存空间,执行pattern{ action;… }语句块,匹配预定义的模式,如果不匹配就不处理,匹配后按照预先规定好的分隔符切割成若干字段,分割成功后,系统会对这些列分配一个域标识$1..$n,直到文件全部被读取完毕。
当读至输入流末尾时,执行END{action;…}语句块
- BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
- END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
- pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
2. 基本用法
- 格式:
awk [options] var=value ‘program’ file… 选项(options):
- -v var=value: 自定义变量
- -f programfile: 读取文件中的程序
awk [options] var=value -f programfile file…
-F:指明输入时用到的字段分隔符,分隔符可以指定多个
示例:指定[;]为记录分隔符,下例为2个记录a:b@c:d与1:2@3
1:指定[:]为字段分割符
$echo "a:b@c:d;1:2@3"|awk -v RS=";" -F":" '{print $1,$3}'
a d
1
2:指定[@:]为字段分割符
$echo "a:b@c:d;1:2@3"|awk -v RS=";" -F"[@:]" '{print $1,$3}'
a c 两个分隔符都起作用,c为第3个字段
1 3
3:以扩展正则表达式方法表示也可以
$echo "a:b@c:d;1:2@3"|awk -v RS=";" -F"@|:" '{print $1,$3}'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
program
为awk自己的语法,通常是被单引号或双引号中,建议使用单引号,因为双引号在程序中会在字符串上用,避免混用
- 格式:
- BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
‘BEGIN{ action;… } pattern{ action;… } END{ action;… }’ - 常用格式:
pattern{action statements;..}
- BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
- 释义pattern和action:
- pattern部分决定动作语句何时触发及触发事件
- action statements对数据进行处理,放在{ }内指明
- action格式: {print|printf item1, item2, …}
- 要点:
- 逗号分隔符
- 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
- 如省略item,相当于print $0
示例
例如:打印出/etc/passwd文件的第1和第3行
awk -F: '{print $1,$3}' /etc/passwd
- 1
3. awk变量
变量:分为内置和自定义变量,只要对变量赋值,就要放在选项-v的后面
3.1 内置变量
FS(field separator):输入字段分隔符,默认为空白字符,指定变量的好处在于,可以多次调用
- 示例1:定义FS=’:’,在(程序)中调用变量
$awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd
named:25
- 示例2:一次引用多个变量,中间需要使用逗号隔开
$awk -v FS=: '{print $1FS,FS,FS$3}' /etc/passwd
named: : :25
- 示例3:还可以引用shell中的变量,先声明fs=:,然后用awk调用
$fs=:;awk -v FS="$fs" '{print $1FS$3}' /etc/passwd
named:25
OFS(output field separator):指定输出字段分隔符,默认为空白字符
- 示例:以###作为字段分隔符
$awk -F":" -v OFS=### '{print $1,$3,$2}' /etc/passwd
named###25###x
RS(record separator):输入记录分隔符,指定输入时的换行符
示例:创建文本,如下awk.txt所示,[@,!,?]与[1,2,3,4]以及[A,B,C\naa,bb,cc],以[;]分隔开
1. 创建实验文本
$cat awk.txt
@,!,?;1,2,3,4;A,B,C
aa,bb,cc
2. 指定输入字段分隔符为[,],输出字段分隔符为[==],输入记录分隔符为[;]
$awk -v FS="," -v OFS="==" -v RS=";" '{print $3,$4}' awk.txt
?== <==第一个记录@,!,?没有第4个字段
3==4 <==第二个记录1,2,3,4,显示后两个
C <==第三个记录,保留原有格式换行,C\naa为第三个字段
aa==bb
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
ORS:输出记录分隔符,输出时用指定符号代替换行符
承上例,每条记录输出时默认为换行处理,可以通过ORS指定
$awk -v FS="," -v OFS="==" -v RS=";" -v ORS="***" '{print $3,$4}' awk.txt
?==***3==4***C <==记录输出时以指定的符号代替原回车换行
aa==bb***
- 1
- 2
- 3
NF:字段数量
$awk -F: '{print NF}' /etc/passwd
7 <==统计字段数量
$awk -F: '{print $(NF-6)}' /etc/passwd
named <==显示第一列内容,相当于$1,要用括号括起来
- 1
- 2
- 3
- 4
NR:记录号
$awk -F: '{print NR,$1}' /etc/passwd
45 named <==相当于多了个行号
- 1
- 2
FNR:各文件分别计数,记录号
$awk -F: '{print NR,$1}' /etc/passwd /etc/group
121 named <==一共有多少记录
处理多个文件时,使用NR会将记录号累计,不会按文件分开,这时需要使用FNR
$awk -F: '{print FNR,$1}' /etc/passwd /etc/group
45 named <==/etc/passwd文件最后一个记录
76 named <==/etc/group文件最后一个记录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
FILENAME:当前文件名
承上例,如果觉得显示不清楚,可以加上文件名
$awk -F: '{print FNR,FILENAME,$1}' /etc/passwd /etc/group
76 /etc/group named
- 1
- 2
ARGC:命令行参数的个数
$awk '{print ARGC}' /etc/passwd /etc/group
3 <==统计命令行参数的个数
- 1
- 2
ARGV:数组,保存的是命令行所给定的各参数
上例为什么是3个参数?使用ARGV查看各参数
$awk '{print ARGV[0] }' /etc/passwd /etc/group
awk <==将awk算为了一个参数
$awk '{print ARGV[2] }' /etc/passwd /etc/group
/etc/group <==第三个参数
- 1
- 2
- 3
- 4
3.2 自定义变量(区分字符大小写)
- 格式
- -v var=value 例如 -v NAME=USERNAME
- 直接在程序中定义变量{NAME=USERNAME;print…}
示例
示例:使用格式1自定义
$awk -F: -v USER=“username” -v UID=“userid” ‘{print USER":"$1,UID":"$3}’ /etc/passwd /etc/group
username:named userid:25 这样显示不是很清晰,还记得记录分隔符吗
$awk -F: -v USER=“username” -v UID=“userid” -v ORS="\n------\n" ‘{print USER":"$1"\n"UID":"$3}’ /etc/passwd /etc/group
$awk -F: -v USER=“username” -v UID=“userid” ‘{print USER":"$1,UID":"$3}’ /etc/passwd /etc/group
username:named userid:25 这样显示不是很清晰,还记得记录分隔符吗
$awk -F: -v USER=“username” -v UID=“userid” -v ORS="\n------\n" ‘{print USER":"$1"\n"UID":"$3}’ /etc/passwd /etc/group
username:screen
userid:84
username:named
userid:25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
示例:使用格式2自定义
$awk -F: -v ORS="\n------\n" '{USER="username"; UID="userid"; print USER":"$1"\n"UID":"$3}' /etc/passwd /etc/group
- 1
示例:将action放入文件,使用-f选项
1. 将程序内容放入文件awk2.txt 中
$cat awk2.txt
{USER="username"; UID="userid"; print USER":"$1"\n"UID":"$3}
2. 执行以下命令,写入文件的程序就不需要单引号了
$awk -F: -v ORS="\n------\n" -f awk2.txt /etc/passwd
- 1
- 2
- 3
- 4
- 5
4. action中的printf命令
- 格式
printf “FORMAT”, item1, item2, … 注意事项
- 必须指定FORMAT
- 不会自动换行,需要显式给出换行控制符,\n
- FORMAT中需要分别为后面每个item指定格式符
格式符:与item对应
符号 | 释义 |
---|---|
%c | 显示字符的ASCII码 |
%d, %i | 显示十进制整数 |
%c | 显示字符的ASCII码 |
%e, %E | 显示科学计数法数值 |
%g, %G | 以科学计数法或浮点形式显示数值 |
%s | 显示字符串 |
%u | 无符号整数 |
%% | 显示%自身 |
修饰符
- #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,如%3.1f
- -: 左对齐(默认右对齐),如%-15s
- +:显示数值的正负符号,如%+d
示例
示例1: 取出/etc/passwd第1,3行,并在其前分别加Username和UID
awk -F: ‘{printf "Username: %-15s,UID:%d\n",$1,$3}’ /etc/passwd
- 1
示例2:%s对应$1,表示文件第一行为字符串,在%与s中间可以添加参数[-]表示左对齐[20]表示第一列所占宽度,同理%-4d表示$2占4个字符宽度左对齐
$awk -F: '{printf"|%-20s %-4d|\n---------------------------\n",$1,$3 }' /etc/passwd
|named 25 |
---------------------------
- 1
- 2
- 3
示例3:在这里演示一下BEGIN的效果,添加表头,效果如下
$awk -F: 'BEGIN{print "|user |ID |\n----------------------------"}''{printf"|%-20s |%-4d|\n----------------------------\n",$1,$3 }' /etc/passwd
|user |ID | <==头部,BEGIN所述内容
----------------------------
|root |0 | <==文件内容,printf所述内容
----------------------------
- 1
- 2
- 3
- 4
- 5
示例4:小数处理示例
1:$echo "3.1415 1.15926" |awk '{printf "%2.2f;%1.4f",$1,$2}'
3.14;1.1593
%2.2f第一个2表示宽度,第二个2个小数点保留位数
%1.4f最后一位达到6时会进位
2:$echo "3.1415 1.15925" |awk '{printf "%2.2f;%1.4f",$1,$2}'
3.14;1.1592
%1.4f最后一位值为5时不会进位
3:$echo "3.1415 11.15925" |awk '{printf "%2.2f;%10.4f",$1,$2}'
3.14;***11.1593 <==这里为了显示效果,以***代替空格,共10个宽度
%10.4f宽度起作用了,说明宽度如果小于实际占用值,会以实际宽度为准
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5. 操作符
5.1 算术操作符:
操作符 | 定义 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
^ | 幂 |
-x | 转换为负数 |
+x | 转换为数值 |
5.2 赋值操作符:
操作符 | 定义 |
---|---|
= | 赋值 |
+= | 将加的结果赋给变量 |
-= | 将减的结果赋给变量 |
*= | 将乘的结果赋给变量 |
/= | 将除的结果赋给变量 |
%= | 将取余的结果赋给变量 |
^= | 将取幂的结果赋给变量 |
++ | 变量加1 |
– | 变量减1 |
- 示例:下面两语句有何不同
awk ‘BEGIN{i=0;print ++i,i}’
awk ‘BEGIN{i=0;print i++,i}’
[root@hai7 ~]$awk 'BEGIN{i=10;print ++i,i}'
11 11
先递增,再将递增值打印
[root@hai7 ~]$awk 'BEGIN{i=10;print i++,i}'
10 11
先打印i值,再递增
- 1
- 2
- 3
- 4
- 5
- 6
5.4 比较操作符:
操作符 | 定义 |
---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
5.5 模式匹配符
操作符 | 定义 |
---|---|
~ | 左边是否和右边匹配 |
!~ | 是否不匹配 |
- 示例
1:筛选文件/etc/passwd中包含root的行
[root@hai7 ~]$awk -F: '$0 ~ /root/{print NR$1}' /etc/passwd
1 root
10 operator
2:筛选文件/etc/passwd中以root开头的行
[root@hai7 ~]$awk -F: '$0 ~ "^root"{print NR,$1}' /etc/passwd
1 root
3:筛选文件/etc/passwd中不包含root的行
[root@hai7 ~]$awk -F: '$0 !~ /root/{print NR,$1}' /etc/passwd
2 bin
9 mail
11 games
4:筛选第三字段为0的行
[root@hai7 ~]$awk -F: '$3==0' /etc/passwd
root:x:0:0:root:/root:/bin/bash
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
5.6 逻辑操作符:与&&,或||,非!
操作符 | 定义 |
---|---|
&& | 逻辑与 |
! | 逻辑非 |
- 示例
1:筛选第3个字段大于等于50并且小于等于60的行
[root@hai7 ~]$awk -F: '$3>=50 && $3<=60 {print NR,$1}' /etc/passwd
31 tss
2:筛选第3个字段等于0或大于等于1000的行
[root@hai7 ~]$awk -F: '$3==0 || $3>=1000 {print NR,$1}' /etc/passwd
1 root
29 nfsnobody
43 dong
3:筛选第3个字段小于等于1000以外的行
$awk -F: '!($3<=1000) {print NR,$1}' /etc/passwd
29 nfsnobody
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
条件表达式(三目表达式):
(selector1)?if-true(expression2):if-false(expression)
先求表达式selector1的值,如果为真,则执行表达式2,并返回表达式2的结果;如果表达式1的值为假,则执行表达式3,并返回表达式3的结果。
- 示例:
先定义条件第3字段大于等于1000,如果成立则打印type="user",不成立打印type="sys"
[root@hai7 ~]$awk -F: '{$3>=1000?type="user":type="sys";printf"%+15s %s\n" ,$1,type}' /etc/passwd
root sys
nfsnobody user
- 1
- 2
- 3
- 4
6 awk-PATTERN
PATTERN:根据pattern条件,过滤匹配的行,再做处理
- 如果未指定:空模式,匹配每一行
/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
例1:判断磁盘利用率,取出设备行
$df|awk -F " +|%" '/^\/dev\/sd/{print $1,$5}'
/dev/sda2 11
/dev/sda3 1
/dev/sda1 16
例2:统计磁盘文件系统类型
$awk -F" +" '/^UUID/{print $3}' /etc/fstab|sort|uniq -c
2 swap
3 xfs
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
relational expression: 关系表达式,结果为“真”才会被处理
- 真:结果为非0值,非空字符串
- 假:结果为空字符串或0值
示例
- 空格或者非0数值,返回值为非空,结果都为真,可以打印结果
awk -F: '" "{print $1}' /etc/passwd
awk -F: '1{print $1}' /etc/passwd
awk ‘!0’ /etc/passwd
- 空值或者0,返回值为空,结果为假,不做处理
awk -F: ' ""{print $1} ' /etc/passwd
awk -F: '0{print $1}' /etc/passwd
awk ‘!12345’ /etc/passwd
任何非0值取反都为空 下例分为两条命令
[root@hai7 ~]$awk -F: 'i=1;j=1{print i,j}' /etc/passwd
named:x:25:25:Named:/var/named:/sbin/nologin
1 1
i=1;j=1{print i,j}是两条命令
i=1省略了{print ...}所以默认打印$0
seq 10 |awk ‘i=0’
- 1
- 2
- 3
- 4
- 5
- 6
打印奇数行,因为i没有指定值所以为空,第一次执行时为空=!空,所以打印1,第二次执行时,非空=!非空值为0,所以不处理,以此类推,可以通过
seq 10 | awk '{i=!i;print i}'
查看返回结果 seq 10 |awk 'i=1'
打印所有数字seq 10 | awk 'i=!i'
打印奇数行seq 10 | awk '!(i=!i)'
打印偶数行seq 10 |awk -v i=1 'i=!i'
打印偶数行
line ranges:行范围
利用正则表达式写法取范围
startline,endline:/pat1/,/pat2/ 不支持直接给出数字格式
awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwd
先要处理指定的行范围,可以利用NR记录编号来实现
awk -F: '(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
BEGIN/END模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
示例
示例1:
[root@hai7 ~]$awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "end file"}' /etc/passwd
USER USERID <==BEGIN的内容
root:0
apache:48
named:25
end file <==END的内容
示例2:
$awk -F: '{print "USER USERID";print $1":"$3} END{print "end file"}' /etc/passwd
USER USERI <==不是开始处理的文本,会被循环打印
root:0
USER USERI
bin:1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
7. awk action
7.1awk控制语句if-else
- 语法
if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}else{statement3} - 使用场景:对awk取得的整行或某个字段做条件判断
- 示例:
示例1:取出第3字段大于等于1000的行
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '$3>=1000{print $1,$3}' /etc/passwd
效果相同,格式不同控制语句是action里的要写在花括号内,判断语句是属于patter的要写在花括号外面
- 1
- 2
- 3
示例2:取磁盘利用率高于5%的
$df|awk -F" +|%" '/^\/dev\/sd/{if($5>5){print $1,$5;print "it is full"}}'
/dev/sda2 11
it is full
/dev/sda1 16
it is full
- 1
- 2
- 3
- 4
- 5
示例3:取访问日志中IP地址重复次数超过100的,放到防火墙中
[root@hai7 ~]$ab -c10 -n 100 http://172.20.50.201/ 实验准备多次访问某地址
[root@hai7 ~]$cat /var/log/httpd/access_log|awk -F" " '{print $1}'|uniq -c|awk -F" " '{if($1>100){print $2}'|while read ip;do iptables -A INPUT -s $ip -j REJECT;done
[root@hai7 ~]$iptables -F 清空防火墙
- 1
- 2
- 3
示例3:shell类型使用bash的行
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
- 1
示例4:第3字段大于等于1000的标记为Common user,否则标记为root or Sysuser
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
- 1
- 2
7.2 awk控制语句 while循环
- 语法:
while(condition){statement;…} - 条件“真”,进入循环;条件“假”,退出循环
- 使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用 - 示例:
示例1:用awk写1..100相加
$awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++};print sum}'
5050
- 1
- 2
示例2:找出不管前面多少空格linux16开头的行,统计各字段字符串长度
[root@hai7 ~]awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg
示例中length是系统函数,用来统计括号中字符串长
[root@hai7 ~]$awk 'BEGIN{print length("sdgdgasdfwergejut")}'
17
- 1
- 2
- 3
- 4
- 5
示例3:统计字段字符串长度超过10的
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}' /etc/grub2.cfg
- 1
示例4:判断随机数最大值与最小值
1:生成以逗号为分隔符的随机数到文件f1
[root@hai7 ~]$(echo "$RANDOM" ;for((i=1;i<100;i++));do echo -n ",$RANDOM";done)>f1
2: 取出随机数最大值和最小值
[root@hai7 ~]$awk -F',' '{max=$1;min=$1;i=2;while(i<=NF){if($i>max){max=$i}else if($i<min){min=$i};i++;}}END{print "max="max,"min="min}' f1
max=32377 min=148
3:判断结果是否正确
[root@hai7 ~]tr ',' '\n' <f1.txt|shot -nr
- 1
- 2
- 3
- 4
- 5
- 6
- 7
7.3 控制语句 do-while循环
- 语法
do {statement;…}while(condition) - 意义
无论真假,至少执行一次循环体 - 示例:
示例1:1..100相加
$awk 'BEGIN{sum=0;i=1;do {sum+=i;i++}while(i<=100);print sum}
- 1
示例2:统计linux16行,大于10个字符的字段
$awk '/^[[:space:]]*linux16/{i=1;do {if(length($i)>=10){print $i,length($i)}; i++}while(i<=NF)}' /etc/grub2.cfg
- 1
7.4 awk控制语句 for循环
- 语法
for(expr1;expr2;expr3) {statement;…} - 常见用法:
for(variable assignment;condition;iteration process){for-body} - 特殊用法:能够遍历数组中的元素
- 语法:for(var in array) {for-body} - 示例:
示例1:统计linux16行,各字段字符长度,
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
- 1
示例2:从1+到100相加
awk 'BEGIN{sum=0;for(i=1;i<=100;i++)sum+=i;print sum}'
- 1
7.5 awk控制语句 switch语句
语法:
swich定义的表达式,是否为case后的只或者正则表达式,如果是则执行statement1, 如果不匹配则继续判断第二个case后的值是否匹配,匹配则执行statement2,都不匹配则执行default后续。格式
switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; …; default: statementn}
7.6 awk控制语句 break、continue及next
break [n] 结束整个循环体
[root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i} ;print sum}'
1225
循环到50,就不往后加了
- 1
- 2
- 3
1:i%2==1意思为奇数循环,跳过奇数循环,得偶数总和
[root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==1)continue;sum+=i} ;print sum}'
2550
2:结束第50次循环,会继续进行后续循环
[root@hai7 /etc/selinux]$awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)continue;sum+=i} ;print sum}'
5000
- 1
- 2
- 3
- 4
- 5
- 6
- next:
提前结束对本行处理而直接进入下一行处理(awk自身循环)
打印 奇数行
awk -F: '{if($3%2!=0)next;print NR,$1,$3}' /etc/passwd
- 1
- 2
8. 性能比较
使用awk、shell、bc计算100000相加,参考所用时间,判断工作性能
1、awk for循环
[root@hai7 /etc/selinux]$time awk 'BEGIN{sum=0;for(i=1;i<=100000;i++)sum+=i ;print sum}'
5000050000
real 0m0.011s
user 0m0.010s
sys 0m0.001s
- 1
- 2
- 3
- 4
- 5
- 6
2、awk while循环
2. $time awk 'BEGIN{sum=0;i=1;while(i<=100){sum+=i;i++};print sum}'
5050
real 0m0.003s
user 0m0.002s
sys 0m0.001s
- 1
- 2
- 3
- 4
- 5
- 6
3、shell for循环
$time for((sum=0,i=1;i<=100000;i++));do let sum+=i;done;echo $sum
real 0m0.666s
user 0m0.666s
sys 0m0.000s
- 1
- 2
- 3
- 4
- 5
4、shell while循环
$time ( sum=0;i=1;while [ $i -le 100000 ] ;do let sum+=i;let i++;done;echo $sum)
5000050000
real 0m1.064s
user 0m0.992s
sys 0m0.072s
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5、bc计算
$time seq -s + 100000|bc
5000050000
real 0m0.041s
user 0m0.036s
sys 0m0.007s
- 1
- 2
- 3
- 4
- 5
- 6
9. awk数组
9.1 awk支持关联数组
arr[“key”]=value 键值对,每个键后面对应着相应的值, 当按下相应的键时, 就会输出相应的结果
- 格式
array[index-expression] - index-expression:
- 可使用任意字符串;字符串要使用双引号括起来
- 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
- 若要遍历数组中的每个元素,要使用for循环,格式为for(var in array) {for-body}
- 示例
•awk ‘!arr[0]++′dupfile去掉重复行,例如文件内容为0]++′dupfile去掉重复行,例如文件内容为cat f2
abc
cvb
abc
当awk读入第一行时,arr的下标为abc,没有赋值为空,取反为非空,所以打印第一行,arr[abc]=1
读入第二行是,arr的下标为cbv,没有赋值为空,取反为非空,所以打印
读入第三行时,arr的下标为abc,值为1,取反为空,所以不打印 arr[abc]=2
去掉重复行还可以使用sort -u f2
•awk ‘{!arr[0]++;print0]++;print0, arr[$0]}’ dupfile
分别定义数组weekdays的键值对为[“mon”]=”Monday”,[“tue”]=”Tuesday”,通过for来遍历
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
- 1
统计tcp状态,NF代表最后一个字段,i表示值,state[i]表示累计结果
netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
- 1
统计IP访问次数,f1为导出的IP访问日志格式为,默认以空格为分隔符
172.20.7.53 - - [10/Sep/2018:10:18:53 +0800] “GET / HTTP/1.0” 403 4961 “-” “ApacheBench/2.3”
awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' f1|sort -rn
- 1
分别统计男生女生的平
[root@hai7 /data]$cat f2
name grade boyorgirls
A 76 G
B 98 M
C 66 M
D 95 G
E 86 G
F 79 M
[root@hai7 /data]$awk '!/^name/{if($3=="M"){score_m+=$2;number_m++}else{score_g+=$2;number_g++}}END{print "avg_m="score_m/number_m"","avg_g="score_g/number_g}' f2
avg_m=81 avg_g=85.6667
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
方法二
[root@hai7 /data]$awk 'NR!=1{score[$3]+=$2;num[$3]++}END{for(sex in num){print sex":avg="score[sex]/num[sex]}}' f2
G:avg=85.6667
M:avg=81
- 1
- 2
- 3
9.2 自定义函数
- 格式:
function name ( parameter, parameter, … ) {
statements
return expression
} - 示例:
cat fun.awk
function max(v1,v2) {
v1>v2?var=v1:var=v2
return var
}
BEGIN{a=3;b=2;print max(a,b)}
awk –f fun.awk
3
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
9.3 awk中调用shell命令
空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用”“引用起来。
- 格式 system(“$var”)
- 示例
awk BEGIN'{system("hostname") }'
awk 'BEGIN{score=100; system("echo your score is " score) }'
9.4 awk脚本
将awk程序写成脚本,直接调用或执行
- 示例
1:将pattern{action statements;..}写到文本中
[root@hai7 /data]$cat f3
{if($3>=1000)print $1,$3}
2:调用文本来筛选文件
$awk -F: -f f3 /etc/passwd
nfsnobody 65534
v9 1000
3:建立awk执行脚本
$cat f3
#!/bin/awk -f <==脚本解释器与shell不同
#this is a awk script
{if($3>=1000)print $1,$3} <==pattern{action statements;..}
4:加执行权限
chmod +x f3
5:调用脚本来过滤文本
./f3 –F: /etc/passwd
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
9.4.1 向awk脚本传递参数
- 格式
awkfile var=value var2=value2… Inputfile - 注意事项
- 在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。
- 可以通过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数
- 示例
1:编辑awk脚本
cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
2:加执行权限
chmod +x test.awk
3:执行脚本,向脚本传递变量如下,也可以使用-v min=100,每个变量前都要加-v
./test.awk -F: min=100 max=200 /etc/passwd
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
10. awk数值处理
- rand():
返回0和1之间一个随机数,需要配合srand实现,srand是用来生成随机数的种子
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
int 取整数
例:生成随机1-1之间的小数awk 'BEGIN{srand();print rand()}'
例:生成0-100之间的数awk 'BEGIN{srand();print int(rand()*100)}'
length([s])
返回指定字符串的长度- sub(r,s,[t])
对t字符串进行搜索r(支持正则表达式)表示的模式匹配的内容,并将第一个匹配的内容替换为s
[root@hai7 /data]$echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08 - gsub(r,s,[t])
对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
[root@hai7 /data]$echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08 - split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…
用:做分隔符将2008:08:08 08:009:09分割为5段,分别存放在下标为1的str[1]=2008,str[2]=08...
echo "2008:08:08 08:009:09"|awk '{split($0,str,":")}END{for(i in str)print i,str[i]}'
4 009
5 09
1 2008
2 08
3 08 08
- 1
- 2
- 3
- 4
- 5
- 6
- 7
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-7f770a53f2.css" rel="stylesheet">
</div>