Shell 编程(六):文本三剑客之 Awk

2023-03-06 09:19:15 浏览数 (3)

awk 简介

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

工作原理

  1. 通过关键字 BEGIN 执行 BEGIN 块的内容,即 BEGIN 后花括号 {} 的内容。
  2. 完成 BEGIN 块的执行,开始执行 body 块。
  3. 读入有 n 换行符分割的记录。
  4. 将记录按指定的域分隔符划分域,填充域,0 则表示所有域(即一行内容),1 表示第一个域,
  5. 依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行 awk-commands 的内容。
  6. 循环读取并执行各行直到文件结束,完成 body 块执行。
  7. 开始 END 块执行,END 块可以输出最终结果。

首先执行 BEGIN(只执行一次),再根据文本一行一行执行pattern{commands}(类似于sed),最好执行 END(只执行一次)

语法格式

格式类型

命令

格式一

awk ‘BEGIN{}pattern{commands}END{}’ file_name

格式二

standard output | awk’BEGIN{}pattern{commands}END{}’

语法格式

解释

BEGIN{}

正式处理数据之前执行

pattern

匹配模式(和sed pattern 一样)

{commands}

处理命令,可能多行

END{}

处理完所有匹配数据后执行

内置变量

语法格式

解释

$0

打印行所有信息

$1 - $n

打印行的第 1 到 n 个字段的信息

NF (Number Field)

处理行的字段个数

NR (Number Row)

处理行的行号

FNR (File Number Row)

多文件处理时,每个文件单独记录行号

FS (Field Separator)

字段分割符,不指定时默认以空格或 tab 键分割

RS (Field Separator)

行分隔符,不指定时以回车分割 n

OFS

输出字段分隔符

ORS

输出行分隔符

FILENAME

处理文件的文件名

ARGC

命令行参数个数

ARGV

命令行参数数组

每行字段数索引开始为 1 并不为 0

例子

新建文件 student.txt,string.txt

代码语言:javascript复制
> cat 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
> cat string.txt
Hadoop|Spark|Flume--Javal|Python|Scala|Go--Allen|Mike|Meggie
  1. 打印行所有信息
代码语言:javascript复制
> awk '{print $0}' 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
  1. 打印行的第 1 到 2 个字段的信息
代码语言:javascript复制
> awk '{print $1,$2}' student.txt
allen 80
mike 78
Kobe 66
Jerry 98
wang 87
  1. 打印每行行的字段个数
代码语言:javascript复制
> awk '{print NF}' student.txt
5
5
5
5
5
  1. 打印处理行的行号
代码语言:javascript复制
> awk '{print NR}' student.txt
1
2
3
4
5
  1. 打印多文件单独记录行号
代码语言:javascript复制
> awk '{print FNR}' string.txt student.txt
1
1
2
3
4
5
  1. -- 行分隔符和用 | 列分割符分割行,且输出字段分隔符为 &
代码语言:javascript复制
> awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $3}' string.txt
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $3}' string.txt
  1. 输出文件名
代码语言:javascript复制
> awk '{print FILENAME}' string.txt
string.txt

格式化输出之 printf

格式符

含义

%s

打印字符串

%d

打印十进制数

%f

打印一个浮点数

%x

打印十六进制数

%o

打印八进制数

%e

打印数字的科学计数法形式

%c

打印单个字符的 ASCII 码

修饰符

修饰符

含义

-

左对齐

右对齐

#

显示 8 进制在前面加 0,显示 16 进制在前面加 0x

例子

  1. 以字符串格式打印 /etc/passwd 中的第 7 个字段,以 “:” 作为分隔符
代码语言:javascript复制
> awk 'BEGIN{FS=":"}{printf "%s n",$7}' passwd
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt
/sbin/nologin
  1. 以 10 进制格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
代码语言:javascript复制
> awk 'BEGIN{FS=":"}{printf "%d n",$3}' passwd
0 
1 
2 
3 
4 
5 
6 
7 
8 
...
  1. 以浮点数格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
代码语言:javascript复制
> awk 'BEGIN{FS=":"}{printf "%f n",$3}' passwd
0.000000 
1.000000 
2.000000 
3.000000 
4.000000 
5.000000 
6.000000 
7.000000 
8.000000
...
  1. 以 8 进制数格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
