Shell编程—【08】Linux的awk文本处理工具 awk命令

介绍

  • awk是一个文本处理工具,通常用于处理处理数据并生成结果报告
  • awk的命名是它的创始人 Affred Aho、Peter Weinberger 和Brian Kernighan 姓氏的首个字母组成的

语法格式

  • 第一种形式:awk ‘BEGIN{}pattern{commands}END{}’ file_name
  • 第二种形式:standard output | awk ‘BEGIN{}pattern{commands}END{}’

语法格式说明

语法格式解释
BEGIN{}正式处理数据之前执行
pattern匹配模式
{commands}处理命令,可能多行
END{}处理完所有匹配数据后执行

内置变量对照表

内置变量含义
$0含义
1 − 1- 1n当前行的第1-n个字段
NF (number field)当前行的字段个数,也就是有多少列
NR (number row)当前行的行号,从1开始计数
FNR (file number row)多文件处理时,每个文件行号单独计数,都是从0开始
FS (field separator)输入字段分隔符。不指定默认以空格或tab键分割
RS (row separator)输入行分隔符。默认回车换行
OFS (output field separator)输出字段分隔符。默认为空格
ORS (output row separator)输出行分隔符。默认为回车换行
FILENAME处理文件的文件名
ARGC ()命令行参数个数
ARGV ()命令行参数数组
awk '{print}'  /etc/passwd
awk 'BEGIN{FS=":"}{print $1} '  /etc/passwd

# number field未指定分隔符
awk '{print NF} '  /etc/passwd   
# number field 指定分隔符:
awk 'BEGIN{FS=":"}{print NF} '  /etc/passwd

#number row
awk '{print NR} '  /etc/passwd

# number row 多个文件行号一直 + 
awk '{print NR} '  /etc/passwd /etc/passwd
# file number row 多个文件,另一个文件也是从1开始
awk '{print FNR} '  /etc/passwd /etc/passwd

awk 'BEGIN{FS=":"}{print $7} '  /etc/passwd
awk 'BEGIN{FS=":"}{print $NF} '  /etc/passwd

printf 的格式说明符

格式符含义
%s打印字符串
%d打印十进制数
%f打印一个浮点数
%x打印十六进制数
%o打印八进制数
%e打印数字的科学计数方式
%c打印单个字符的ADCII码

printf 的修饰符

修饰符含义
-左对齐
+右对齐
#显示8进制的在前面+0,显示16进制的在前面加0x
awk 'BEGIN{FS=":"}{printf "%s\n",$1} '  /etc/passwd

awk 'BEGIN{FS=":"}{printf  "%20s%20s\n",$1,$7} '  /etc/passwd
awk 'BEGIN{FS=":"}{printf  "%-20s%-20s\n",$1,$7} '  /etc/passwd

格式符示例

#1.以字符串格式打印 /etc/passwd中的第7个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%s\n",$7}' /etc/passwd
#2.以10进制格式打印 /etc/passwd中的第3个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%d\n",$3}' /etc/passwd
#3.以浮点数格式打印 /etc/passwd中的第3个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%f\n",$3}' /etc/passwd
awk 'BEGIN{FS=":"}{printf "%0.2f\n",$3}' /etc/passwd
#4.以16进制数格式打印 /etc/passwd中的第3个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%x\n",$3}' /etc/passwd
awk 'BEGIN{FS=":"}{printf "%#x\n",$3}' /etc/passwd
#5.以8进制数格式打印 /etc/passwd中的第3个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%o\n",$3}' /etc/passwd
awk 'BEGIN{FS=":"}{printf "%#o\n",$3}' /etc/passwd

#6.以科学计数法格式打印 /etc/passwd中的第3个字段,以“:”作为分隔符
awk 'BEGIN{FS=":"}{printf "%e\n",$3}' /etc/passwd

模式匹配的两种方法

  • 第一种模式匹配 :RegExp
  • 第二种模式匹配 :关系运算符匹配
1.RegExp
#匹配 /etc/passwd文件行中含有root字符串的所有行
awk 'BEGIN{FS=":"} /root/ {print $0}' /etc/passwd
#匹配 /etc/passwd文件行中以root开头的所有行
awk 'BEGIN{FS=":"} /^root/ {print $0}' /etc/passwd
2.运算符匹配

关系运算符匹配

