不可错过的文本处理工具:sed和awk
大家都说,grep、sed和awk是shell中的文本处理三剑客。他们的功能分别是对文本进行查找、替换和整理输出。
本文会详述awk和sed工具。
awk和sed的整体设计理念
串行、流式的使用行缓冲区来处理大文本中的数据。也就是,一行一行的处理文本数据,涉及的概念包括:
- 文件
- 行
- 列
awk和sed这些文本处理流工具,可以将输入的流执行打印、转换或者修改等操作。如下示意图:
awk
awk的主要功能是读入文本,做统计报告。
内置变量
首先了解一下awk的内置变量,可分次文件、行和列相关的:
- 文件相关的变量
FNR
:表示多文本处理时,每个文件行号单独计数,都是从1开始。FILENAME
:当前输入的文件名字。
- 行相关的变量
$0
:整行内容NR
:Number Row
的缩写,表示当前行的行号,从1开始计数。RS
--Row Separator
的缩写,表示输入行分隔符,默认回车换行\n
。
- 列相关的变量
$1-$n
: 当前行的第1-n
个字段。NF
:Number Field
的缩写,表示当前行的字段个数,也就是有多少列。FS
--Field Separator
的缩写,表示输入字段分隔符,不指定默认以空格或tab
键分割。
用法
基本用法是:awk 'BEGIN{...}pattern{...}END{...}' file_name
显示是用print/printf函数,在使用awk的时候用的比较多。
前置处理BEGIN
在处理文本流之前,一般用来初始化变量,比如设置列分隔符FS。
awk 'BEGIN{FS=":"}'
后置处理END
在处理文本流之后,比如用来打印总和
# awk 'BEGIN{total=0}{total++}END {print total}' access.log
495903
access.log 一共有49.59万行。
如何匹配
文本流的行内匹配,awk支持的包括:
- 通用的Linux正则匹配,文本匹配的格式是
/pattern/
##匹配行内有root的行
# awk -F : '/root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
##以root开头的行
operator:x:11:0:operator:/root:/sbin/nologin
# awk -F : '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
- 运算符匹配
awk -F : '$3<10{print $0}' /etc/passwd
满足$3<10,也就是第三列的值小于10,才会print。
awk的一些内置函数
还有一些算术函数、事件函数和字符串函数比如length、substr和index等,需要的时候可以查看用法: www.tutorialspoint.com/awk/awk_bui…
{}中的表达式
前置、后置和流处理块中都可以写入函数表达式,多表达式用;隔开。 变量在awk的三个块中是从BEGIN到流处理、再到END依次传递的,按顺序执行的。
if语法
示意:
awk 'BEGIN {num = 10; if (num % 2 == 0) printf "%d is even number.\n", num }'
循环语法
awk 'BEGIN {i = 1; while (i < 6) { print i; ++i } }'
另外还有,都和C语言语法差不多:
- for循环
- do while循环
- break跳出
数组
awk有关联数组, 索引不必是一组连续的数字;可以使用字符串或数字作为数组索引。此外,无需提前声明数组的大小 - 数组可以在运行时增长/缩小。直接使用数组就行,如下:
array_name[index] = value
例子:accesslog统计报告
下面看一个例子。
服务器有一份请求访问的access.log,部分请求是流量攻击,流量攻击有特殊的头部”TEST“,现在要打印流量攻击的不同http请求的统计报告。 我们可以使用awk来做,如下:
# tail access.log
"9.134.77.72" "127.0.0.1" "-" "[11/Jan/2022:21:20:16 +0800]" "/api/test1" "200" "2" "2914" "-" "Prometheus/2.17.1" "TEST"
"9.134.77.72" "127.0.0.1" "-" "[11/Jan/2022:21:20:16 +0800]" "/api/test2" "200" "2" "2914" "-" "Prometheus/2.17.1" "TEST"
...
# grep "TEST" access.log|wc -l # grep可以统计到攻击的调用次数 50万次
## 写awk处理脚本
# cat access.awk
BEGIN{
FS="\" \"";
printf "%-10s %-10s HandledTime ResponseBytes\n", "Request", "Total";
}
## 1、BEGIN设置每行日志列的分隔符是" "(\用来转译);2、打印表头
/TEST/
{
REQUEST[$5]+=1 ;
REQUEST1[$5]+=$7;# handle time ,ms
REQUEST2[$5]+=$8; # response bytes;
handledTime+=$7;
bytes+=$8;
}
## 匹配有TEST字符串的日志行,$5表示请求的路由,$7表示请求处理时间,$8表示响应的包大小,
##按照路由聚合到REQUEST、REQUEST1和REQUEST2数组,handledTime是总聚合时间,bytes总聚合包大小
END{
for(u in REQUEST)
{
printf "%-10s %-10d %-10d %-10d\n", u , REQUEST[u], REQUEST1[u], REQUEST2[u]
}
printf "handledTime: %d min\n",handledTime/1000/60;
printf "handledBytes: %d MB\n",bytes/1000/1000;
}
## 遍历数组,打印数组每一项
# awk -f access.awk access.log #执行
# #最后输出
api/test1 10000 200000 2000000
api/test2 6900 30000 200000
...
handledTime: 39 min
handledBytes: 960 MB
# #上面的内容保存到文件后,可以使用sort进行排序,找到调用最频繁的请求路由
# sort -k 2 -rn stat.txt |head -n 20
awk总结
通过例子可以看出,sed有一些Option可以配置
- -F:后面接field的分隔符
- -f: sed在命令行是通过''输入脚本,也可以-f选择脚本文件。 awk的语法并没有想的那么难,它可以匹配感兴趣的行进行流处理并输出统计报告。
sed工具
前面讲了awk的基本原理和用法,接下来讲讲同样流处理的工具sed。
sed的用法主要是用来修改文本。
sed对文本里面的每一行进行匹配,执行一些动作。用法如下:
sed [option] '/pattern/command' file
例如:
# sed '/abc/p' test.txt # 省略了option,/abc/为pattern正则匹配,p为command命令打印的意思
主要功能
sed提供了很多命令,包括p打印,d删除和s替换等。
在sed中,最常用的功能是s替换
sed的匹配
匹配是可以按照行号或者字符串\pattern\
匹配,如下图:
- 文本匹配,跟awk的意义,字符串
\pattern\
匹配,-E 也可以按照增强的正则表达式来匹配 - 行匹配,数字匹配,比如
1p
- 匹配多行,匹配以start匹配开头的,以end匹配结束的行块。
1p,10p
可以匹配1-10行。
选项
有一些比较有用的选项:
- 默认是不修改原文件,而是只将修改的内容输出到控制台。加上选项-i可以原地直接修改文本文件内容
- 默认会显示匹配并操作的行,选项-n可不显示匹配并操作的行,所以-n和p命令一般配合使用。
- 选项-e 指令命令处理,可以有多个。sed不像awk可以使用表达式,只能使用简单的命令,但是可以使用多个命令。
- 选项-f 命令从文件读
- 选项-E 可以使用增强的正则匹配模式
主要命令
p
#echo hello >>test.txt
#echo "nice to test" >>test.txt
# sed 'p' test.txt # 打印原来的行
hello
hello
nice to test
nice to test
# sed '2p' -n test.txt
nice to test
2p表示匹配第二行并打印,-n不打印原来的行。
d
# sed -i '1d' test.txt
# #d不会打印原行
# cat test.txt
nice to test
# sed '/nice/d' -i test.txt
# cat test.txt
##删完了
d不会打印原行,两次操作之后两行数据都删完了。
s
s的用法是命令放在前头,匹配和替换的字符放后面。语法为s/pattern/replace/
# sed 's/test/hello/' test.txt # 只匹配行的第一个test并替换为hello
hello
nice to hello
# sed -i 's/test/hello/' test.txt
# # -i则原地修改不会输出到控制台
#sed 's/test/hello/g' test.txt # g表示要对这一行的所有匹配进行替换
#sed 's/test/hello/2g' test.txt #
# sed 's/test/hello/1g' test.txt
# sed 's/test/hello/ig' test.txt #忽略大小写
- s/test/hello/ 只替换行内的第一个,相当于s/test/hello/1g
- s/test/hello/g 替换行内的所有test为hello
- s/test/hello/2g,替换行内的匹配到的第二个test到最后的test为hello
- s/test/hello/ig 匹配忽略大小写,TEST也可以替换
其他不太常用的
其他不太常用的功能还有很多,包括a/i/r/w,可能还有其他的一些,用到的时候可以查查手册。
总结
本文主要讲了awk和sed这两个文本处理流工具的基本原理和常见用法,包括匹配规则,主要命令和基本语法。希望能给你带来一定帮助。
转载自:https://juejin.cn/post/7051972966309560333