AWK
awk 其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
awk 是一种很棒的语言,它适合文本处理和报表生成,其语法较为常见,借鉴了某些语言的一些精华,如 C 语言等。在 linux 系统日常处理工作中,发挥很重要的作用,掌握了 awk 将会使你的工作变的高大上。 awk 是三剑客的老大,利剑出鞘,必会不同凡响。
语法
awk '{pattern + action}' {filenames}
其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来
通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。
BEGIN 和 END 模块
# 行处理前
BEGIN{}
# 行处理
{...}
# 行处理后
END{}
通常,对于每个输入行, awk 都会执行每个脚本代码块一次。然而,在许多编程情况中,可能需要在 awk 开始处理输入文件中的文本之前执行初始化代码。对于这种情况, awk 允许您定义一个 BEGIN 块。
因为 awk 在开始处理输入文件之前会执行 BEGIN 块,因此它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中以后会引用的全局变量的极佳位置。
awk 还提供了另一个特殊块,叫作 END 块。 awk 在处理了输入文件中的所有行之后执行这个块。通常, END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。
# 实例
[root@vm72 ~]# awk 'BEGIN {count=0;print "[start] user count is ",count}{count=count+1;print $0} END{print "[end] user count is ",count}' /etc/passwd
[start] user count is 0
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
…………..
[end] user count is 25
内置变量
属性 | 说明 |
$0 | 当前记录(作为单个变量) |
$1~$n | 当前记录的第n个字段,字段间由FS分隔 |
FS | Field Separator 输入字段分隔符,默认是空格,等价于命令行 -F选项 |
OFS | Out of Field Separator 输出字段分隔符,默认也是空格 |
NF | 当前记录中的字段个数,就是有多少列 |
NR | 已经读出的记录数,就是行号,从1开始 |
RS | Record Separator 输入记录分隔符,默认为换行符,一行变多行 |
ORS | Output Record Separate 输出记录分隔符,默认为换行符,多行变一行 |
FNR | 多个文件分别计数的行号 |
1、IF 用法
# 输出指定文件前 5 行
awk '{if(NR<=5){print $0}}' /etc/passwd
2、RS ORS 用法
# 输出行记录分隔符--,多行变成一行
[root@vm73 ~]# cat rs.txt
111 222 333
aaa bbb ccc
444 555 666
[root@vm73 ~]# awk 'BEGIN{ORS="--"}{print $0}' rs.txt
111 222 333--aaa bbb ccc--444 555 666--
# 输入行记录分隔符--,一行变多行
[root@vm73 ~]# cat ors.txt
111 222 333--aaa bbb ccc--444 555 666--
[root@vm73 ~]# awk 'BEGIN{RS="--"}{print $0}' ors.txt
111 222 333
aaa bbb ccc
444 555 666
3、数组
# 定义数组
[root@vm73 ~]# awk 'BEGIN{person[0]="tom";person[1]="male";print person[0]}'
tom
# 定义关联数组
[root@vm73 ~]# awk 'BEGIN{person["name"]="tom";person["gender"]="male";print person["name"]}'
tom
# 判断一个数组项是否存在 用(下标 in 数组名)
[root@vm73 ~]# awk 'BEGIN{person[0]="tom";person[1]="male";if(1 in person){print person[1]}}'
male
4、运算
[root@vm73 ~]# awk 'BEGIN{a=1;a=a+1;print a}'
2
# 当变量值为字符串,参加运算时,会当作0
[root@vm73 ~]# awk 'BEGIN{a="aaa";a=a+1;print a}'
1
# 当变量值为空值,参加运算时,会当作0
[root@vm73 ~]# awk 'BEGIN{a="";a=a+1;print a}'
1
# 数组元素的值为空值,参加运算时,会当作0
[root@vm73 ~]# awk 'BEGIN{print person[0]+1}'
1
5、统计字符串出现的次数
[root@vm73 ~]# cat for.txt
192.168.1.72 TIME_WAIT
192.168.1.113 ESTABLISHED
192.168.1.73 TIME_WAIT
192.168.1.114 TIME_WAIT
192.168.1.72 TIME_WAIT
192.168.1.72 TIME_WAIT
192.168.1.72 ESTABLISHED
192.168.1.114 ESTABLISHED
192.168.1.113 ESTABLISHED
192.168.1.73 ESTABLISHED
192.168.1.73 ESTABLISHED
192.168.1.114 ESTABLISHED
# 统计不同IP出现的总次数
[root@vm73 ~]# awk '{count[$1]++} END {for( i in count) {print i,count[i]}}' for.txt
192.168.1.72 4
192.168.1.73 3
192.168.1.113 2
192.168.1.114 3
# 统计不同状态的总次数
[root@vm73 ~]# awk '{count[$2]++} END {for( i in count) {print i,count[i]}}' for.txt
ESTABLISHED 7
TIME_WAIT 5
awk 内置函数
1、split
split 允许你把一个字符串分隔为单词并存储在数组中。你可以自己定义域分隔符或者使用现在FS(域分隔符)的值。
格式:split (string, array, field separator)
vi split.sh
#! /usr/bin/bash
# 重新输出时间格式
time="16:05:38"
string=`echo $time | awk '{split($0,arr,":");print arr[1],arr[2],arr[3]}'`
echo $string
# 执行结果
[root@vm72 ~]# bash split.sh
16 05 38
[root@vm72 ~]# cat salary.txt
helen 2020-01-15 4500
jack 2019-12-12 5000
alice 2019-12-20 2000
tom 2019-12-22 3200
helen 2019-12-05 4000
tom 2019-12-09 6000
# 合计2019年12月份的人员工资
[root@vm72 ~]# awk '{split($2,arr,"-");if(arr[2]==12){b[$1]+=$3}}END{for(i in b)print i,b[i]}' salary.txt
# 执行结果
alice 2000
jack 5000
helen 4000
tom 9200
2、substr
[root@vm72 ~]# echo "baidu.com" | awk '{print substr($0,1,5)}'
# 执行结果
baidu
3、length
[root@vm72 ~]# echo "baidu.com" | awk '{print length}'
# 执行结果
9