关系运算符含义
<小于
>大于
<=小于等于
>=大于等于
==等于
!=不等于
~匹配正则表达式
!~不匹配正则表达式
#1.以:为分隔符,匹配/etc/passwd 文件中的第3个字段小于50的所有行信息
awk 'BEGIN{FS=":"} $3<50 {print $0}' /etc/passwd
#2.以:为分隔符,匹配/etc/passwd 文件中的第3个字段大于50的所有行信息
awk 'BEGIN{FS=":"} $3>50 {print $0}' /etc/passwd
#3.以:为分隔符,匹配/etc/passwd 文件中的第7个字段为/bin/bash的所有行信息
awk 'BEGIN{FS=":"} $7=="/bin/bash" {print $0}' /etc/passwd
#4.以:为分隔符,匹配/etc/passwd 文件中的第7个字段不为/bin/bash的所有行信息
awk 'BEGIN{FS=":"} $7!="/bin/bash" {print $0}' /etc/passwd
#5.以:为分隔符,匹配/etc/passwd 文件中的第3个字段包含三个以上数字的所有行信息
awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/{print $0}' /etc/passwd

布尔运算符匹配

布尔运算符含义
||
&&
#1.以:为分隔符,匹配/etc/passwd 文件中包含hdfs或yarn的所有行信息
awk 'BEGIN{FS=":"} /hdfs/||/yarn/ {print $0}' /etc/passwd
#2.以:为分隔符,匹配/etc/passwd 文件中第3个字段小于50且第4个字段大于50的所有行信息
awk 'BEGIN{FS=":"} $3<50&&$4>50 {print $0}' /etc/passwd

awk动作表达式中的算术运算符

运算符含义
+
-
*
/
%
^ 或**乘方
++X在返回X变量之前,X变量加1
X++在返回X变量之后,X变量加1
awk 'BEGIN{var1=20;var2="hello";print var1,var2}'
awk 'BEGIN{num1=20;num2+=num1;print num1,num2}'
awk 'BEGIN{num1=20;num2=num2+num1;print num1,num2}'
awk 'BEGIN{num1=20;num2=20;print num1,num2}'
awk 'BEGIN{num1=20;num2=30;print num1+num2}'
awk 'BEGIN{num1=20;num2=30;print num1-num2}'
awk 'BEGIN{num1=20;num2=30;print num1*num2}'
awk 'BEGIN{num1=20;num2=30;print num1/num2}'
awk 'BEGIN{num1=20;num2=30;printf "%0.2f\n",num1/num2}'
awk 'BEGIN{num1=20;num2=2;print num1^num2}'
awk 'BEGIN{num1=20;num2=2;print num1**num2}'

# 先赋值,后++
awk 'BEGIN{num1=20;num2=num1++;print num1,num2}'
# 先++,后赋值
awk 'BEGIN{num1=20;num2=++num1;print num1,num2}'
#1.使用awk计算/etc/services 中的空白行个数
awk '/^$/{sum++}END{print sum}' /etc/services 
17
#2.计算学生课程分数平均值,学生课程分数文件student.txt如下
Allen 80 90 96 98
Mike  93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han   85 95 75 90
Li    78 88 98 100

awk '{total=$2+$3+$4+$5;AVG=total/4;printf "%-8s%-5d%-5d%-5d%-5d%0.2f\n",$1,$2,$3,$4,$5,AVG}' student.txt
Allen 80 90 86 98 87.4

awk 'BEGIN{printf "%-8s%-8s%-8s%-8s%-8s%s\n","姓名","语文","数学",“英语”,"物理","平均分"}{total=$2+$3+$4+$5;AVG=total/4;printf "%-8s%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,AVG}' student.txt

awk中的条件及循环语句

条件语句:
if (条件表达式)
   动作1
else if (条件表达式)
   动作2
else 
   动作3

循环语句:
while 循环:
   while (条件表达式)
      动作

do while 循环:
   do 
      动作
   while (条件表达式)

for 循环:
   for (初始化计数器;计数器测试;计数器变更)
      动作

  • 示例:
#1.以:为分隔符,只打印/etc/passwd中第3个字段的数值在50-100范围内的行信息
awk 'BEGIN{FS=":"}{if($3>50 && $3<100) print $0}' /etc/passwd

awk中的字符串函数

字符串函数对照表