代码语言:javascript复制
> awk 'BEGIN{FS=":"}{printf "%o n",$3}' passwd
0 
1 
2 
3 
4 
5 
6 
7 
10
...
  1. 以科学计数法格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
代码语言:javascript复制
> awk 'BEGIN{FS=":"}{printf "%e n",$3}' passwd
0.000000e 00 
1.000000e 00 
2.000000e 00 
3.000000e 00 
4.000000e 00 
5.000000e 00 
6.000000e 00 
7.000000e 00 
8.000000e 00
...

匹配的两种用法

RegExp

  1. 匹配 /etc/passwd 文件行中含有 root 字符串的所有行
代码语言:javascript复制
> awk '/root/{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
  1. 匹配 /etc/passwd 文件行中以 yarn 开头的所有行
代码语言:javascript复制
> awk '/^yarn/{print $0}' passwd

运算符匹配

关系运算符

含义

<

小于

>

大于

<=

小于等于

>=

大于等于

==

等于

!=

不等于

~

匹配正则表达式

!~

不匹配正则表达式

  1. 以:为分隔符,匹配 /etc/passwd 文件中第 3 个字段小于 50 的所有行信息
代码语言:javascript复制
> awk 'BEGIN{FS=":"}$3<50{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
  1. 以:为分隔符,匹配 /etc/passwd 文件中第 7 个字段不为 /bin/bash 的所有行信息
代码语言:javascript复制
> awk 'BEGIN{FS=":"}$7!="/bin/bash"{print $0}' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nolog
...

布尔运算符匹配

布尔运算符

含义

||

&&

  1. 以:为分隔符,匹配 /etc/passwd 文件中包含 hdfs 或 yarn 的所有行信息
代码语言:javascript复制
> awk 'BEGIN{FS=":"} hdfs|| yarn {print $0}' passwd
  1. 以:为分隔符,匹配 /etc/passwd 文件中第 3 个字段小于 50 并且第 4 个字段大于 50 的所行信息
代码语言:javascript复制
> awk 'BEGIN{FS=":"}$3<50 && $4>50{print $0}' passwd
games:x:12:100:games:/usr/games:/sbin/nologin

动作中的表达式用法

运算符

含义

-

*

/

%

^或**

乘方

x

在返回 x 变量之前,x 变量加1

x

在返回 x 变量之后,x 变量加1

  1. 使用 awk 计算 /etc/services 中的空白行数量
代码语言:javascript复制
> awk 'BEGIN{count=0}/^$/{count  ;}END{print count}' services 
20
  1. 计算学生课程分数平均值,学生课程文件内容如下:
代码语言:javascript复制
> cat 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","chinese","math","english","computer","avg"}{avg=($2 $3 $4 $5)/4;printf "%-10s %-10d %-10d %-10d %-10d %-10d n",$1,$2,$3,$4,$5,avg}' student.txt
name       chinese    math       english    computer   avg        
allen      80         90         87         91         87         
mike       78         86         93         96         88         
Kobe       66         92         82         78         79         
Jerry      98         74         66         54         73         
wang       87         21         100        43         62  

动作中的条件及循环语句

条件语句

代码语言:javascript复制
if (condition)
    action-1
else
    action-2
  1. 以:为分隔符,只打印 /etc/passwd 中第 3 个字段的数值在 50-100 范围内的行信息
代码语言:javascript复制
> awk 'BEGIN{FS=":"}$3>50 && $3<100{print $0}' passwd
dbus:x:81:81:System message bus:/:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
  1. 计算下列每个同学的平均分数,并且只打印平均分数大于90的同学姓名和分数信息
代码语言:javascript复制
> cat 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","chinese","math","english","computer","avg"}{avg=($2 $3 $4 $5)/4;if(avg>90)printf "%-10s %-10d %-10d %-10d %-10d %-10d n",$1,$2,$3,$4,$5,avg}' student.txt

循环语句

代码语言:javascript复制
for (initialisation; condition; increment/decrement)
    action
    
while (condition)
    action

