shell编程7-shell三剑客(下)-文本处理工具awk
shell编程7-shell三剑客之文本处理工具awk
一.awk介绍
- awk是一种编程语言,主要用于在linux/unix下对文本和数据进行处理,是linux/unix下的一个工具。数据可以来自标准输入、一个或多个文件,或其它命令的输出。
- awk的处理文本和数据的方式:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
- awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
- awk是GNU的项目之一,是基于早期unix上的awk程序语言改善而来,所以现在我们在CentOS上用的awk其实是叫gawk。因为人们习惯用awk,所以后来干脆把awk创建了一个指向gawk的符号链接。
1.awk使用方式
- 命令模式语法
awk 选项 'commands' 文件名
- 常用选项
- -F 定义字段分割符号,默认的分隔符是空格
- -v 定义变量并赋值
[root@shell-note ~]# awk -F. '{print $1,$(NF-1)}' 1.txt
192 10
10 20
you are the hero you are the hero
ABC ABC
root abv root abv
-
'命名部分(commands)’
- 正则表达式,地址定位
'/root/{awk语句}' sed中: '/root/p' 'NR==1,NR==5{awk语句}' sed中: '1,5p' '/^root/,/^ftp/{awk语句}' sed中:'/^root/,/^ftp/p'
- {awk语句1;awk语句2;…}
'{print $0;print $1}' sed中:'p' 'NR==5{print $0}' sed中:'5p' 注:awk命令语句间用分号间隔
- BEGIN…END…
'BEGIN{awk语句};{处理中};END{awk语句}' 'BEGIN{awk语句};{处理中}' '{处理中};END{awk语句}'
- 引用shell变量需用双引号引起
-
脚本模式
- 脚本执行
方法1: awk 选项 -f awk的脚本文件 要处理的文本文件 awk -f awk.sh filename sed -f sed.sh -i filename 方法2: ./awk的脚本文件(或者绝对路径) 要处理的文本文件 ./awk.sh filename ./sed.sh filename
- 脚本编写
#!/bin/awk -f 定义魔法字符 以下是awk引号里的命令清单,不要用引号保护命令,多个命令用分号间隔 BEGIN{FS=":"} NR==1,NR==3{print $1"\t"$NF} ...
- 脚本执行
2.awk内部相关变量
变量 | 变量说明 | 备注 |
---|---|---|
$0 | 当前处理行的所有记录 | |
$1,$2,$3…$n | 文件中每行以间隔符号分割的不同字段 | awk -F: ‘{print $1,$3}’ |
NF | 当前记录的字段数(列数) | awk -F: ‘{print NF}’ |
$NF | 最后一列 | $(NF-1)表示倒数第二列 |
FNR/NR | 行号 | |
FS | 定义间隔符 | ‘BEGIN{FS=“:”};{print $1,$3}’ |
OFS | 定义输出字段分隔符,默认空格 | ‘BEGIN{OFS=“\t”};print $1,$3}’ |
RS | 输入记录分割符,默认换行 | ‘BEGIN{RS=“\t”};{print $0}’ |
ORS | 输出记录分割符,默认换行 | ‘BEGIN{ORS=“\n\n”};{print $1,$3}’ |
FILENAME | 当前输入的文件名 |
- 例
[root@shell-note ~]# awk -F. '{print $1,$(NF-1),$NF,NF}' 1.txt
192 10 254 4
10 20 1 4
you are the hero you are the hero you are the hero 1
ABC ABC ABC 1
[root@shell-note ~]# awk '/root/{print $0}' 1.txt
root abv
[root@shell-note ~]# awk '/10/{print $0}' 1.txt
192.168.10.254
10.10.20.1
[root@shell-note ~]# awk '/10/' 1.txt
192.168.10.254
10.10.20.1
[root@shell-note ~]# awk -F. '/10/{print $0,NF}' 1.txt
192.168.10.254 4
10.10.20.1 4
[root@shell-note ~]# awk 'NR==1,NR==4' 1.txt
192.168.10.254
10.10.20.1
you are the hero
ABC
[root@shell-note ~]# awk 'NR==1,NR==4;/^A/{print $0}' 1.txt
192.168.10.254
10.10.20.1
you are the hero
ABC
ABC
- FS和OFS
[root@shell-note ~]# awk 'BEGIN{FS="."};/^1/,/^root/{print $1,$NF}' 1.txt
192 254
10 1
you are the hero you are the hero
ABC ABC
root abv root abv
[root@shell-note ~]# awk -F. 'BEGIN{OFS="\t\t"};/^1/{print $1,$NF}' 1.txt
192 254
10 1
[root@shell-note ~]# awk -F. 'BEGIN{OFS="@"};/10/{print $1,$NF}' 1.txt
192@254
10@1
- RS和ORS
[root@shell-note ~]# awk 'BEGIN{RS="."};{print $0}' 1.txt
192
168
10
254
10
10
20
1
you are the hero
ABC
root abv
[root@shell-note ~]# awk 'BEGIN{ORS="."};{print $0}' 1.txt
192.168.10.254.10.10.20.1.you are the hero.ABC.root abv.
- 格式化输出print和printf
print函数 类似echo
[root@shell-note ~]# date |awk '{print "time: "$4 "\nday: "$3 "\nMonth: "$2 "\nYear: "$NF}'
time: 12:02:07
day: 26
Month: Sep
Year: 2022
[root@shell-note ~]# awk -F: '{print "username: "$1 "\t uid: "$3}' /etc/passwd
...
printf函数 类似echo -n
[root@shell-note ~]# awk -F: '{printf "username: %-20s uid: %-15s\n", $1,$3}' /etc/passwd
[root@shell-note ~]# awk -F: '{printf "|%20s| %10s| %15s|\n", $1,$2,$3}' /etc/passwd
[root@shell-note ~]# awk -F: '{printf "|%-20s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
%s 字符类型 strings
%d 数值类型
%-15s 占15字符
- 表示左对齐,默认是右对齐
printf默认不会在行尾自动换行,加\n
3.awk工作原理
awk -F: '{print $1,$3}' /etc/passwd
-
awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符(RS)结束
-
每行被间隔符 : (默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始
问:awk如何知道用空格来分隔字段的呢?
答:因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空格
-
awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格
-
awk处理完一行后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕
4.awk变量定义
[root@shell-note ~]# date| awk -v Num=6 '{ print $Num }'
2022
[root@shell-note ~]# date| awk -v Num=6 '{ print Num }'
6
-v 传入一个用户定义的环境变量
awk中调用定义的变量不需要加$
5.awk中BEGIN…END使用
①BEGIN:表示在程序开始前执行
②END :表示所有文件处理完后执行
③用法:'BEGIN{开始处理之前};{处理中};END{处理结束后}'
- 例
- 打印/etc/passwd里的用户名,家目录及登录shell
[root@shell-note ~]# awk -F: 'BEGIN{ print "U_name\t\tH_dir\t\tLogin_shell\n*****************************************"};{print $1"\t\t"$(NF-1)"\t\t"$NF};END{print "************************"}' /etc/passwd
U_name H_dir Login_shell
*****************************************
root /root /bin/bash
bin /bin /sbin/nologin
daemon /sbin /sbin/nologin
adm /var/adm /sbin/nologin
lp /var/spool/lpd /sbin/nologin
sync /sbin /bin/sync
shutdown /sbin /sbin/shutdown
halt /sbin /sbin/halt
...
二.awk和正则综合运用
运算符 | 说明 |
---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
~ | 匹配 |
!~ | 不匹配 |
! | 逻辑非 |
&& | 逻辑与 |
|| | 逻辑或 |
- 例
[root@shell-note ~]# awk 'NR==1,NR==5{print $0}' /etc/passwd
# 打印文件第一行到第五行
[root@shell-note ~]# awk '/^a/,NR==5{print $0}' /etc/passwd
# 打印文件以a开头的行匹配到第五行
[root@shell-note ~]# awk 'NR==1,/^a/{print $0}' /etc/passwd
# 与上道命令反之,打印文件从第一行匹配到以a开头的行结束
[root@shell-note ~]# awk 'NR==1,/^a/||/^r/{print $0}' /etc/passwd
# 打印文件第一行匹配到以a开头或以r开头的行结束
[root@shell-note ~]# awk -F':' 'NR>=5 && NR<=10 {print $0}' /etc/passwd
# 显示5-10行
[root@shell-note ~]# awk 'NR>=10 && NR<=35 && $0 ~ /bash$/{print $0}' /etc/passwd
centos:x:1000:1000:Cloud User:/home/centos:/bin/bash
user1:x:1001:1001::/home/user1:/bin/bash
# 打印10-35行以bash结尾的内容
[root@server shell07]# awk 'NR>=3 && NR<=8 || /bash$/' 1.txt
[root@server shell07]# awk 'NR>=3 && NR<=8;/bash$/' 1.txt
# 理解;号和||的含义
三.awk脚本编程
1.流程控制语句
if语句:
if [ condition ];then
command
fi
格式:
awk 选项 '正则,地址定位{awk语句}' 文件名
{ if(表达式){语句1;语句2;...}}
awk -F: '{if($3>=500 && $3<=60000) {print $1,$3} }' /etc/passwd
[root@shell-note ~]# awk -F: '{if($3==0) {print $1"是超级管理员"}}' /etc/passwd
root是超级管理员
if...else语句:
if [ condition ];then
command
else
command
fi
格式:
{if(表达式){语句;语句;...}else{语句;语句;...}}
awk -F: '{ if($3>=500 && $3 != 65534) {print $1"是普通用户"} else {print $1,"不是普通用户"}}' /etc/passwd
if [ condition ];then
command
elif [ condition ];then
command
....
else
...
fi
if...else if...else语句:
格式:
{ if(表达式1){语句;语句;...}else if(表达式2){语句;语句;...}else if(表达式3){语句;语句;...}else{语句;语句;...}}
[root@shell-note ~]# awk -F: '{ if($3==0) {print $1,":是超级管理员"} else if($3>=1 && $3<=499 || $3==65534 ) {print $1,":是系统用户"} else {print $1,":是普通用户"}' /etc/passwd
[root@shell-note ~]# awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd
管理员个数: 1
普通用个数: 3
系统用户: 29
[root@shell-note ~]# awk -F: '{ if($3==0) {print $1,":是超级管理员";i++} else if($3>=1 && $3<=499 || $3==65534 ) {print $1,":是系统用户";j++} else {print $1,":是 普用户";k++}};END{print "管理员个数为" i"\n系统用户个数为:" j"\n普通用户个数为:" k}' /etc/passwd
2.循环语句
while语句:
[root@shell-note ~]# awk 'BEGIN { i=1; while(i<=10) { print i;i++ } }'
# 打印1到10
[root@shell-note ~]# awk -F: '{ i=1; while(i<=10) { print $0;i++ } }' /etc/passwd
# 文件每一行循环打印10次
for语句:
[root@shell-note ~]# awk 'BEGIN { for(i=1;i<=5;i++) { print i } }'
# 打印1到5
[root@shell-note ~]# awk -F: '{ for(i=1;i<=5;i++) { print $0 } }' /etc/passwd
# 文件每一行循环打印5次
awk 选项 '{awk语句1 语句2 }'
[root@shell-note ~]# awk 'BEGIN { i=1;while(i<=5) {(sum+=i) i++};print sum }'
# 打印1-5的和
[root@shell-note ~]# for ((i=1;i<=10;i+=2));do echo $i;done|awk -v sum=0 '{sum+=$0};END{print sum}'
# 打印1-10的奇数和
嵌套循环:
#!/bin/bash
for ((y=1;y<=5;y++))
do
for ((x=1;x<=$y;x++))
do
echo -n $x
done
echo
done
[root@shell-note ~]# awk 'BEGIN { for(y=1;y<=5;y++) { for(x=1;x<=y;x++) {printf x};print} }'
1
12
123
1234
12345
[root@shell-note ~]# awk 'BEGIN { for(y=1;y<=9;y++) { for(x=1;x<=y;x++) {printf x"*"y"="x*y"\t"};print} }'
# 打印99乘法表
循环的控制:
break 条件满足的时候中断循环
continue 条件满足的时候跳过循环
[root@shell-note ~]# awk 'BEGIN{for(i=1;i<=5;i++) {if(i==3) break;print i} }'
1
2
[root@shell-note ~]# awk 'BEGIN{for(i=1;i<=5;i++) {if(i==3) continue;print i} }'
1
2
4
5
3.算术运算
+ - * / %(模) ^(幂2^3)
可以在模式中执行计算,awk都将按浮点数方式执行算术运算
- 例
[root@shell-note ~]# awk -F: '{ shells[$NF]++ };END{for (i in shells) {print i,shells[i]} }' /etc/passwd
/bin/sync 1
/bin/bash 3
/sbin/nologin 27
/sbin/halt 1
/sbin/shutdown 1
# 统计/etc/passwd中各种类型shell的数量
[root@shell-note ~]# ss -an |grep :80 |awk -F":" '!/LISTEN/{ip_count[$(NF-1)]++} END{for(i in ip_count){print i,ip_count[i]}}' |sort -k2 -rn |head
80 172.20.2.23 2
1] 1
# 统计当前访问的每个IP的数量
4.小测试
写一个检测磁盘使用率的脚本,磁盘使用空间大于5%时,输出磁盘空间不足
[root@shell-note ~]# vim Disk_usage.sh
#!/bin/bash
df -h| grep ^/dev| awk '{print $1"总容量为:"$2"\t已使用空间为:"$3"\t还可使用空间为:"$4"\t使用率为:"$(NF-1)}'
while true
do
Size=`df -h| grep ^/dev| awk '{print $(NF-1)}'| grep -Eo "[[:digit:]]+"`
if [ $Size -ge 5 ];then
echo "磁盘容量不足"
break
else
sleep 3s
continue
fi
done
[root@shell-note ~]# dd if=/dev/urandom of=/root/b.txt bs=1M count=5000
# 测试,写入5G随机数字到b.txt文件中
[root@shell-note ~]# source Disk_usage.sh
/dev/vda1总容量为:100G 已使用空间为:8.0G 还可使用空间为:93G 使用率为:8%
磁盘容量不足
[root@shell-note ~]# ln -s Disk_usage.sh /etc/profile.d/Disk_usage.sh
# 每次登录执行一次脚本
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具