函数名解释函数返回值
length(str)计算字符串长度整数长度值
index(str1,str2)在str1中查找str2的位置返回值为位置索引,从1计数
tolower(str)转换为小写转换后的小写字符串
toupper(str)转换为大写转换后的大学字符串
substr(str,m,n)从str的m个字符开始,截取n位截取后的子串
split(str,arr,fs)按fs切割字符串,结果保存arr切割后的子串的个数
match(str,RE)在str中按照RE查找,返回位置返回索引位置
sub(RE.RepStr,str)在str中搜索符合RE的字串,将其替换为RepStr,只替换第一个替换的个数
gsub(RE.RepStr,str)在str中搜索符合RE的字串,将其替换为RepStr,替换所有替换的个数
#1.以:为分割符,返回/etc/passwd中每行中每个字段的长度
awk 'BEGIN{FS=":"}
{for(i=1;i<=NF;i++){
 if(i!=NF){
   printf "%d:",length($i)
 }else{
   printf "%d",length($i)
 }
 }
 print '\n'
}' /etc/passwd

#2.搜索字符串“I have a dream” 中出现"ea"字符串的位置
awk 'BEGIN{str="I have a dream";location=index(str,"ea");print location}'

#3.将字符串“Hadoop is a bigdata Framework” 全部转换为小写
awk 'BEGIN{str="Hadoop is a bigdata Framework";print tolower(str)}'
#4.将字符串“Hadoop is a bigdata Framework” 全部转换为大写
awk 'BEGIN{str="Hadoop is a bigdata Framework";print toupper(str)}'
#5.将字符串“Hadoop Kafka Spark Storm HDFS YARN Zookeeper”,按照空格为分隔符,分隔每
awk 'BEGIN{str="Hadoop Kafka Spark Storm HDFS YARN Zookeeper";split(str,arr," ");print arr[1]}'

awk 'BEGIN{str="Hadoop Kafka Spark Storm HDFS YARN Zookeeper";split(str,arr," ");for(a in arr){print arr[a]}}'

#6.搜索字符串“Tranction 2345 Start:Select * from master” 第一个数字出现的位置
awk 'BEGIN{str="Tranction 2345 Start:Select * from master";printf "%d",match(str,"[0-9]")}'
#7.截取字符串“transaction start”的子串,截取条件从第4个字符开始,截取5位
awk 'BEGIN{str="transaction start";print substr(str,4,5)}'
#8.替换字符串“Tranction 243 Start,Event ID:9002”中第一个匹配到的数字串为$符号
awk 'BEGIN{str="ranction 243 Start,Event ID:9002";print sub(/[0-9]+/,"$",str)}'
awk 'BEGIN{str="ranction 243 Start,Event ID:9002";sub(/[0-9]+/,"$",str);print str}'

awk中的常用选项

awk选项总结

选项解释
-v参数传递 (定义或引用变量)
-f指定脚本文件
-F指定分隔符
-V查看awk版本号
num1 =20
var ="hello world"
awk -v num2=$num1 -v var1=$var'BEGIN{print num2,var1}'


#awk 'BEGIN{FS=":"}{print $7}' /etc/passwd
awk -F ":" '{print $7}' /etc/passwd

shell中数组的用法

  • array=(“Allen” “Mike” “Messi” “Jerry” “Hanmeimei” “Wang”)
