首页 > 代码库 > shell 正则表达三剑客--awk

shell 正则表达三剑客--awk

awk命令

awk和sed一样是流式编辑器,它也是针对文档中的行来操作的,一行一行的去执行。awk比sed更加强大,它能做到sed能做到的。awk工具其实是很复杂的,有专门的书籍来介绍它的使用。


1 awk命令形式

awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file

 [-F|-f|-v]   大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value

‘  ‘         引用代码块

BEGIN       初始化代码块,主要是引用全局变量,设置FS分隔符

//           匹配代码块,可以是字符串或正则表达式

{}           命令代码块,包含一条或多条命令

;          多条命令使用分号分隔

END          结尾代码块,主要是进行最终计算或输出结尾摘要信息


2 特殊变量符

$0           表示整个当前行

$1           每行第一个字段

NF           字段数量变量

NR           每行的记录号,多文件记录递增

FNR          与NR类似,不过多文件记录不递增,每个文件都从1开始

\t           制表符

\n           换行符

FS           BEGIN时定义分隔符

RS           输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)

~            匹配,与==相比不是精确比较

!~           不匹配,不精确比较

==           等于,必须全部相等,精确比较

!=           不等于,精确比较

&&           逻辑与

||           逻辑或

+            匹配时表示1个或1个以上

/[0-9][0-9]+/    两个或两个以上数字

/[0-9][0-9]*/    一个或一个以上数字

FILENAME 文件名

OFS          输出字段分隔符, 默认也是空格,可以改为制表符等

ORS          输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕

-F‘[:#/]‘      定义三个分隔符


3 实例解析


截取文档中的某个段

[root@localhost ~]# head -n2 /etc/passwd |awk -F ‘:‘ ‘{print $1}‘
root
bin

解释一下,-F 选项的作用是指定分隔符,如果不加-F指定,则以空格或者tab为分隔符。 Print为打印的动作,用来打印出某个字段。$1为第一个字段,$2为第二个字段,依次类推,$0表示整行。

[root@localhost ~]# head -n2 test.txt |awk -F‘:‘ ‘{print $0}‘
rto:x:0:0:/rto:/bin/bash
operator:x:11:0:operator:/roto:/sbin/nologin

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

[root@localhost ~]# head -n2 test.txt |awk -F‘:‘ ‘{print $1"#"$2"#"$3"#"$4}‘
rto#x#0#0
operator#x#11#0


匹配字符或字符串

[root@localhost ~]# awk ‘/oo/‘ test.txt
operator:x:11:0:operator:/rooto:/sbin/nologin
roooto:x:0:0:/rooooto:/bin/bash

[root@localhost ~]# awk -F ‘:‘ ‘$1 ~/oo/‘ test.txt

roooto:x:0:0:/rooooto:/bin/bash

可以让某个段去匹配,这里的’~’就是匹配的意思

[root@localhost ~]# awk -F ‘:‘ ‘/root/ {print $1,$3} /test/ {print $1,$3}‘ /etc/passwd
root 0
operator 11
test 511
test1 512

awk还可以多次匹配,如上例中匹配完root,再匹配test,它还可以只打印所匹配的段。


条件操作符

[root@localhost ~]# awk -F ‘:‘ ‘$3=="0"‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash

awk中是可以用逻辑符号判断的,比如 ‘==’ 就是等于,也可以理解为 ‘精确匹配’ 另外也有 >, ‘>=, ‘<, ‘<=, ‘!= 等等,值得注意的是,在和数字比较时,若把比较的数字用双引号引起来后,那么awk不会认为是数字,而认为是字符,不加双引号则认为是数字。

[root@localhost ~]# awk -F ‘:‘ ‘$3>="500"‘ /etc/passwd
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
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
user11:x:510:502:user11,user11‘s office,12345678,123456789:/home/user11:/sbin/nologin
test:x:511:511::/home/test:/bin/bash
test1:x:512:511::/home/test1:/bin/bash

在上面的例子中,要把uid大于等于500的行打印出,但是结果并不是我们的预期,这是因为awk把所有的数字当作字符来对待了

[root@localhost ~]# awk -F ‘:‘ ‘$7!="/sbin/nologin"‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
test:x:511:511::/home/test:/bin/bash
test1:x:512:511::/home/test1:/bin/bash

!= 为不匹配,除了针对某一个段的字符进行逻辑比较外,还可以两个段之间进行逻辑比较。

[root@localhost ~]# awk -F ‘:‘ ‘$3<$4‘ /etc/passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

另外还可以使用 && 和 || 表示 “并且” 和 “或者” 的意思。

[root@localhost ~]# awk -F ‘:‘ ‘$3>"5" && $3<"7"‘ /etc/passwd
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
user11:x:510:502:user11,user11‘s office,12345678,123456789:/home/user11:/sbin/nologin
test:x:511:511::/home/test:/bin/bash
test1:x:512:511::/home/test1:/bin/bash

[root@localhost ~]# awk -F ‘:‘ ‘$3>"5" || $7=="/bin/bash"‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash
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
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
user11:x:510:502:user11,user11‘s office,12345678,123456789:/home/user11:/sbin/nologin
test:x:511:511::/home/test:/bin/bash
test1:x:512:511::/home/test1:/bin/bash


awk常用的变量

NF :用分隔符分隔后一共有多少段

NR :行数

[root@localhost ~]# head -n3 /etc/passwd | awk -F ‘:‘ ‘{print NF}‘
7
7
7
[root@localhost ~]# head -n3 /etc/passwd | awk -F ‘:‘ ‘{print $NF}‘
/bin/bash
/sbin/nologin
/sbin/nologin

NF 是多少段,而$NF是最后一段的值, 而NR则是行号

[root@localhost ~]# head -n3 /etc/passwd | awk -F ‘:‘ ‘{print NR}‘
1
2
3

我们可以使用行号作为判断条件:

[root@localhost ~]# awk ‘NR>20‘ /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
user11:x:510:502:user11,user11‘s office,12345678,123456789:/home/user11:/sbin/nologin
test:x:511:511::/home/test:/bin/bash
test1:x:512:511::/home/test1:/bin/bash

也可以配合段匹配一起使用:

[root@localhost ~]# awk -F ‘:‘ ‘NR>20 && $1 ~ /ssh/‘ /etc/passwd
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin


awk可以把段值更改

[root@localhost ~]# head -n 3 /etc/passwd |awk -F ‘:‘ ‘$1="root"‘
root x 0 0 root /root /bin/bash
root x 1 1 bin /bin /sbin/nologin
root x 2 2 daemon /sbin /sbin/nologin

awk还可以对各个段的值进行数学运算

[root@localhost ~]# head -n2 /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@localhost ~]# head -n2 /etc/passwd |awk -F ‘:‘ ‘{$7=$3+$4}‘
[root@localhost ~]# head -n2 /etc/passwd |awk -F ‘:‘ ‘{$7=$3+$4; print $0}‘
root x 0 0 root /root 0
bin x 1 1 bin /bin 2

当然还可以计算某个段的总和

[root@localhost ~]# awk -F ‘:‘ ‘{(tot=tot+$3)}; END {print tot}‘ /etc/passwd
2891

这里的END要注意一下,表示所有的行都已经执行

[root@localhost ~]# awk -F ‘:‘ ‘{if ($1=="root") print $0}‘ /etc/passwd
root:x:0:0:root:/root:/bin/bash


日常应用

应用1

awk -F: ‘{print NF}‘ helloworld.sh          输出文件每行有多少字段

awk -F: ‘{print $1,$2,$3,$4,$5}‘ helloworld.sh  输出前5个字段

awk -F: ‘{print $1,$2,$3,$4,$5}‘ OFS=‘\t‘ helloworld.sh 输出前5个字段并使用制表符分隔输出

awk -F: ‘{print NR,$1,$2,$3,$4,$5}‘ OFS=‘\t‘ helloworld.sh 制表符分隔前5个字段,并打印行号

应用2

awk -F‘[:#]‘ ‘{print NF}‘  helloworld.sh     指定多个分隔符: #,输出每行多少字段

awk -F‘[:#]‘ ‘{print $1,$2,$3,$4,$5,$6,$7}‘ OFS=‘\t‘ helloworld.sh  制表符分隔输出多字段

应用3

awk -F‘[:#/]‘ ‘{print NF}‘ helloworld.sh      指定三个分隔符,并输出每行字段数

awk -F‘[:#/]‘ ‘{print $1,$2,$3,$4,$5,$6}‘ helloworld.sh  制表符分隔输出多字段

应用4

计算/home目录下,普通文件的大小,使用KB作为单位,int是取整的意思

ls -l|awk ‘BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",sum/1024,"KB"}‘

ls -l|awk ‘BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}‘     应用5

统计netstat -anp 状态为LISTEN和CONNECT的连接数量分别是多少

netstat -anp|awk ‘$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}‘

应用6

统计/home目录下不同用户的普通文件的总数是多少

ls -l|awk ‘NR!=1 && !/^d/{sum[$3]++} END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}‘   

mysql        199 

root         568

统计/home目录下不同用户的普通文件的大小总size是多少

ls -l|awk ‘NR!=1 && !/^d/{sum[$3]+=$5} END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}‘




本文出自 “实用Linux知识技能分享” 博客,请务必保留此出处http://superleedo.blog.51cto.com/12164670/1888014

shell 正则表达三剑客--awk