1、awk
awk是一种编程语言,用于在Linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其他命令的输出。awk支持函数、正则表达式、数组等先进功能。
awk处理文本和数据的方式是先逐行扫描文件,从第一到最后一行,寻找匹配的特定模式的行,并在这些找出的行上进行分段操作。如果没有指定处理动作,则把匹配到的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理,它可以在命令行中使用,更多是作为脚本中一部分来使用。
2、awk中的变量
(1)awk的内置变量
FS(input field seperator):输入时字段分隔符,默认为空白字符;作用同-F参数
OFS(output field seperator):输出时字段分隔符,默认为空白字符;
RS(input record seperator):输入时的换行符;
ORS(output record seperator):输出时的换行符;
NF(number of field):字段数量
NR(number of record): 行数;多个文件和当一个文件对待
FNR:各文件分别计数,行数;
FILENAME:当前文件名;
ARGC:命令行参数的个数;
ARGV:数组,保存的是命令行所给定的各参数;
# FS的使用,作用同-F [root@node02 ~]# awk -v FS=':' '{print $1}' /etc/passwd|head -2 root bin [root@node02 ~]# awk -F: '{print $1}' /etc/passwd|head -2 root bin # 指定分隔符为\t,换行符为# [root@node02 ~]# head -2 /etc/passwd|awk -F: -v OFS="\t" -v ORS='#' '{print $1,$3}' root 0#bin 1 # NF为输出行字段的数量,$NF为输出最后一个字段 [root@node02 ~]# awk -F: '{print NF}' /etc/passwd|head -2 7 7 [root@node02 ~]# awk -F: '{print $NF}' /etc/passwd|head -2 /bin/bash /sbin/nologin # 输出的每一行都会用该文件名来代替 [root@node02 ~]# awk '{print FILENAME}' /etc/fstab /etc/passwd /etc/fstab . . . . . . /etc/passwd # 每一行都会显示参数的个数 [root@node02 ~]# awk '{print ARGC}' /etc/fstab /etc/passwd 3 3 . . . . . . # 只显示一次参数的个数 [root@node02 ~]# awk 'BEGIN{print ARGC}' /etc/fstab /etc/passwd 3 # 显示指定的参数,第0个参数为awk自身 [root@node02 ~]# awk 'BEGIN{print ARGV[0]}' /etc/fstab /etc/passwd awk
(2)awk的内置变量
Awk中自定义变量时通过-v选项自定义变量,同时也可在在program中直接定定义。
[root@node02 ~]# awk -v test="hello awk" 'BEGIN{print test}' /etc/fstab hello awk [root@node02 ~]# awk 'BEGIN{test="awk";print test}' awk
3、awk中printf输出命令的使用
Awk中printf也为输出,相对于print输出,print不会自动换行,需要显式给出换行控制符”\n”, FORMAT必须给出, FORMAT中需要分别为后面的每个item指定一个格式化符号。
(1)使用printf输出时的格式符
%c: 显示字符的ASCII码;
%d, %i: 显示十进制整数;
%e, %E: 科学计数法数值显示;
%f:显示为浮点数;
%g, %G:以科学计数法或浮点形式显示数值;
%s:显示字符串;
%u:无符号整数;
%%: 显示%自身;
(2)使用printf输出时的装饰符
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度;
-: 左对齐
+:显示数值的符号
# 默认输出是不换行的 [root@node02 ~]# awk -F: '{printf "%s",$3}' /etc/passwd 01234567811121499192819997489998322965534997996995994993 # 使用\n换行输出 [root@node02 ~]# awk -F: '{printf "username: %s \t uid:%d\n",$1,$3}' /etc/passwd|head -2 username: root uid:0 username: bin uid:1 # 输出时左对齐并且用户名字段的长度为15个字符 [root@node02 ~]# awk -F: '{printf "username: %-15s \t uid:%d\n",$1,$3}' /etc/passwd| head -3 username: root uid:0 username: bin uid:1
4、awk中算术运算符
运算符 |
描述 |
= += -+ *= /= %= ^= **= |
赋值语句 |
|| && |
逻辑与、逻辑或 |
~ !~ |
匹配则正表达式和不匹配正则表达式 |
< <= > >= != == |
关系运算符 |
+ - * / & ** ++ -- |
算术运算符 |
$ |
字段引用 |
空格 |
字符串连接符 |
?: |
三目运算符 |
ln |
数组中是否存在某键值 |
# 打印非系统用户 [root@node02 ~]# awk -F: '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd root 0 nfsnobody 65534 # 三目运算判断用户的类型 [root@node02 ~]# awk -F: '{$3>=1000||$3==0?usertype="common user":usertype="system user";print$1,"-->"usertype}' /etc/passwd root -->common user bin -->system user
5、awk中的模式匹配
(1) empty:空模式,匹配每一行;
(2)/regular expression/:仅处理能够被此处的模式匹配到的行;
(3)关系表达式;结果有“真”有“假”;结果为“真”才会被处理;真是结果为非0值,非空字符串;
(4)行范围的匹配,不支持直接给出数字的格式
(5)BEGIN/END模式, BEGIN{}仅在开始处理文件中的文本之前执行一次;END{}仅在文本处理完成之后执行一次;
# 输出fstab文件中以UUID或/dev开头的行 [root@node02 ~]# awk '/^UUID|^\/dev/{print $1}' /etc/fstab /dev/mapper/centos-root UUID=8dcf68e6-ba4c-4aaa-abd9-d008f1ecbe51 # 输出uid大于1000的用户 [root@node02 ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd nfsnobody 65534 # 输出bash为/bin/bash的用户 [root@node02 ~]# awk -F: '$NF=="/bin/bash"{print $1,$3,$NF}' /etc/passwd root 0 /bin/bash # 行范围的匹配 [root@node02 ~]# awk -F: '(NR>=2&&NR<=4){print $1}' /etc/passwd bin daemon adm # BEGIN/END模式输出 [root@node02 ~]# awk -F: 'BEGIN{print "username\t\tuid\n------------------"}{printf "%s\t\t\t%d\n",$1,$3}END{print "=================================\nend"}' /etc/passwd|head -5 username uid ------------------ root 0 bin 1 daemon 2
6、awk中使用控制语句
awk中的条件语句是从C语言中借鉴过来的,可控制程序的流程.
(1)if-else语句
语句格式:if(condition) statement [else statement]
# 输出bash为“/bin/bash”的用户 [root@node02 ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd root # 判断用户类型 [root@node02 ~]# awk -F: '{if($3>=1000) {printf "Commonuser: %s\n",$1} else if($3==0){printf "admin-user: %s\n",$1} else {printf "Sysuser: %s\n",$1}}' /etc/passwd admin-user: root Sysuser: bin # 判断一行的字数大于5个时输出整行 [root@node02 ~]# awk '{if(NF>5) print $0}' /etc/fstab # Created by anaconda on Fri Jan 4 01:49:11 2019 # Accessible filesystems, by reference, are maintained under # 列出磁盘使用率大于等于10%的分区 [root@node02 ~]# df -h | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>=10) print $1}' /dev/sda1
(2)while语句
While语句用于当条件成立时进入循环,当条件不成立时推出循环,常用于对一行内的多个字段逐一类似处理时或数组中的各元素逐一处理。
# 统计liunx所在行的每个字段的长度 [root@node02 ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-862.el7.x86_64 30 root=/dev/mapper/centos-root 28 # 统计liunx所在行的每个字段的长度并输出长度大于7的字段及长度 [root@node02 ~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-862.el7.x86_64 30 root=/dev/mapper/centos-root 28
(3)for语句
# 上面的案例使用for语句实现为 [root@node02 ~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) if(length($i)>=7){print $i,length($i)}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-862.el7.x86_64 30 root=/dev/mapper/centos-root 28
(4)其他控制语句
除了上述常用的控制语句外,其他的控制语句还有do-while循环,switch语句,break和continue,next(前结束对本行的处理而直接进入下一行)。
7、awk中的数组
awk中的数组的下标可以是数字和字母,称为关联数组;下标可使用任意字符串;字符串要使用双引号;如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”。
# 定义一个数组并遍历输出数组中的每个元素 [root@node02 ~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}' Tuesday Monday # 统计每种TCP状态的数量 [root@node02 ~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}' LISTEN 19 ESTABLISHED 24 TIME_WAIT 3 # 统计fstab中每种类型的文件系统出现的次数 [root@node02 ~]# awk '/^UUID|^\/dev/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab swap 1 xfs 2 # 统计fstab中每个字段出现的次数 [root@node02 ~]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab swap 2 fstab(5), 1
8、awk中的函数
awk中常用的内置函数有:
rand():返回0和1之间一个随机数;
length([s]):返回指定字符串的长度;
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容;
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容;
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中;
# 统计机同外部每个ip建立tcp连接的数量 [root@node02 ~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}' 192.168.17.195 1 192.168.17.196 3 # 将匹配到的第一个root替换为admin [root@node02 ~]# awk -F: '{ sub(/root/, "admin"); print }' /etc/passwd admin:x:0:0:root:/root:/bin/bash