选项解释
打印元素echo ${array[2]}
打印元素个数echo ${#array[@]}
打印元素长度echo ${#array[3]}
给元素赋值array[3]=“Chen”
删除元素unset array[2];unset array
分片访问echo ${array[@]:1:3}
元素内容替换 a r r a y [ @ ] / e / E 只 替 换 第 一 个 e ; {array[@]/e/E} 只替换第一个e; array[@]/e/Ee;{array[@]//e/E} 替换所有的e
|数组的遍历:
 for a in ${array[@]}	
 do
	echo $a
 done 
#1.统计计算机上的所有tcp连接状态数,按照每个tcp状态分类
# $6作为数组下标

netstat -an|grep tcp|awk '{array[$6]++}END{for(a in array) print a,array[a]}'
结果:
LISTEN 12
ESTABLISHED 1

#2.计算横向数据总和,计算纵向数据总和(student.txt)
Allen 80 90 87 91
Mike 78 86 93 96
Kobe 66 92 82 78
Jerry 98 74 66 54
Wang 87 21 100 43

awk 'BEGIN{printf "%-10s%-10s%-10s%-10s%-10s%-10s\n","Name","Yuwen","Math","English","Physical","Total"}
	 {total=$2+$3+$4+$5
	 yuwen_sum+=$2
	 math_sum+=$3
	 eng_sum+=$4
	 phy_sum+=$5
	 printf "%-10s%-10d%-10d%-10d%-10d%-10d\n",$1,$2,$3,$4,$5,total}
	 END{printf "%-10s%-10d%-10d%-10d%-10d\n","",yuwen_sum,math_sum,eng_sum,phy_sum}' student.txt

模拟生产环境数据脚本

db.log.20190608 数据如下:

2019-06-08 10:31:40 15459 Batches: user Jerry insert 5504 records into datebase:product table:detail, insert 5253 records successfully,failed 251 records
2019-06-08 10:31:40 15460 Batches: user Tracy insert 25114 records into datebase:product table:detail, insert 13340 records successfully,failed 11774 records
2019-06-08 10:31:40 15461 Batches: user Hanmeimei insert 13840 records into datebase:product table:detail, insert 5108 records successfully,failed 8732 records
2019-06-08 10:31:40 15462 Batches: user Lilei insert 32691 records into datebase:product table:detail, insert 5780 records successfully,failed 26911 records
2019-06-08 10:31:40 15463 Batches: user Allen insert 25902 records into datebase:product table:detail, insert 14027 records successfully,failed 11875 records

#1.统计每个人分别插入了多少条record进数据库

exam1.awk

    BEGIN{
        printf "%-20s%-20s\n","User","Total records"
    }

    {
        USER[$6]+=$8
    }

    END{
        for(u in USER)
            printf "%-20s%-20d\n",u,USER[u]
    }
 

awk -f exam1.awk  db.log.20190608

#2.统计每个人分别插入成功了多少record,失败了多少record
exam2.awk

    BEGIN{
        printf "%-30s%-30s%-30s\n","User","Success records","Failed records"
    }

    {
        SUCCESS[$6]+=$14
        FAILED[$6]+=$17
    }

    END{
        for(u in SUCCESS)
            printf "%-30s%-30d%-30d\n",u,SUCCESS[u],FAILED[u]
    }
 

awk -f exam2.awk db.log.20190608 

#3.将例子1和例子2结合起来,一起输出,输出每个人分别插入多少条数据,多少成功,多少失败,并且要格式化输出,加上标题

exam3.awk

    BEGIN{
        printf "%-30s%-30s%-30s%-30s\n","Name","total records","success records","failed records"
    }

    {
        TOTAL_RECORDS[$6]+=$8
        SUCCESS[$6]+=$14
        FAILED[$6]+=$17
    }

    END{
        for(u in TOTAL_RECORDS)
            printf "%-30s%-30d%-30d%-30d\n",u,TOTAL_RECORDS[u],SUCCESS[u],FAILED[u]
    }
  

awk -f exam3.awk db.log.20190608 

#4.在例子3的基础上,加上结尾,统计全部插入记录数,成功记录数,失败记录数

exam4_b.awk

    BEGIN{
        printf "%-30s%-30s%-30s%-30s\n","Name","total records","success records","failed records"
    }

    {
        TOTAL_RECORDS[$6]+=$8
        SUCCESS[$6]+=$14
        FAILED[$6]+=$17
    }

    END{
        for(u in TOTAL_RECORDS)
        {
            # 在统计出的结果数组中进行累加
            records_sum+=TOTAL_RECORDS[u]
            success_sum+=SUCCESS[u]
            failed_sum+=FAILED[u]
            printf "%-30s%-30d%-30d%-30d\n",u,TOTAL_RECORDS[u],SUCCESS[u],FAILED[u]
        }

        printf "%-30s%-30d%-30d%-30d\n","",records_sum,success_sum,failed_sum
    }
  

awk -f exam4_b.awk db.log.20190608

方法2:

exam4.awk  

    BEGIN{
        printf "%-30s%-30s%-30s%-30s\n","Name","total records","success records","failed records"
    }

    {
        RECORDS[$6]+=$8
        SUCCESS[$6]+=$14
        FAILED[$6]+=$17
        
        # 在原始数据中进行汇总计算
        records_sum+=$8
        success_sum+=$14
        failed_sum+=$17    
    }

    END{
        for(u in RECORDS)
            printf "%-30s%-30d%-30d%-30d\n",u,RECORDS[u],SUCCESS[u],FAILED[u]

        printf "%-30s%-30d%-30d%-30d\n","total",records_sum,success_sum,failed_sum
    }

#5.查找丢失数据的现象,也就是成功+失败的记录数不等于一共插入的记录数,找出这些数据并显示行号和对应行的日志信息

awk '{if($8!=$14+$17) print NR,$0}' db.log.20190608


写入文件的方式

exam5.awk

    BEGIN{
    }
    {
        if($8!=$14+$17) 
        print NR,$0
    }
 

awk -f exam5.awk db.log.20190608

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值