do(
)while()
  1. 计算1 2 3 4 … 100的和,请使用while、 do while、 for三种循环方式实现(我只写for了)
代码语言:javascript复制
> awk 'BEGIN{sum=0;for(i=0;i<=100;i  )sum =i;print sum}'
5050

字符串函数

函数名

解释

函数返回值

length(str)

计算字符串长度

整数长度值

index(str1,str2)

在 str1 中查找 str2 的位置

返回值为位置索引,从 1 计数

tolower(str)

转换为小写

转换后的小写字符串

toupper(str)

转换为大写

转换后的大写字符串

substr(str,m,n)

从 str 的 m 个字符开始,截取 n 位

截取后的子串

split(str,array,fs)

按 fs 切割字符串,结果保存 array

切割后的子串的个数

match(str,RE)

在 str 中按照 RE 查找,返回位置

返回索引位置

sub(RE,RepStr,str)

在 str 中搜索符合 RE 的字串,将其替换为 RepStr;(只替换第一个)

替换的个数

gsub(RE,RepStr,str)

在 str 中搜索符合 RE 的字串,将其替换为 RepStr;(替换所有)

替换的个数

  1. 以:为分隔符,返回 /etc/passwd 中每行中每个字段的长度
代码语言:javascript复制
> awk '{print length($0)}' passwd
31
32
39
36
40
31
44
32
46
44
...
  1. 搜索字符串“I have a dream“中出现“ea“字符串的位置
代码语言:javascript复制
> awk 'BEGIN{str="I have a dream";print index(str,"ea")}'
12
  1. 将字符串“Hadoop is a bigdata Framawork“全部转换为小写
代码语言:javascript复制
> awk 'BEGIN{str="Hadoop is a bigdata Framawork";print tolower(str)}'
hadoop is a bigdata framawork
  1. 将字符串“Hadoop is a biadata Framawork“全部转换为大写
代码语言:javascript复制
> awk 'BEGIN{str="Hadoop is a bigdata Framawork";print toupper(str)}'
HADOOP IS A BIGDATA FRAMAWORK

常用选项

选项

解释

-v

参数传递

-f

指定脚本文件

-F

指定分割符(FS)

-V

查看 awk 的版本号

数组用法

shell 中数组的用法:

代码语言:javascript复制
array=("百度" "阿里" "腾讯")

功能

命令

打印元素

echo ${array[2]}

打印元素个数

echo ${#array[@]}

打印元素长度

echo ${#array[3]}

给元素赋值

array[1]=”字节跳动”

删除元素

unset array[2];unset array;

分片访问

echo ${array[@]: 1:3}

元素内容替换

$array [@/e/E]

数组的遍历

for a in ${array[0]} do echo $adone

awk 中数组的用法:

代码语言:javascript复制
array[index]=value

功能

命令

创建数组

array[1]=”hello”

删除数组元素

delete array[1]

多维数组

array[“0,0”] = 100;array[“0,1”] = 200;array[“0,2”] = 300;array[“1,0”] = 400;array[“1,1”] = 500;array[“1,2”] = 600;

  1. 统计主机上所有的 TCP 连接状态数,按照每个 TCP 状态分类
代码语言:javascript复制
> netstat -an | grep tcp| awk '{array[$6]  }END{ for (a in array) print a,array[a]}'
LISTEN 21
SYN_RECV 1
ESTABLISHED 4
TIME_WAIT 2
  1. 计算横向数据综合,计算纵向数据总和
代码语言:javascript复制
> cat 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","chinese","math","english","computer","total"}{total=$2 $3 $4 $5;totol_count =total;chinese_count =$2;math_count =$3;english_count =$4;computer_count =$5;printf "%-10s %-10d %-10d %-10d %-10d %-10d n",$1,$2,$3,$4,$5,total}END{printf "%-10s %-10d %-10d %-10d %-10d %-10d n","total",chinese_count,math_count,english_count,computer_count,totol_count}' student.txt
name       chinese    math       english    computer   total      
allen      80         90         87         91         348        
mike       78         86         93         96         353        
Kobe       66         92         82         78         318        
Jerry      98         74         66         54         292        
wang       87         21         100        43         251        
total      409        363        428        362        1562 

1 人点赞