1、介绍
awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。
相对于sed常常作用于一整个行的处理,awk则比较倾向于一行当中分成数个【字段】处理,因此,awk相当适合处理小型的数据数据处理。同时,awk也是一种报表生成器,就是对文件进行格式化处理的,这里的格式化不是文件系统的格式化,而是对文件内容进行各种“排版”,进而格式化显示;awk有多个版本,目前Centos7上使用的awk属于GNU awk( gawk)
[root@client_1]# ll `which awk`
lrwxrwxrwx. 1 root root 4 Feb 3 05:14 /usr/bin/awk -> gawk
2、基本用法
awk [-v var=value | -F “分隔符”] ‘program’ file
awk [-v var=value | -F “分隔符”] ‘BEGIN{action;… }pattern{action;… }END{action;… }’ file
awk [-v var=value | -F “分隔符” ] -f scripfile file
参数
-F “分隔符” 指定输入分隔符,可以是字符串或正则表达式,如-F:
-v var=value 变量赋值
-f scripfile 从脚本文件中读取awk命令进行执行
说明
program : 通常放在单引号下面,也就是: pattern{action statements},为awk对数据主要的处理指令
pattern: 为过滤条件,就是对读入的行进行指定的过滤,如果不加,就是所有的行都符合条件
action statements:对过滤后的数据进行的处理,放在{}内指明,输出主要用 print, printf
BEGIN、END都是可选的,BEGIN会在program 执行前就执行,END则是在program执行后执行
分割符、域和记录
• awk执行时,由分隔符分隔的字段(域)标记$1,$2...$n称为域标识。$0
为所有域,注意:此时和shell中变量$符含义不同
• 文件的每一行称为记录
• 省略action,即program只有pattern。则默认会执行 print $0 的操作
Note:
如果 program中字符串不加上"",就会当成变量,数字则不需要,也就是-v var=value 定义了参数,在program中可直接使用
3、awk工作原理
第一步:执行BEGIN{action;… }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块
补充:
- BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
- END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
- pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供
pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
4、awk用法讲解
4.1、print
print格式:print item1, item2, …
要点:
(1) 逗号分隔符
(2) 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
(3) 如省略item,相当于print $0
举例:获取当前系统用户及其uid
#$1,$3之间的 “,”,输出后就是 空格
[root@client_1 ~]# awk -F ":" '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
....
“\t”:以tab键作为分隔符
[root@client_1 ~]# awk -F ":" '{print $1"\t"$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
...
也可以自定义分隔符
[root@client_1 ~]# awk -F ":" '{print $1"---"$3}' /etc/passwd
root---0
bin---1
daemon---2
adm---3
lp---4
...
4.2 、内置和自定义变量
FS:输入字段分隔符,默认为空白字符
OFS:输出字段分隔符,默认为空白字符
RS:输入记录分隔符,指定输入时的换行符
ORS:输出记录分隔符,输出时用指定符号代替换行符
NF:字段数量
NR:记录号
FNR:各文件分别计数,记录号
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数
举例说明
awk -F “:” 等价于 awk -v FS=":" FS为awk内置的字段分隔符,默认为空白字符
[root@client_1 ~]# awk -v FS=":" '{print $1FS$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
lp:4
...
FS还可以引用shell中的变量
[root@client_1 ~]# fs=":"
[root@client_1 ~]# awk -v FS=$fs '{print $1FS$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
...
查看分区使用情况,并且舍弃掉%
[root@client_1 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 470M 0 470M 0% /dev
tmpfs 487M 0 487M 0% /dev/shm
tmpfs 487M 8.3M 478M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 4.0G 14G 24% /
/dev/sda1 1014M 171M 844M 17% /boot
tmpfs 98M 12K 98M 1% /run/user/42
tmpfs 98M 0 98M 0% /run/user/0
[root@client_1 ~]# df -h | awk -F " +|%" '{print $1,$5}'
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 2
tmpfs 0
/dev/mapper/centos-root 24
/dev/sda1 17
tmpfs 1
tmpfs 0
Note:这种可作为报警,超过80,触发报警机制
以 [ 或空格作为分隔符
awk -F "[\[ ]" '{print $5}' access_log
OFS为输出分隔符,FS是以什么作为分割的,而OFS是分割后,输出的时候,分隔符用什么来表示
[root@client_1 ~]# awk -F ":" -v OFS="--" '{print $1,$3}' /etc/passwd
root--0
bin--1
daemon--2
adm--3
...
RS:输入记录分割符,也就是这个符号定义了到哪是一条记录,默认是换行符 \n.
之前说awk每次读入一行,准确的说,是读入一条记录
下面例子中定义了以“;”结束,才为一条记录
[root@client_1 ~]# cat tets.txt
aa,bb,cc
dd;eeff;gg,
hh;
[root@client_1 ~]# awk -F "," -v RS=";" '{print $1}' tets.txt
aa
eeff
gg
ORS是输出行按照什么符号作为记录的分隔符,默认是换行符 \n.
[root@client_1 ~]# cat tets.txt
aa,bb,cc
dd;eeff;gg,
hh;
[root@client_1 ~]# awk -F "," -v RS=";" '{print $1}' tets.txt
aa
eeff
gg
[root@client_1 ~]# awk -F "," -v RS=";" -v ORS="#" '{print $1}' tets.txt
aa#eeff#gg#
NF是字段数量
查看下每行以“:”分割后,有多少个字段
[root@client_1 ~]# awk -F ":" '{print NF}' /etc/passwd
7
7
7
...
$NF则可以代表最后一行
#获取倒数第二行信息
[root@client_1 ~]# awk -F ":" '{print $(NF-1)}' /etc/passwd
/root
/bin
/sbin
/var/adm
/var/spool/lpd
...
获取目前连接的IP地址
[root@client_1 ~]# ss -nt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 52 192.168.0.101:22 192.168.0.106:60850
[root@client_1 ~]# ss -nt| awk -F " +|:" '{print $(NF-2)}'
Address
192.168.0.106
NR 记录号,默认情况下是行号
[root@client_1 ~]# awk -F ":" '{print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
...
FNR:每个文件独立编号
FILENAME :当前文件名
[root@client_1 ~]# awk -F":" '{print FNR,FILENAME,$1}' /etc/passwd /etc/shadow
1 /etc/passwd root
2 /etc/passwd bin
...
1 /etc/shadow root
2 /etc/shadow bin
3 /etc/shadow daemon
除了以上这些内置变量,还有其他的变量
自定义变量
[root@client_1 ~]# awk -v NAME='test' 'BEGIN{print NAME}'
test
[root@client_1 ~]# awk -v NAME='test' 'BEGIN{print NAME;NAME="test1";print NAME}'
test
test1
4.3 格式化输出-printf
1、格式化输出:printf “FORMAT”, item1, item2, …
(1) 必须指定FORMAT
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面每个item指定格式符
2、 格式符:与item一一对应
%c:显示字符的ASCII码
%d, %i:显示十进制整数
%e, %E:显示科学计数法数值
%f:显示为浮点数,默认右对齐
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串,默认右对齐
%u:无符号整数
%%:显示%自身
3、修饰符
#[.#] 第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
- 左对齐(默认右对齐) %-15s
- 显示数值的正负符号 %+d
举例
-%10s:左对齐,占据10个宽度,为字符串
%4.1f代表浮点数,占据4个宽度,其中1个小数位,取第一个小数(不是四舍五入)
%s、%f默认都是右对齐
[root@client_1 ~]# echo "aaa:123.456" | awk -F ":" '{printf "%-10s %4.1f",$1,$2}'
aaa 123.5
note:当宽度小于实际值位数的时候,对齐的效果就不会有用
[root@client_1 ~]# awk -F ":" '{printf "%20s--%6d \n",$1,$3}' /etc/passwd
root-- 0
bin-- 1
daemon-- 2
adm-- 3
lp-- 4
sync-- 5
shutdown-- 6
halt-- 7
mail-- 8
....
[root@client_1 ~]# awk -F ":" '{printf "%-20s--%6d \n",$1,$3}' /etc/passwd
root -- 0
bin -- 1
daemon -- 2
adm -- 3
lp -- 4
sync -- 5
shutdown -- 6
halt -- 7
...
补充:
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
4.4、操作符
算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值
字符串操作符:没有符号的操作符,字符串连接
*赋值操作符:
=, +=, -=, =, /=, %=, ^=,++, –
[root@client_1 ~]# awk 'BEGIN{i=1;i++;print i}'
2
比较操作符:
==, !=, >, >=, <, <=
'$3>=1000' 相当于'$3>=1000{print $0}' 默认省略了{print $0}
[root@client_1 ~]# awk -F ":" '$3>=1000' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
xulx:x:1000:1000:xulx:/home/xulx:/bin/bash
模式匹配符:
~:左边是否和右边匹配,包含
!~:是否不匹配
查找第一列包含root的行
[root@client_1 ~]# awk -F ":" '$1 ~ "root"' /etc/passwd
root:x:0:0:root:/root:/bin/bash
将/etc/fstab中uuid开头的行显示出来
[root@client_1 ~]# awk '$0 ~ "^UUID"' /etc/fstab
UUID=9f1ee330-7b0f-4c5f-98e2-64662d483664 /boot xfs defaults 0 0
或
[root@client_1 ~]# awk '$1 ~ "^UUID"' /etc/fstab
UUID=9f1ee330-7b0f-4c5f-98e2-64662d483664 /boot xfs defaults 0 0
逻辑操作符:与&&,或||,非!
查找UID小于1000的用户
[root@client_1 ~]# awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
root
bin
daemon
adm
lp
查找UID小于100的用户
[root@client_1 ~]# awk -F: '$3<100 {print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
...
或
[root@client_1 ~]# awk -F: '!($3>=100) {print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
...
条件表达式(三目表达式)
selector?if-true-expression:if-false-expression
根据UID判断用户是系统用户还是普通用户
[root@client_1 ~]# awk -F: '{$3>=1000?usertype="Common user":usertype="System user";printf "%-20s:%20s\n",$1,usertype}' /etc/passwd
root : System user
bin : System user
daemon : System user
adm : System user
lp : System user
...
nfsnobody : Common user
4.5、PATTERN
PATTERN:根据pattern条件,过滤匹配的行,再做处理
(1)如果未指定:空模式,匹配每一行
(2) /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来
#过滤以UUID开头的行
[root@client_1 ~]# awk '/^UUID/' /etc/fstab
UUID=9f1ee330-7b0f-4c5f-98e2-64662d483664 /boot xfs defaults 0 0
#不是以UUID开头的行
[root@client_1 ~]# awk '!/^UUID/' /etc/fstab
#
# /etc/fstab
# Created by anaconda on Sun Feb 2 16:13:20 2020
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
(3) relational expression: 关系表达式,结果为“真”才会被处理
真:结果为非0值,非空字符串
假:结果为空字符串或0值
示例:
awk -F: 'i=1;j=1{print i,j}' /etc/passwd
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/passwd
#当过滤条件为0时,代表为假,所以无内容
[root@client_1 ~]# awk -v i=0 'i' /etc/passwd
[root@client_1 ~]# awk -v i="0" 'i' /etc/passwd
[root@client_1 ~]# awk -v i=1 'i' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
...
查看磁盘空间使用以/dev/sd开头的信息,并显示其尺寸
[root@client_1 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 470M 0 470M 0% /dev
tmpfs 487M 0 487M 0% /dev/shm
tmpfs 487M 8.3M 478M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 4.0G 14G 24% /
/dev/sda1 1014M 171M 844M 17% /boot
tmpfs 98M 12K 98M 1% /run/user/42
tmpfs 98M 0 98M 0% /run/user/0
[root@client_1 ~]# df |awk -F " +|%" '/^\/dev\/sd/{print $1,$5}'
/dev/sda1 17
获取处于连接状态下,远程服务器的IP
[root@client_1 ~]# ss -nt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 52 192.168.0.101:22 192.168.0.106:60850
[root@client_1 ~]# ss -nt | awk -F" +|:" '/^ESTAB/{print $(NF-2)}' |sort |uniq -c|sort -nr
1 192.168.0.106
为什么会显示如下结果呢?因为i=1 后面默认隐藏了 {print $0}
[root@client_1 ~]# awk -F: 'i=1;j=1{print i,j}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
1 1
bin:x:1:1:bin:/bin:/sbin/nologin
1 1
daemon:x:2:2:daemon:/sbin:/sbin/nologin
1 1
adm:x:3:4:adm:/var/adm:/sbin/nologin
1 1
第一行 root行被过滤掉了,因为i初始值为空,所以从第二行开始
[root@client_1 ~]# awk -F: 'i++' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
....
同理
[root@client_1 ~]# seq 10 | awk 'i++'
2
3
4
5
6
7
8
9
10
取奇数
[root@client_1 ~]# seq 10 | awk 'i=!i'
1
3
5
7
9
(4) line ranges:行范围
查找/etc/passwd中以b开头的行到f开头的行 之间的内容
[root@client_1 ~]# awk '/^b/,/^f/' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
Note:上述有个问题,就是如果有多段匹配,就都会找出来
[root@client_1 ~]# awk '/^s/,/^f/' /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
libstoragemgmt:x:998:996:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
colord:x:997:995:User for colord:/var/lib/colord:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
saslauth:x:996:76:Saslauthd user:/run/saslauthd:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
setroubleshoot:x:995:992::/var/lib/setroubleshoot:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
gluster:x:994:991:GlusterFS daemons:/run/gluster:/sbin/nologin
chrony:x:993:990::/var/lib/chrony:/sbin/nologin
radvd:x:75:75:radvd user:/:/sbin/nologin
qemu:x:107:107:qemu user:/:/sbin/nologin
unbound:x:992:989:Unbound DNS resolver:/etc/unbound:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
geoclue:x:991:987:User for geoclue:/var/lib/geoclue:/sbin/nologin
pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
saned:x:990:984:SANE scanner daemon user:/usr/share/sane:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
gnome-initial-setup:x:989:983::/run/gnome-initial-setup/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
xulx:x:1000:1000:xulx:/home/xulx:/bin/bash
查找s开头到f开头之间的内容,当首次查找到s-f的内容后,会继续往下查找 符合条件的行。但是如果匹配到s行后,一直查到最后都没找到f开头的行作为结尾。那么这之间所有的行都会被显示,也就发生了上面的现象。
(5) BEGIN/END模式
BEGIN{}:仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
#获取当前系统用户及其uid
[root@client_1 ~]# awk -F ":" 'BEGIN{print "user","uid"}{print $1,$3}' /etc/passwd
user uid
root 0
bin 1
daemon 2
adm 3
lp 4
...
4.6、控制语句
awk控制语句
用if实现输出奇数行
[root@client_1 ~]# seq 10 | awk '{if(NR%2==1){print $0}}'
1
3
5
7
9
[root@client_1 ~]# seq 10 | awk '{if(NR%2==1){print "奇数行"}else {print "偶数行"}}'
奇数行
偶数行
奇数行
偶数行
奇数行
偶数行
奇数行
偶数行
奇数行
偶数行
过滤出/dev/sd的分区系统中,磁盘空间适用超过10%的
[root@client_1 ~]# df -h | awk -F " +|%" '/^\/dev\/sd/{if($5>10)print $1,$5}'
/dev/sda1 17
while循环
统计字符串个数
[root@client_1 ~]# awk '/linux16/' /boot/grub2/grub.cfg
linux16 /vmlinuz-3.10.0-1062.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=en_US.UTF-8
linux16 /vmlinuz-0-rescue-99e2e8e857a640ab8c9eb6ce1785138f root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
[root@client_1 ~]# awk '/linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /boot/grub2/grub.cfg
linux16 7
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=/dev/mapper/centos-root 28
ro 2
crashkernel=auto 16
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
rhgb 4
quiet 5
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-99e2e8e857a640ab8c9eb6ce1785138f 50
root=/dev/mapper/centos-root 28
ro 2
crashkernel=auto 16
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
rhgb 4
quiet 5
for循环
计算1到100的和
#awk while循环
[root@client_1 ~]# awk 'BEGIN{i=1;sum=0;while(i<=100){sum=sum+i;i++};print sum}'
5050
#awk for循环
[root@client_1 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum=sum+i};print sum}'
5050
#shell循环
[root@client_1 ~]# sum=0;for((i=1;i<100;i++));do let sum+=i;done;echo $sum
4950
#seq计算,-s是指定分隔符,指定了分隔符为+。然后通过bc计算
[root@client_1 ~]# seq -s+ 100 |bc
5050
计算时间对比,awk效率最高,shell效率最低
[root@client_1 ~]# time (sum=0;for((i=1;i<100000;i++));do let sum+=i;done;echo $sum)
4999950000
real 0m0.768s
user 0m0.713s
sys 0m0.053s
[root@client_1 ~]# time (seq -s+ 100000 |bc)
5000050000
real 0m0.057s
user 0m0.047s
sys 0m0.010s
[root@client_1 ~]# time awk 'BEGIN{sum=0;for(i=1;i<=100000;i++){sum=sum+i};print sum}'
5000050000
real 0m0.020s
user 0m0.018s
sys 0m0.002s
continue,跳过本次循环
break,结束循环
next,提前结束对本行处理而直接进入下一行处理(awk自身循环)
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
#continue,跳过本次循环
[root@client_1 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
2500
#break,跳出循环
[root@client_1 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==66)break;sum+=i}print sum}'
2145
switch语句
语法:switch(expression) {case VALUE1 or /REGEXP/: statement1; case
VALUE2 or /REGEXP2/: statement2; …; default: statementn}
[root@client_1 ~]# cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Sun Feb 2 16:13:20 2020
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=9f1ee330-7b0f-4c5f-98e2-64662d483664 /boot xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
[root@client_1 ~]# awk '!/^#/{switch($3) { case "xfs":print "i am xfs" ; case "ext4":print "i am ext4";default : print "unknown"; }}' /etc/fstab
unknown
i am xfs
i am ext4
unknown
i am xfs
i am ext4
unknown
unknown
4.7、awk数组
关联数组:array[index-expression]
index-expression:
• (1) 可使用任意字符串;字符串要使用双引号括起来
• (2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值
初始化为“空串”
• (3) 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
举例
创建数组
[root@client_1 ~]# awk 'BEGIN{test["a"]="aaa";test["b"]="bbb";print test["a"]}'
aaa
Note:上面awk数组不灵活,必须要先知道下标,如果想要灵活遍历,就需要使用for循环
awk数组实现去重
a[$0]++,增加下标为$0的数组元素的个数,!a[$0]++会先根据a[$0]的值取反,然后才会++
[root@client_1 ~]# cat awk.txt
aa
bb
cc
dd
aa
dd
[root@client_1 ~]# awk '!a[$0]++' awk.txt
aa
bb
cc
dd
遍历数组
[root@client_1 ~]# awk 'BEGIN{test["a"]="aaa";test["b"]="bbb";for(i in test){print i,test[i]}}'
a aaa
b bbb
获取文件指定信息列,并添加到文件
[root@client_1 ~]# cat awk.txt
1 aa.test.com
2 bb.test.com
3 cc.test.com
4 dd.test.cm
[root@client_1 ~]# awk -F "[ .]" '{print $2}' awk.txt
aa
bb
cc
dd
[root@client_1 ~]# awk -F "[ .]" '{print $2}' awk.txt >>awk.txt
[root@client_1 ~]# cat awk.txt
1 aa.test.com
2 bb.test.com
3 cc.test.com
4 dd.test.cm
aa
bb
cc
dd
#获取最大值与最小值
[root@client_1 ~]# cat awk.txt
123,342,1131,32,56,78,1313,333,65,73,223,666
[root@client_1 ~]# awk -F "," '{max=$i;min=$i;for(i=1;i<=NF;i++){if(max<$i){max=$i}else if(min>$i){min=$i}}}END{print "min="min,"max="max}' awk.txt
min=32 max=1313
#求男女平均分数
[root@client_1 ~]# cat awk.txt
name gender score
a m 100
b f 99
c m 98
d f 97
e m 96
[root@client_1 ~]# awk 'NR!=1{sum[$2]+=$3;num[$2]++}END{for(i in sum){print i,sum[i]/num[i]}}' awk.txt
m 98
f 98
4.8、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)'
echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
• gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表
示的内容
echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}'
• split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所
表示的数组中,第一个索引值为1,第二个索引值为2,…
netstat -tn | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}’
举例
rand():函数本身并不随机,只是生成随机数的种子
[root@client_1 ~]# awk 'BEGIN{print rand()}'
0.237788
[root@client_1 ~]# awk 'BEGIN{print rand()}'
0.237788
[root@client_1 ~]# awk 'BEGIN{print rand()}'
0.237788
要想生成随机数,需要结合srand()函数使用
#生成0到1之间的随机数
[root@client_1 ~]# awk 'BEGIN{srand();print rand()}'
0.177516
[root@client_1 ~]# awk 'BEGIN{srand();print rand()}'
0.199206
生成1到100的整数
[root@client_1 ~]# awk 'BEGIN{srand();print int(rand()*100)}'
79
[root@client_1 ~]# awk 'BEGIN{srand();print int(rand()*100)}'
92
[root@client_1 ~]# awk 'BEGIN{srand();print int(rand()*100)}'
16
[root@client_1 ~]# awk 'BEGIN{srand();print int(rand()*100)}'
3
awk中自定义函数
[root@client_1 ~]# cat fun.awk
function max(x,y) {
x>y?var=x:var=y
return var
}
BEGIN{a=3;b=2;print max(a,b)}
[root@client_1 ~]# awk -f fun.awk
3
4.9、awk中调用shell命令
[root@client_1 ~]# awk 'BEGIN{system("hostname")}'
client_1
[root@client_1 ~]# awk 'BEGIN{system("echo name")}'
name
4.10、awk脚本
将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
向awk脚本传递参数
格式:
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/passwd
over.