文章目录
1 awk 介绍
- AWK 是一种处理文本文件的语言,是一个强大的文本分析工具
- 可以在无交互的模式下实现复杂的文本操作
- 相较于sed常作用于一整个行的处理,awk则比较倾向于一行当中分成数个字段来处理,因为awk相当适合小型的文本数据。
1.1 awk 工作原理
逐行读取文本,默认以空格或 tab 键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。
sed 命令常用于一整行的处理,而awk 比较倾向于将一行分成多个"字段" 然后再进行处理。awk 信息的读入也是逐行读取的,执行结果可以通过 print 的功能将字段数据打印显示。在使用 awk 命令的过程中,可以使用逻辑操作符 “&&” 表示"与"、“||” 表示"或"、“!” 表示 “非”;还可以进行简单的数学运算,如 +、-、*、/、%、^ 分别表示加、减、乘、除、取余和乘方。
1.2 awk 命令的基本格式
命令格式
awk 选项 '模式或条件 {操作}' 文件1 文件2...
awk -f 脚本文件 文件1 文件2...
awk 常见的内建变量(可直接用)
FS | 列分割符。指定每行文本的字段分隔符,默认为空格或制表位。:与 “-F” 作用相同 |
---|---|
NF | 当前处理的行的字段个数 |
NR | 当前处理的行的行号(序数) |
$0 | 当前处理的行的整行内容 |
$n(代表大于等于1的数字) | 当前处理行的第 n 个字段(第n列) |
FILENAME | 被处理的文件名 |
RS | 行分隔符。awk 从文件上读取资料时,将根据 RS 的定义把资料切割成许多条记录,而 awk 一次仅读入一条记录,以进行处理。预设值是 ‘\n’ |
1.2.1 以行输出文本
例1:打印全部内容
[root@localhost awk]# awk '{print $0}' test1
[root@localhost awk]# awk '{print}' test1
例2:打印前三行
[root@localhost awk]# awk 'NR==1,NR==3 {print $0}' test1
[root@localhost awk]# awk '(NR>=1)&&(NR<=3) {print}' test1 #输出连续的行
#输出第一行和第三行内容(输出不连续的行)
[root@localhost awk]# awk '(NR==1)||(NR==3) {print}' test1
[root@localhost awk]# awk '(NR>=1)||(NR<=3) {print}' test1
例3:取出ifconfig ens33里面的ip(第二行第二列)
[root@localhost awk]# ifconfig ens33
[root@localhost awk]# ifconfig ens33 |awk 'NR==2{print $2}'
例4:打印奇数行和偶数行
[root@localhost awk]# awk '(NR%2)==1{print}' test1
[root@localhost awk]# awk '(NR%2)==0{print}' test1
例5:字符串打印
[root@localhost awk]# awk '/root/ {print}' /etc/passwd #包含root的行内容
[root@localhost awk]# awk '/^root/ {print}' /etc/passwd #以root开头的行内容
[root@localhost awk]# awk '/bash$/ {print}' /etc/passwd #以bash结尾的行内容
1.2.2 扩充:awk ‘BEGIN{…};{…};END{…}’ 文件
- 在 awk 处理文本前会执行 BEGIN 模式里的命令
- 中间的 {…} 是真正用于处理文件的命令操作
- 在 awk 处理完文件后才会执行 END{…} 模式里的命令操作
[root@localhost awk]# awk 'BEGIN {x=0};/\/bin\/bash$/{x++};END {print x}' /etc/passwd #统计以/bin/bash 结尾的行数,等同于grep -c "/bin/bash$"
[root@localhost awk]# awk 'BEGIN {x=0};/\/bin\/bash$/{print;x++;print x};END {print x}' /etc/passwd
BEGIN模式表示,在处理指定的文本之前,需要先执行BEGIN模式中指定的动作;awk 再处理指定的文本,之后再执行END模式中指定的动作,END{}语句块中,往往会放入打印结果等语句
1.2.3 以字段(列)输出文本
例1
全部
[root@localhost awk]# awk -F ':' '{print $0}' /etc/passwd -F指定分隔符分隔符
打印第一列
[root@localhost awk]# awk -F ':' '{print $1}' /etc/passwd
打印第一列和第三列
[root@localhost awk]# awk -F ':' '{print $1,$3}' /etc/passwd
[root@localhost awk]# awk -F ':' 'BEGIN{FS=":"};{print $1,$3}' /etc/passwd
例2:在/etc/passwd中打印UID号大于500 的字段和小于等于500的字段
[root@localhost awk]# awk -F : '$3>500 {print $1,$3}' /etc/passwd #-F指定以冒号为分隔符,UID号大于500
[root@localhost awk]# awk -F : '!($3>500) {print $1,$3}' /etc/passwd #UID号小于等于500
使用相关语句操作时,需要再加一层{},整条语句看做一个操作命令
[root@localhost awk]# awk -F : '{if($3>500) {print $1,$3}}' /etc/passwd
1.2.4 三元运算符
(条件表达式)?(A表达式或者值):(B表达式或者值) 三元运算符 [条件表达式] && A ||
条件表达式成立为真时会取:号前面的A的值
条件表达式不成立为假时会取:号后面的B的值
[root@localhost awk]# awk -F : '{max=($3>=$4)?$3:$4;{print max,$1}}' /etc/passwd
#($3>$4)?$3:$4;三元运算符,如果第3个字段的值大于等于第4个字段的值,则把第3个字段的值赋给max,否则第4个字段的值赋给max
1.2.5 $NF、$n~、$n==、$n!=
$NF 代表最后一个字段
$n~"字符串" ~代表字段包含某个字符串的作用
$n=="字符串" ==代表字段为某个字符串的作用
$n!="字符串" !=代表字段不为某个字符串的作用
[root@localhost awk]# awk -F : '{print NR,$0}' /etc/passwd #输出每行内容和行号,每处理完一条记录,NR值加1
[root@localhost ~]# awk -F ":" '$7~"/bash"{print $1}' /etc/passwd #输出以冒号分隔且第7个字段中~(包含)/bash的行的第1个字段
[root@localhost ~]# awk -F ":" '($1~"root") && (NF==7){print $1,$2}' /etc/passwd #输出第1个字段中包含root且有7个字段的行的第1、2个字段
[root@localhost ~]# awk -F ":" '($7!="/bin/bash") && ($7!="/sbin/nologin"){print}' /etc/passwd #输出第7个字段既不为/bin/bash,也不为/sbin/nologin的所有行
[root@localhost ~]# awk -F ":" '{print $1,$NF}' /etc/passwd #输出以冒号分隔且第1个字段和最后1个字段
1.2.6 OFS 输出内容的列分隔符
[root@localhost awk]# echo "A B C D"
A B C D
[root@localhost awk]# echo "A B C D" | tr " " "|"
A|B|C|D
[root@localhost awk]# echo "A B C D" | sed 's/ /\|/g'
A|B|C|D
[root@localhost awk]# echo "A B C D" | awk 'BEGIN{OFS="|"};{print $0}'
A B C D
[root@localhost awk]# echo "A B C D" | awk 'BEGIN{OFS="|"};{$1=$1;print $0}'
A|B|C|D
[root@localhost awk]# echo "A B C D" | awk 'BEGIN{OFS="|"};{$2=$2;print $0}'
A|B|C|D
1.3 高级用法1—通过管道、双引号调用shell命令
例1:统计行数
[root@localhost awk]# echo $PATH
[root@localhost awk]# echo $PATH |awk 'BEGIN{RS=":"};{print NR,$0}'
[root@localhost awk]# echo $PATH |awk 'BEGIN{RS=":"};{print NR,$0};END{print NR}'
#统计以冒号分隔的文本段落数,END{}语句块中,往往会放入打印结果等语句
[root@localhost awk]# awk -F: '/bash$/{print | "wc -l"}' /etc/passwd
#调用wc -l命令统计使用bash的用户个数,等同于grep -c "bash$"/etc/passwd
[root@localhost awk]# grep -c "bash$" /etc/passwd
例2:查看当前内存使用百分比
[root@localhost awk]# free
[root@localhost awk]# free | awk '/Mem:/ {print $3/$2}'
[root@localhost awk]# free | awk '/Mem:/ {print $3/$2*100}'
[root@localhost awk]# free | awk '/Mem:/ {print int($3/$2*100)}'
[root@localhost awk]# free | awk '/Mem:/ {print int($3/$2*100)"%"}' #查看当前内存使用百分比
例3:CPU的空闲率,输出top里id的值
[root@localhost awk]# top -b -n 1 #一次输出
[root@localhost awk]# top -b -n 1 | awk -F, '/Cpu/{print $4}' | awk '{print $1}'
[root@localhost awk]# top -b -n 1 | awk -F, '/Cpu/{print $4}' | awk '{print int ($1)}'
[root@localhost awk]# top -b -n 1 | awk -F, '/Cpu/{print $4}' | awk '{print int ($1) "%"}'
1.4 高级用法2—awk数组特性
例1
[root@localhost awk]# awk 'BEGIN{a[0]=10;a[1]=20;print a[0]}'
[root@localhost awk]# awk 'BEGIN{a[0]=10;a[1]=20;print a[1]}'
[root@localhost awk]# awk 'BEGIN{a["abc"]=10;a["xyz"]=20;print a["abc"]}' #字符串要带" "
[root@localhost awk]# awk 'BEGIN{a["abc"]=10;a["xyz"]=20;print a["xyz"]}'
[root@localhost awk]# awk 'BEGIN{a[0]=10;a[1]=20;a[2]=30;for(i in a)print i,a[i]}' #i代表数组的下标值
例2
[root@localhost awk]# awk '{a[1]++;print a[1]}' test1
[root@localhost awk]# awk '{a[$1]++};END{for(i in a){print i,a[i]}}' test1
$1第一个字段的值
[]里代表列的值
把列的值作为数组的下标去看待
然后去匹配行的内容,匹配一次加1
通过for循环,打印出下标的值和出现的次数
1.5 日期—date
例1
[root@localhost awk]# date
[root@localhost awk]# date +"%F"
[root@localhost awk]# date +"%Y%m%d" #当前时间
[root@localhost awk]# date +"%Y%m01" #本月第一天
[root@localhost awk]# date -d "$(date -d "1 month" +"%Y%m01") -1 day" +"%Y%m%d" #当前月最后一天
[root@localhost awk]# date -d "$(date -d "1 month" +"%Y%m01")" +"%Y%m%d" #下个月第一天
[root@localhost awk]# date -d "$(date +"%Y%m01") -1 day" +"%Y%m%d" #上个月最后一天
[root@localhost awk]# date -d "$(date +"%Y%m01") -3 day" +"%Y%m%d" #上个月倒数第三天
例2
[root@localhost awk]# date -d "2 day ago" +"%Y%m%d" #2天前的时间
[root@localhost awk]# date -d "10 day ago" +"%Y%m%d" #10天前的时间
[root@localhost awk]# date -d "10 second
[root@localhost awk]# date -d "10 second ago" +"%Y%m%d %H:%M:%S" #10秒前
例3
[root@localhost awk]# cat /proc/uptime
[root@localhost awk]# awk -F. '{print $1}' /proc/uptime
[root@localhost awk]# date -d "$(awk -F. '{print $1}' /proc/uptime) second ago" +"%Y%m%d %H:%M:%S" #服务器重启的时间
1.6 案例
通过脚本分析 /var/log/secure 查看哪些主机在暴力破解本服务,如果统计出密码验证失败超过三次就把IP加入到黑名单中 /etc/hosts.deny
awk sort uniq -c
awk '/Failed passwdord/{print $11}' /var/log/secure |sort -rn | uniq -c | awk '$1>3 {print "sshd:"$2}' >>/etc/hosts.deny
先筛选11列包含Failed passwdord的
然后排序、去重
最后把筛选出超过三次的加入到黑名单/etc/hosts.deny中
或者
awk '/Failed passwdord/{a[$11]++};END{for(i in a){print i,a[i]}}' /var/log/secure
方法一:
方法二:
总结
1、awk ‘BEGIN{…};{…};END{…}’ 文件
- 在 awk 处理文本前会执行 BEGIN 模式里的命令
- 中间的 {…} 是真正用于处理文件的命令操作
- 在 awk 处理完文件后才会执行 END{…} 模式里的命令操作
2、三元运算符
(条件表达式)?(A表达式或者值):(B表达式或者值) 三元运算符 [条件表达式] && A ||
条件表达式成立为真时会取:号前面的A的值
条件表达式不成立为假时会取:号后面的B的值
3
BEGIN{FS="指定分隔符"}; #FS:列分隔符 OFS:输出内容的列分隔符
awk -F '指定分隔符' '条件{print $1,$NF}'
NR==n,NR==m #n~m
(NR>=n) && (NR<=m)
/字符串+正则/
4
sn> <==对比数值
$NF 代表最后一个字段
$n~"字符串" ~代表字段包含某个字符串的作用
$n=="字符串" ==代表字段为某个字符串的作用
$n!="字符串" !=代表字段不为某个字符串的作用
5、日期date
date +"%Y%m%d" #当前时间
date +"%Y%m01" #当前月的第一天
date -d "$(date -d "1 month" +"%Y%m01") -1 day" +"%Y%m%d" #当前月最后一天
date -d "$(date -d "1 month" +"%Y%m01")" +"%Y%m%d" #下个月第一天
date -d "$(date +"%Y%m01") -1 day" +"%Y%m%d" #上个月最后一天
date -d "$(date +"%Y%m01") -3 day" +"%Y%m%d" #上个月倒数第三天
date -d "2 day ago" +"%Y%m%d" #2天前的时间
date -d "$(awk -F. '{print $1}' /proc/uptime)second ago" +"%Y%m%d %H:%M:%S" #服务器重启的时间