Shell 编程(六):文本三剑客之 Awk
- 陈大剩
- 2023-02-08 22:34:04
- 1320
awk 简介
awk 是一个文本处理工具,通常用于处理数据并生成结果报告,awk的命名是它的创始人 Alfred Aho、 Peter Weinberger 和 Brian Kernighan 姓氏的首个字母组成的。
工作原理
- 通过关键字 BEGIN 执行 BEGIN 块的内容,即 BEGIN 后花括号 {} 的内容。
- 完成 BEGIN 块的执行,开始执行 body 块。
- 读入有 \n 换行符分割的记录。
- 将记录按指定的域分隔符划分域,填充域,$0 则表示所有域(即一行内容),$1 表示第一个域,$n 表示第 n 个域。
- 依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行 awk-commands 的内容。
- 循环读取并执行各行直到文件结束,完成 body 块执行。
- 开始 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
> 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
- 打印行所有信息
> 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 到 2 个字段的信息
> awk '{print $1,$2}' student.txt
allen 80
mike 78
Kobe 66
Jerry 98
wang 87
- 打印每行行的字段个数
> awk '{print NF}' student.txt
5
5
5
5
5
- 打印处理行的行号
> awk '{print NR}' student.txt
1
2
3
4
5
- 打印多文件单独记录行号
> awk '{print FNR}' string.txt student.txt
1
1
2
3
4
5
- 用
--
行分隔符和用|
列分割符分割行,且输出字段分隔符为&
> awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $3}' string.txt
awk 'BEGIN{RS="--";FS="|";ORS="&"}{print $3}' string.txt
- 输出文件名
> awk '{print FILENAME}' string.txt
string.txt
格式化输出之 printf
格式符 | 含义 |
---|---|
%s | 打印字符串 |
%d | 打印十进制数 |
%f | 打印一个浮点数 |
%x | 打印十六进制数 |
%o | 打印八进制数 |
%e | 打印数字的科学计数法形式 |
%c | 打印单个字符的 ASCII 码 |
修饰符
修饰符 | 含义 |
---|---|
- | 左对齐 |
+ | 右对齐 |
# | 显示 8 进制在前面加 0,显示 16 进制在前面加 0x |
例子
- 以字符串格式打印 /etc/passwd 中的第 7 个字段,以 “:” 作为分隔符
> 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
- 以 10 进制格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
> awk 'BEGIN{FS=":"}{printf "%d \n",$3}' passwd
0
1
2
3
4
5
6
7
8
...
- 以浮点数格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
> 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
...
- 以 8 进制数格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
> awk 'BEGIN{FS=":"}{printf "%o \n",$3}' passwd
0
1
2
3
4
5
6
7
10
...
- 以科学计数法格式打印 /etc/passwd 中的第 3 个字段,以 “:” 作为分隔符
> 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
- 匹配 /etc/passwd 文件行中含有 root 字符串的所有行
> awk '/root/{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
- 匹配 /etc/passwd 文件行中以 yarn 开头的所有行
> awk '/^yarn/{print $0}' passwd
运算符匹配
关系运算符 | 含义 |
---|---|
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
~ | 匹配正则表达式 |
!~ | 不匹配正则表达式 |
- 以:为分隔符,匹配 /etc/passwd 文件中第 3 个字段小于 50 的所有行信息
> 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
- 以:为分隔符,匹配 /etc/passwd 文件中第 7 个字段不为 /bin/bash 的所有行信息
> 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
...
布尔运算符匹配
布尔运算符 | 含义 |
---|---|
|| | 或 |
&& | 与 |
! | 非 |
- 以:为分隔符,匹配 /etc/passwd 文件中包含 hdfs 或 yarn 的所有行信息
> awk 'BEGIN{FS=":"} hdfs|| yarn {print $0}' passwd
- 以:为分隔符,匹配 /etc/passwd 文件中第 3 个字段小于 50 并且第 4 个字段大于 50 的所行信息
> 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 |
- 使用 awk 计算 /etc/services 中的空白行数量
> awk 'BEGIN{count=0}/^$/{count++;}END{print count}' services
20
- 计算学生课程分数平均值,学生课程文件内容如下:
> 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
动作中的条件及循环语句
条件语句
if (condition)
action-1
else
action-2
- 以:为分隔符,只打印 /etc/passwd 中第 3 个字段的数值在 50-100 范围内的行信息
> 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
- 计算下列每个同学的平均分数,并且只打印平均分数大于90的同学姓名和分数信息
> 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
循环语句
for (initialisation; condition; increment/decrement)
action
while (condition)
action
do(
)while()
- 计算1+2+3+4+…+100的和,请使用while、 do while、 for三种循环方式实现(我只写for了)
> 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;(替换所有) | 替换的个数 |
- 以:为分隔符,返回 /etc/passwd 中每行中每个字段的长度
> awk '{print length($0)}' passwd
31
32
39
36
40
31
44
32
46
44
...
- 搜索字符串“I have a dream“中出现“ea“字符串的位置
> awk 'BEGIN{str="I have a dream";print index(str,"ea")}'
12
- 将字符串“Hadoop is a bigdata Framawork“全部转换为小写
> awk 'BEGIN{str="Hadoop is a bigdata Framawork";print tolower(str)}'
hadoop is a bigdata framawork
- 将字符串“Hadoop is a biadata Framawork“全部转换为大写
> awk 'BEGIN{str="Hadoop is a bigdata Framawork";print toupper(str)}'
HADOOP IS A BIGDATA FRAMAWORK
常用选项
选项 | 解释 |
---|---|
-v | 参数传递 |
-f | 指定脚本文件 |
-F | 指定分割符(FS) |
-V | 查看 awk 的版本号 |
… | … |
数组用法
shell 中数组的用法:
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 $a done |
awk 中数组的用法:
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; |
- 统计主机上所有的 TCP 连接状态数,按照每个 TCP 状态分类
> 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
- 计算横向数据综合,计算纵向数据总和
> 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
参考 & 引用
赞
(0)