一起入门Linux咯 | 文本处理awk命令
如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!博客目录 | 先点这里
- 前提概念
- 什么是awk?
- awk的工作模式
- 注意事项
- awk命令讲解
- awk的选项
- awk的内置变量
- awk格式化输出printf
- awk模式匹配
- awk动作中表达式用法
- awk动作中的条件及循环语句
- awk的字符串函数
- awk的数组
- awk命令实践
- 修改默认字段分割符,输入输出
- printf格式化输出
- 格式化输出top的信息
- 输出每个学生的平均成绩
前提概念
什么是awk?
- awk在linux中是一个很重要的文本处理工具,也是一个重要的报告生成器,可以对我们提供的数据进行复杂的处理,并生成我们需要的数据报告格式,所以也称为数据报告生成器
- 也因为awk可以生成我们自定义的各种格式,所以有时候也称awk是一门编程,因为awk中,可以执行条件判断语句,循环语句,声明变量等各种操作
awk的工作模式
awk的工作模式其实就类似sed一样,逐行处理行数据
语法格式
- 第一种
stdout | awk [option] 'BEGIN{}pattern{comands}END{}'
,通过管道对标准输出进行处理 - 第二种
awk [option] 'BEGIN{}pattern{comands}END{}' files
, 对文件进行处理
重要概念
BEGIN中的命令是在遍历文件前执行的,END中的命令是在处理文件所有数据后才执行的,pattern中的命令就是对每个匹配行进行处理
BEGIN{}
正式处理数据之前执行pattern
模式匹配{commands}
对匹配行进行command命令处理,可以写多个命令,甚至写条件判断语句,循环语句等END{}
处理完所有匹配行数据后执行
akw注意事项
- {}中引用变量,不需要加$符号,比如
'{print NF}'
- 总之awk的’'中的内容,如果有字符串,建议使用""双引号括起来,避免被以为是变量
- akw可以跟多个文件,既处理多个文件
awk命令讲解
awk的选项
-F fs
fs为输入分隔符,fs可以是字符串或正则表达式, 比如awk -F "-" '{print $0}' file
-v var=value
赋值一个用户定义变量,将外部变量传递给awk-f scripfile
从脚本文件中读取awk命令
awk的内置变量
内置变量 | 含义 | 备注 |
---|---|---|
$0 | 整行内容 | |
$1 -$n | 当前行的第1-n个字段 | 比如hello world,$1是hello, $2是world |
NF | 当前行的字段个数,也就是有多少列 | Number Field |
NR | 当前行的行号,从1开始计数 | Number Row |
FNR | 多文件处理时,每个文件行号单独计数,都是从0开始 | File Number Row |
FS | 输入字段分隔符,不指定就默认以空格或tab键分割 | 输入给awk的内容以FS作为在字段分隔 |
RS | 输入行分隔符,默认回车换行 | |
OFS | 输出字段分割符,默认为空格 | awk输出的内容的字段以OFS作为字符分割 |
ORS | 输出行分割符,默认为回车换行 | |
FILENAME | 输入行分隔符,默认回车换行 | |
ARGC | 命令行参数个数 | awk ‘BEGIN{print ARGC}’ cpu 输出2个参数,awk是一个,cpu文件名也是一个 |
ARGV | 命令行参数数组 |
- 所谓字段,因为awk默认的字段是分割是空格或tab,既一行数据中,以空格或tab键分割的字符串,awk认为是单独的字段,既单词。就像Java的string.spilt(" ")一样
- {}中的命令,以;分隔不同的命令
- pattern默认为Null时,代表匹配所有行,
{commands}
默认为空是,输出行的完整信息,等价print $0
- awk的pattern不同于sed的方式操作行号,而是根据
NR变量
去控制行号,同时pattern不仅局限于使用正则,同时支持运算符号判断,比如大于,小于,等于,不等于等…
awk格式化输出printf
- printf和print的很大的区别是,print会默认换行,而printf不会做任何事情
格式符 | 含义 | 备注 |
---|---|---|
%s | 打印字符串 | printf "%20s" helloworld 右对齐20字符固长输出 |
%d | 打印十进制数 | |
%f | 打印浮点数 | printf "%0.3f" 123 |
%x | 打印十六进制数 | printf "%#x" 123 (输出带0x),printf "%x" 123 |
%o | 打印八进制数 | |
%e | 打印科学计数法 | |
%c | 打印字符的ASCII码 | |
- | 左对齐 | printf "%-20s" hello 左对齐,20固长的字符串输出 |
+ | 右对齐 | printf默认输出是右对齐 |
# | 打印字符的ASCII码 |
awk模式匹配
awk模式匹配有两种用法。第一种是RegExp
, 按照正则表达式匹配;第二种是关系运算
,按照关系运算匹配
RegExp
- 这个就没有什么好说的,跟sed差不多,不过经过测试awk的pattern不支持行号
/root/
查看含有root字符串的行/root/,/10/
匹配带有root到10字符串的行数据,范围匹配
关系运算
<
,>
,<=
,>=
,==
,!=
小于,大于,小于等于,大于当于,相等,不等
比如,匹配某文件中行数据第3个字段小于50的行,awk '$3<50{print $0}'
比如,匹配某文件中行数据第7个字段为hello的行,awk '$7=="hello"{print $0}'
比如,匹配某文件中行号为10的行,awk 'NR==10'{print $0} file.txt
~
,!~
匹配正则表达式,不匹配正则表达式
比如,匹配某文件中行数据的第3个字段是3位以上数字的行,awk '$3~/[0-9]{3,}{print $0}/'
||
,&&
,!
或,与,非
比如,匹配某文件中行数据包含java或python的行,awk '/java/||/python/{print $0}'
比如,匹配某文件中行数据第3列大于50且第8列小于100的行,awk '$3>50 || $8<100{print $0}'
awk动作中表达式用法
格式符 | 含义 | 备注 |
---|---|---|
+ | 加 | |
- | 减 | |
* | 乘 | |
/ | 除 | |
% | 模 | |
^或** | 乘方 | |
++x | 就是语言中的i++ | |
x++ | 就是语言中的i++ |
- 什么是awk动作,其实就是’BEGIN{}pattern{}END{}'中{}的代码。比如说
BEGIN{num = 1;num++;} {num++;}END{print num}
, BEGIN代码块初始化num变量,{commands}每匹配一个行,num++, 匹配文件结束后执行END代码块的print num, 输出num变量,这样我们就知道了awk总共匹配了多少行
awk动作中的条件及循环语句
条件语句
- 这一点其实跟我们平时的c语言是差不多的
if(条件){
...
} else if (条件){
...
} else {
...
}
循环语句
- while循环
while(条件表达式){
...
}
- do while循环
do{
...
}while(条件表达式)
- for循环
for(i = 0; i<10;i++){
...
}
跟c的区别就是,awk的变量不需要声明
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(str,arr,fs) | 从str搜索符合RE的子串,将其替换成RepStr, 替换全部 | 替换个数 | RE是正则 |
- 查找str字符串中"love"子串的位置
awk 'BEGIN{str="I love you"; index=index(str,"love");print index}'
awk 'BEGIN{str="I love you"; index=match(str,"love");print index}'
awk的数组
awk的数组的使用和shell脚本中数组的使用是有些不同的,这个要注意
Shell脚本下数组的使用
- 定义数组
array=("Jerry" "Tom" "Li" "Allen")
- 打印元素
echo ${array[2]}
- 打印数组元素的个数
echo ${#array[@]}
,求个数基本都是#xx, 比如字符串的长度#str - 打印元素字符的个数
echo{#array[1]}
- 给元素赋值
array[3] = "Mike"
- 删除元素
unset array[2]
, 要删除整个数组则是unset array[@]
, 其实就是清空遍历 - 分片访问
echo ${array[@]:1:3}
- 元素内容替换
${array[@]/old/new}
, 使用new替换一个old
${array[@]//old/new}
, 使用new替换所有old - 数组的遍历
for i in array
do
echo $i
done
awk脚本下数组的使用
- shell数组的索引下标从0开始,而awk数组索引下标从1开始
- shell需要通过${array[1]}获取数组元素的值,而awk就跟普通语言一样,array[1]即可
awk命令实践
修改默认字段分割符,输入输出
- cpu文件 ,大致是top -c输出的内容
# PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,COMMAND
165,lwj,20,0,16920,3564,3452,S,0.0,0.0,0:00.30,-bash,
266,lwj,20,0,17016,3656,3344,S,0.0,0.0,0:00.36,-bash,
321,lwj,20,0,27888,6492,3304,S,0.0,0.0,0:00.20,vim,h,
364,lwj,20,0,16884,3444,3372,S,0.0,0.0,0:00.07,-bash,
375,lwj,20,0,14860,1148,964,S,0.0,0.0,0:00.03,sed,-n,/lwj/w,cpua,
- awk命令:
awk 'BEGIN{FS=","}{print $1,$2,$3}' cpu
, 修改字段分隔符FS为逗号,
,不再是空格或tab键, 输出每行的第1,2,3个单词(要注意,逗号用双引号括住,而不是单引号)
165 lwj 20
266 lwj 20
321 lwj 20
364 lwj 20
375 lwj 20
- awk命令
awk 'BEGIN{FS=",";OFS="-"}{print $0}' cpu
, 输入字符分隔修改为",“逗号,输出字符分隔修改为”-",输出每行的第1,2,3个单词(要注意print $1,$2,$3字段之间要用","号分隔,如果没有逗号,使用空格,那么输出分隔符会失效)
165-lwj-20
266-lwj-20
321-lwj-20
364-lwj-20
375-lwj-20
printf格式化输出
- cpu文件 ,大致是top -c输出的内容
# PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,COMMAND
165,lwj,20,0,16920,3564,3452,S,0.0,0.0,0:00.30,-bash,
266,lwj,20,0,17016,3656,3344,S,0.0,0.0,0:00.36,-bash,
321,lwj,20,0,27888,6492,3304,S,0.0,0.0,0:00.20,vim,h,
364,lwj,20,0,16884,3444,3372,S,0.0,0.0,0:00.07,-bash,
375,lwj,20,0,14860,1148,964,S,0.0,0.0,0:00.03,sed,-n,/lwj/w,cpua,
- awk命令:
awk 'BEGIN{FS=","}{printf "%-20s %-20s",$1,$2}' cpu
格式化输出cpu的第一和第二列字段,格式化为输出字符串,每一列固定20个字符串,左对齐方式输出,字段之间2个空格长度隔开
165 lwj
266 lwj
321 lwj
364 lwj
375 lwj
- awk命令:
awk 'BEGIN{FS=","}{printf "%+20s %+20s",$1,$2}' cpu
, 右对齐
165 lwj
266 lwj
321 lwj
364 lwj
375 lwj
格式化输出top的信息
- linux cpu信息,通过
top -H -n 1 >> cpu
将top的信息追加到cpu文件中
top - 20:42:12 up 40 days, 9:46, 1 user, load average: 0.00, 0.07, 0.04
Threads: 284 total, 1 running, 245 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.1 us, 1.0 sy, 0.0 ni, 97.4 id, 0.5 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1877536 total, 157380 free, 822956 used, 897200 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 856424 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6939 ubuntu 20 0 43556 4076 3296 R 5.9 0.2 0:00.01 top
1 root 20 0 225520 7796 5204 S 0.0 0.4 1:46.62 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.20 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq
7 root 20 0 0 0 0 S 0.0 0.0 2:09.87 ksoftirqd/0
8 root 20 0 0 0 0 I 0.0 0.0 21:46.47 rcu_sched
9 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_bh
10 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
11 root rt 0 0 0 0 S 0.0 0.0 0:06.17 watchdog/0
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/0
13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
14 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_kthre
16 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kauditd
17 root 20 0 0 0 0 S 0.0 0.0 0:01.32 khungtaskd
18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 oom_reaper
19 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 writeback
20 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kcompactd0
21 root 25 5 0 0 0 S 0.0 0.0 0:00.00 ksmd
22 root 39 19 0 0 0 S 0.0 0.0 0:12.74 khugepaged
23 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 crypto
24 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kintegrityd
- awk命令:
awk '/PID/,/END/{if (NR>7){printf "%-8s %-8s\n",NR,$0}}' cpu
我只想要PID以下的所有信息,不包含PID行
通过/PID/,/END/查询PID行以下的所有信息,因为文本不存在END字符,所以会遍历到文件结束
通过输出NR,当前行的行号,得到PID行的行号为7,所以通过if条件判断,不输出第7行数据即可
8 9061 ubuntu 20 0 43556 4092 3312 R 6.2 0.2 0:00.01 top
9 1 root 20 0 225520 7796 5204 S 0.0 0.4 1:46.64 systemd
10 2 root 20 0 0 0 0 S 0.0 0.0 0:00.20 kthreadd
11 4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
12 6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq
13 7 root 20 0 0 0 0 S 0.0 0.0 2:09.91 ksoftirqd/0
14 8 root 20 0 0 0 0 I 0.0 0.0 21:46.89 rcu_sched
15 9 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_bh
16 10 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
17 11 root rt 0 0 0 0 S 0.0 0.0 0:06.17 watchdog/0
18 12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/0
19 13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
20 14 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
21 15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_kthre
22 16 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kauditd
23 17 root 20 0 0 0 0 S 0.0 0.0 0:01.32 khungtaskd
24 18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 oom_reaper
25 19 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 writeback
26 20 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kcompactd0
27 21 root 25 5 0 0 0 S 0.0 0.0 0:00.00 ksmd
28 22 root 39 19 0 0 0 S 0.0 0.0 0:12.74 khugepaged
29 23 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 crypto
30 24 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kintegrityd
输出每个学生的平均成绩
- student成绩表
Jerry 80 90 78 56
Tom 45 65 89 12
Allen 89 96 95 87
Li 45 89 75 63
- awk命令:
awk '{total=$2+$3+$4+$5;avg=total/4;printf "%-10s %-10 0.2f\n",$1,avg}' student
输出每位学生的平均成绩
total统计每行每个学生的所有成绩,avg求平均分,printf格式化输出,固定10个字符长度,左对齐,浮点数保留小数点后两位
Jerry 76.00
Tom 52.75
Allen 91.75
Li 68.00
- awk命令:
awk '{if($2>60) print $1,$2}' student
, 输出第一列成绩中合格的学生(大于60分)
Jerry 80
Allen 89
- awk命令:
awk -f cal.awk student
, 计算横向和纵向数据的总和
# cak.awk
BEGIN{
printf "%-10s%-10s%-10s%-10s%-10s%-10s\n","Name","Chinese","Math","Enligsh","PC","Sum"
}
{
total=$2+$3+$4+$5;
for(i=2;i<=NF;i++){
sum=array[i];
array[i]=sum+$i;
}
printf "%-10s%-10d%-10d%-10d%-10d%-10d\n",$1,$2,$3,$4,$5,total
}
END{
printf "%-10s%-10d%-10d%-10d%-10d%-10d\n","",array[2],array[3],array[4],array[5],""
}
# output
Name Chinese Math Enligsh PC Sum
Jerry 80 90 78 56 304
Tom 45 65 89 12 211
Allen 89 96 95 87 367
Li 45 89 75 63 272
259 337 218 0 0
参考资料
- 《鸟哥的Linux私房菜》
- 跟着360架构师 学习Shell脚本编程 -@作者:酷田
- 如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!