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

  1. awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符(RS)结束

  2. 每行被间隔符 : (默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始

    问:awk如何知道用空格来分隔字段的呢?

    答:因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空格

  3. awk使用print函数打印字段,打印出来的字段会以空格分隔,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格

  4. 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{处理结束后}'

  1. 打印/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
# 每次登录执行一次脚本
posted @   llllyh812  阅读(24)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示