awk 详解 (上)

awk 介绍

awk 是一种编程语言,用于在 linux/unix 下对文本和数据进行处理。数据可以来自标准输入 (stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是 linux/unix 下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk 有很多内建的功能,比如数组、函数等,这是它和 C 语言的相同之处,灵活性是 awk 最大的优势。


awk 的工作原理

awk 'BEGIN{ commands } pattern{ commands } END{ commands }' 

** 第一步:** 执行 BEGIN{commands} 语句块中的语句;
** 第二步:** 从文件或标准输入 (stdin) 读取一行,然后执行 pattern{
commands } 语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
** 第三步:** 当读至输入流末尾时,执行 END{commands} 语句块。

BEGIN 语句块在 awk 开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在 BEGIN 语句块中。

END 语句块在 awk 从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在 END 语句块中完成,它也是一个可选语句块。

pattern 语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供 pattern 语句块,则默认执行 { print
},即打印每一个读取到的行,awk 读取的每一行都会执行该语句块。


awk 应用

1. 截取文档中的某个段

[root@localhost ~]# mkdir awk
[root@localhost ~]# cp /etc/passwd awk/test.txt
[root@localhost ~]# cd awk
[root@localhost awk]# ls
test.txt
[root@localhost awk]# awk -F ':' '{print $1}' test.txt     
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody
systemd-bus-proxy
.....................

说明:-F 选项的作用是指定分隔符。如果不加 -F 选项,则以空格或者 tab 或空白字符为分隔符。print 为打印,用来打印某个字段。$1 为第一字段,$2 为第二字段, 以此类推。但 $0 比较特殊,它表示整行:

[root@localhost awk]# awk -F ':' '{print $0}' test.txt 
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/sbin/nologin
daemon❌2:2:daemon:/sbin:/sbin/nologin
adm❌3:4:adm:/var/adm:/sbin/nologin
lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
sync❌5:0:sync:/sbin:/bin/sync
shutdown❌6:0:shutdown:/sbin:/sbin/shutdown
halt❌7:0:halt:/sbin:/sbin/halt
mail❌8:12:mail:/var/spool/mail:/sbin/nologin
operator❌11:0:operator:/root:/sbin/nologin
games❌12💯games:/usr/games:/sbin/nologin
ftp❌14:50:FTP User:/var/ftp:/sbin/nologin
nobody❌99:99:Nobody:/:/sbin/nologin
systemd-bus-proxy❌999:997:systemd Bus Proxy:/:/sbin/nologin
systemd-network❌192:192:systemd Network Management:/:/sbin/nologin
dbus❌81:81:System message bus:/:/sbin/nologin
polkitd❌998:996:User for polkitd:/:/sbin/nologin
tss❌59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
......................................

awk 格式注意:-F 后面紧跟单引号,单引号里面为分隔符。print 的动作要用 {} 括起来,否则会报错。print 还可以打印自定义的内容,但是自定义的内容要用双引号括起来,如下:

[root@localhost awk]# awk -F ':' '{print $1"#"$2"#"$3"#"$4}' test.txt     //指定以#分割
root#x#0#0
bin#x#1#1
daemon#x#2#2
adm#x#3#4
lp#x#4#7
sync#x#5#0
shutdown#x#6#0
halt#x#7#0
mail#x#8#12
operator#x#11#0
.............

2. 匹配字符或者字符串 (匹配是需要加 ~)

[root@localhost awk]# awk '/oo/' test.txt    //打印含有oo的所有列
root❌0:0:root:/root:/bin/bash
lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
mail❌8:12:mail:/var/spool/mail:/sbin/nologin
operator❌11:0:operator:/root:/sbin/nologin
postfix❌89:89::/var/spool/postfix:/sbin/nologin

说明:跟 sed 的用法类似,能实现 grep 的功能,但是没有颜色显示。

[root@localhost awk]# awk -F ':' '$1 ~ /oo/' test.txt    //打印含有oo的所有列的第一列
root❌0:0:root:/root:/bin/bash

说明:这里的 ~ 是匹配的意思。wak 还可以多次匹配,如下


[root@localhost awk]# awk -F ':' '/root/ {print $1,$3} /user/ {print $1,$3}' test.txt   //打印包含root和user的所有列的第1和3段。

root 0
operator 11
tss 59
user1 1000
user2 1001
user4 1005
user5 1010
user11 1011

说明 :或者这种写法 awk -F ‘:’ ‘/root|user/ {print $1,$3}’ test.txt


算数运算符

运算符 描述
+ - 加,减
* / & 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ – 增加或减少,作为前缀或后缀

赋值运算符

运算符 描述
= += -= *= /= %= ^= **= 赋值语句

逻辑运算符

运算符 描述
|| 逻辑或
&& 逻辑与

关系运算符

运算符 描述
< <= > >= != == 关系运算符

其他运算符

运算符 描述
$ 字段引用
空格 字符串连接符
?: C 条件表达式
in 数组中是否存在某键值

示例:

awk 'BEGIN{a="b";print a=="b"?"ok":"err";}' 
ok 
awk 'BEGIN{a="b";arr[0]="b";arr[1]="c";print (a in arr);}' 
0 
awk 'BEGIN{a="b";arr[0]="b";arr["b"]="c";print (a in arr);}' 
1

运算级优先级表

awk 详解 (上)


3. 条件操作符

[root@localhost awk]# awk -F ':' '$3==0' test.txt    //打印第三段等于0的列
root❌0:0:root:/root:/bin/bash

[root@localhost awk]# awk -F ':' '$3==0 {print $1}' test.txt    //打印第三段等于0的列的第一段
root

[root@localhost awk]# awk -F ':' '$3>=1000 {print $0}' test.txt  //打印第三段大于等于1000的列的所有段($0代表所有段)
user1❌1000:1000::/home/user1:/bin/bash
user2❌1001:1002::/home/user2:/bin/bash
dd1❌1002:1004::/home/dd1:/bin/bash
user4❌1005:1003::/home/user4:/bin/bash
user5❌1010:1003::/home/dd1:/sbin/nologin
user11❌1011:1011::/home/user11:/bin/bash

说明: 当“1000”加引号时会被当做是字符串,以 ASC 码(二进制)的方式进行计算处理,不加引号的时候会被当做是数值处理。


[root@localhost awk]# awk -F ':' '$7!="sbin/nologin" {print $0}' test.txt   //打印第7段不等于sbin/nologin的所有段
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/sbin/nologin
daemon❌2:2:daemon:/sbin:/sbin/nologin
adm❌3:4:adm:/var/adm:/sbin/nologin
。。。。。。。。。。。。

说明: 当使用一个“=”等号时表示为等号前面字符赋值,使用两个“==”表示逻辑关系(进行判断)。


[root@localhost awk]# awk -F ':' '$3<$4' test.txt    //打印第三段小于第四段的列
adm❌3:4:adm:/var/adm:/sbin/nologin
lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
mail❌8:12:mail:/var/spool/mail:/sbin/nologin
games❌12💯games:/usr/games:/sbin/nologin
ftp❌14:50:FTP User:/var/ftp:/sbin/nologin
user2❌1001:1002::/home/user2:/bin/bash
dd1❌1002:1004::/home/dd1:/bin/bash

[root@localhost awk]# awk -F ':' '$3==$4' test.txt    //打印第三段等于第四段的列。
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/sbin/nologin
daemon❌2:2:daemon:/sbin:/sbin/nologin
nobody❌99:99:Nobody:/:/sbin/nologin
systemd-network❌192:192:systemd Network Management:/:/sbin/nologin
dbus❌81:81:System message bus:/:/sbin/nologin
tss❌59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
postfix❌89:89::/var/spool/postfix:/sbin/nologin
sshd❌74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
user1❌1000:1000::/home/user1:/bin/bash
user11❌1011:1011::/home/user11:/bin/bash
//注意: =是赋值;==是比较

[root@localhost awk]# awk -F ':' '$3>"5" && $3<"7"' test.txt    //打印第三段大于5并且小于7的列
shutdown❌6:0:shutdown:/sbin:/sbin/shutdown
tss❌59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin

[root@localhost awk]# awk -F ':' '$3>1000 || $7=="/sbin/nologin"' test.txt    //打印第三段大于1000或者第七段等于/sbin/nologin
bin❌1:1:bin:/bin:/sbin/nologin
daemon❌2:2:daemon:/sbin:/sbin/nologin
adm❌3:4:adm:/var/adm:/sbin/nologin
lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
mail❌8:12:mail:/var/spool/mail:/sbin/nologin
operator❌11:0:operator:/root:/sbin/nologin
games❌12💯games:/usr/games:/sbin/nologin
ftp❌14:50:FTP User:/var/ftp:/sbin/nologin
nobody❌99:99:Nobody:/:/sbin/nologin

[root@localhost awk]# awk -F ':' '$3>1000 || $7 ~"/bash/"' test.txt    //打印第三段大于1000或者第七段匹配 /bash 的列
user2❌1001:1002::/home/user2:/bin/bash
dd1❌1002:1004::/home/dd1:/bin/bash
user4❌1005:1003::/home/user4:/bin/bash
user5❌1010:1003::/home/dd1:/sbin/nologin
user11❌1011:1011::/home/user11:/bin/bash

awk 的常用内置变量

OFS 输出字段分隔符(默认值是一个空格)。
NF 表示字段数,在执行过程中对应于当前的字段数。
NR 表示记录数,在执行过程中对应于当前的行号。

[root@localhost awk]# awk -F ':' '{OFS="#"} $3>1000 || $7 ~ /bash/ {print $1,$3,$7}' test.txt
root#0#/bin/bash
user1#1000#/bin/bash
user2#1001#/bin/bash
dd1#1002#/bin/bash
user4#1005#/bin/bash
user5#1010#/sbin/nologin
user11#1011#/bin/bash

[root@localhost awk]# awk -F ':' '{OFS="#"} {if ($3>1000) {print $1,$3,$7}}' test.txt
user2#1001#/bin/bash
dd1#1002#/bin/bash
user4#1005#/bin/bash
user5#1010#/sbin/nologin
user11#1011#/bin/bash

[root@localhost awk]# awk -F ':' '{print NR":"$0}' test.txt    //NR显示行号
1:root❌0:0:root:/root:/bin/bash
2:bin❌1:1:bin:/bin:/sbin/nologin
3:daemon❌2:2:daemon:/sbin:/sbin/nologin
4:adm❌3:4:adm:/var/adm:/sbin/nologin
5:lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync❌5:0:sync:/sbin:/bin/sync
7:shutdown❌6:0:shutdown:/sbin:/sbin/shutdown
8:halt❌7:0:halt:/sbin:/sbin/halt
9:mail❌8:12:mail:/var/spool/mail:/sbin/nologin

[root@localhost awk]# awk -F ':' '{print NF":"$0}' test.txt    //NF表示一列中有多少段
4:root:root:/root:/bin/bash
7:bin❌1:1:bin:/bin:/sbin/nologin
7:daemon❌2:2:daemon:/sbin:/sbin/nologin
7:adm❌3:4:adm:/var/adm:/sbin/nologin
7:lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
7:sync❌5:0:sync:/sbin:/bin/sync
7:shutdown❌6:0:shutdown:/sbin:/sbin/shutdown

[root@localhost awk]# awk -F ':' 'NR<=10' test.txt    //打印前10行
root:root:/root:/bin/bash
bin❌1:1:bin:/bin:/sbin/nologin
daemon❌2:2:daemon:/sbin:/sbin/nologin
adm❌3:4:adm:/var/adm:/sbin/nologin
lp❌4:7:lp:/var/spool/lpd:/sbin/nologin
sync❌5:0:sync:/sbin:/bin/sync
shutdown❌6:0:shutdown:/sbin:/sbin/shutdown
halt❌7:0:halt:/sbin:/sbin/halt
mail❌8:12:mail:/var/spool/mail:/sbin/nologin
operator❌11:0:operator:/root:/sbin/nologin

[root@localhost awk]# awk -F ':' 'NF==4 && $1 ~ /root|sync/' test.txt    //打印一列为4段并且第一段匹配root或者sync的列
root:root:/root:/bin/bash

[root@localhost awk]# head -n 3 test.txt | awk -F ':' '$1="root"'    //给前三列的第一段赋值为root(这里的=是赋值的意思)
root root /root /bin/bash
root x 1 1 bin /bin /sbin/nologin
root x 2 2 daemon /sbin /sbin/nologin

可以发现,当赋值之后这里的:分割符变成了空格。下例变回:分割符

[root@localhost awk]# 
[root@localhost awk]# head -n 3 test.txt | awk -F ':' '{OFS=":"}$1="root"'
root:root:/root:/bin/bash
root❌1:1:bin:/bin:/sbin/nologin
root❌2:2:daemon:/sbin:/sbin/nologin

[root@localhost awk]# awk -F ':' '{(tot=tot+$3)}; END {print tot}' test.txt
9690
//说明: 这里的tot是求和的意思。默认是0。整句命令的意思是: 打印出test.txt文件中所有列的第三段相加的和。

[root@localhost awk]# awk -F ':' '{if($1=="root"){print $0}}' test.txt
root:root:/root:/bin/bash

//说明:在test.txt文件中,如果第1段等于root就打印出来。