shell文本处理工具sed、cut、awk

2022-08-12 20:33:34 浏览数 (1)

sed

sed 编辑器逐行处理文件,并将输出结果发送到屏幕,不会修改或破坏源文件

-i会直接修改文件

语法

代码语言:javascript复制
sed [-hnV][-e<script>][-f<script文件>][文本文件] 

参数说明:

代码语言:javascript复制
-e<script>或--expression=<script> 以选项中指定的script来处理输入的文本文件。 
-f<script文件>或--file=<script文件> 以选项中指定的script文件来处理输入的文本文件。 
-n 仅显示script处理后的结果。不打印模式空间中的内容,而是仅打印和sed命令匹配的内容 
-i 直接对源文件进行修改(慎用) 

动作说明:

代码语言:javascript复制
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~ 
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行! 
d :删除,因为是删除啊,所以 d 后面通常不接任何东东; 
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行); 
p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~ 
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是 /正则/ 

测试文件

代码语言:javascript复制
[root@VM---centos ~]# cat testfile 
xujinding 
wuyicheng 
caiyufei 
wahaha 
hello world 
hello java 

删除

将 testfile 的内容列出并且列印行号,同时,请将第 2~4 行删除

代码语言:javascript复制
nl testfile | sed '2,4d' 
1 xujinding 
5 hello world 
6 hello java 

只删除第二行

代码语言:javascript复制
nl testfile | sed '2d'

删除第3行~到最后一行 $代表最后

代码语言:javascript复制
nl testfile | sed '3,$d'

插入

在第二行后(即加在第三行) 加上drink tea

代码语言:javascript复制
nl testfile | sed '2a drink tea' 
1 xujinding 
2 wuyicheng 
drink tea 
3 caiyufei 
4 wahaha 
5 hello world 
6 hello java 

代码语言:javascript复制
在第二行前(即加在第二行) 加上drink tea
nl testfile | sed '2i drink tea'

第二行后面加入两行字 使用可以一次加多行,每一行之间都必须要以反斜杠 来进行新行标记

代码语言:javascript复制
nl testfile |sed '2a drink tea or  
> drink beer' 
1 xujinding 
2 wuyicheng 
drink tea or 
drink beer 
3 caiyufei 
4 wahaha 
5 hello world 
6 hello java 

以行为单位的替换与显示

将第二行到第五行换成 xixixixixi

代码语言:javascript复制
nl testfile |sed '2,5c xixixixixi' 
1 xujinding 
xixixixixi 
6 hello java 

打印

n和p 一般是一起使用

代码语言:javascript复制
-n选项:只显示匹配处理的行(否则会输出所有)(也就是关闭默认的输出) 
-p选项:打印 
[root@centos6 ~]# vim a.txt 
[root@centos6 ~]# cat a.txt 
asdf;1324;fdsag 
1234567890 
qwer 
asdasdsadasdasdas 
[root@centos6 ~]# sed 's/1324/aaaa/' a.txt > b.txt 首先sed是有一个默认输出的,也就是将所有文件内容都输出,加上命令行中的替换,那么输出结果就是下面这样 
[root@centos6 ~]# cat b.txt 
asdf;aaaa;fdsag 
1234567890 
qwer 
asdasdsadasdasdas 
[root@centos6 ~]# sed 's/1324/aaaa/p' a.txt > b.txt 这行的意思就是:首先sed默认输出文件全部内容,然后p又将匹配到的内容打印了一遍,也就是会输出两边匹配到的内容 
[root@centos6 ~]# cat b.txt 
asdf;aaaa;fdsag 
asdf;aaaa;fdsag 
1234567890 
qwer 
asdasdsadasdasdas 
[root@centos6 ~]# sed -n 's/1324/aaaa/p' a.txt > b.txt 这行就是sed -n屏蔽默认输出然后s替换,p再将匹配到的内容打印出来,所以只显示了一行,也就是匹配到的那一行 
[root@centos6 ~]# cat b.txt 
asdf;aaaa;fdsag 
[root@centos6 ~]# sed -n 's/1324/aaaa/' a.txt > b.txt 这行就是sed -n选项屏蔽默认输出,s替换,但是没有p就不会将匹配到的内容输出 
[root@centos6 ~]# cat b.txt 
[root@centos6 ~]# 

代码语言:javascript复制
仅列出 testfile 文件内的第 5-7 行
nl testfile | sed -n '5,7p'

数据的搜寻并显示

搜索 testfile 有 hello 关键字的行:

代码语言:javascript复制
nl testfile | sed -n '/hello/p' 
5 hello world 
6 hello java 

数据的搜寻并删除

删除 testfile 所有包含 hello 的行,其他行输出

代码语言:javascript复制
nl testfile | sed '/hello/d' 
1 xujinding 
2 wuyicheng 
3 caiyufei 
4 wahaha 

数据的查找与替换

sed ‘s/要被取代的字串/新的字串/g’ g 标识符表示全局查找替换

将 testfile 文件中每行第一次出现的 hello 用字符串 heihei 替换,然后将该文件内容输出到标准输出:

代码语言:javascript复制
nl testfile | sed 's/hello/heihei/' 
1 xujinding 
2 wuyicheng 
3 caiyufei 
4 wahaha 
5 heihei world 
6 heihei java 

注意 是每行第一个 (第5行的第2个o没被替换掉)

代码语言:javascript复制
nl testfile | sed 's/o/heihei/' 
1 xujinding 
2 wuyicheng 
3 caiyufei 
4 wahaha 
5 hellheihei world 
6 hellheihei java 

sed 对文件中所有符合的字符串都被替换,修改后内容会到标准输出,不会修改原文件:

代码语言:javascript复制
sed -e 's/oo/kk/g' testfile

修改源文件(-i)

代码语言:javascript复制
sed -i 's/oo/kk/g' testfile

多点编辑(-e)

一条 sed 命令,删除 testfile 第三行到末尾的数据

代码语言:javascript复制
nl testfile | sed -e '3,$d' -e 's/HELLO/RUNOOB/'

cut

cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。

语法

代码语言:javascript复制
cut [选项参数] filename

说明:默认分隔符是制表符 参数说明

代码语言:javascript复制
-f: 列号,提取第几列 
-d: 分隔符,按照指定分隔符分割列 
-c: 指定具体的字符 

测试文件

[admin@ datas]$ vim words hello world !! hadoop spark hive 张三 李四 王五

简单实例

根据空格切割 words 第一、三列

代码语言:javascript复制
cut -d " " -f 1,3 words

根据空格切割,获取 words 第 2 行第 1 列

代码语言:javascript复制
cat words | head -n 2 | tail -n 1 | cut -d " " -f 1

选取系统 PATH 变量值
代码语言:javascript复制
[admin@ datas]$ echo $PATH 
选取系统 PATH 变量值,第 2 个“:”开始后的所有路径: 
[admin@ datas]$ echo $PATH | cut -d : -f 2- 
选取系统 PATH 变量值,第 4 列(包括第 4 列)之前的所有路径: 
[admin@ datas]$ echo $PATH | cut -d : -f -4 
选取系统 PATH 变量值,第 2 到 4 列(包括第 2 到 4 列)之间的所有路径: 
[admin@mc datas]$ echo $PATH | cut -d : -f 2-4 

切割 ifconfig 后打印的 IP 地址
代码语言:javascript复制
[admin@ ~]$ ifconfig 
获取 ip 地址 
[admin@ ~]$ ifconfig | grep "inet" | tail -n 1 | cut -d " " -f 9- | cut -d " " -f 2 

awk

一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。

语法

代码语言:javascript复制
awk [选项参数] ‘pattern1{action1} pattern2{action2}...’ filename

pattern:表示 AWK 在数据中查找的内容,就是匹配模式 action:在找到匹配内容时所执行的一系列命令

选项参数 -F: 指定输入文件折分隔符 -v(小写): 赋值一个用户定义变量(action1里面是不能引用awk外面的变量)

自定义变量(-v)

代码语言:javascript复制
awk -v x="bob" -v y=10 '{print x,y}' /tmp/hosts

自定义分隔符(-F)

代码语言:javascript复制
awk -F ':' {print $1}' words
awk -F: {print $1}' words

二维数组

代码语言:javascript复制
echo " "|awk 'BEGIN{ay[1,10]=5;ay[10,2]=12;}END{for (k in ay) {split(k,idx,SUBSEP);print idx[1],idx[2],ay[idx[1],idx[2]];}}' 
输出结果: 
1 10 5 
10 2 12 
echo " "|awk 'BEGIN{ay[1,10]=5;ay[10,2]=12;}END{for (k in ay) {print k,ay[k];}}' 
输出结果: 
110 5 
102 12 

多个分隔符

代码语言:javascript复制
[root@VM---centos ~]# vim awktest 
xu jin:ding 
wu yi:cheng 
cai yu:fei 
awk -F '[ ,:]' '{print $1,$2,$3}' awktest 
按空格和分号拆分 
xu jin ding 
wu yi cheng 
cai yu fei 

内置变量RS、OFS、ORS

RS

内置变量RS保存的是输入数据的行分隔符,默认为n,可以指定其它字符作为行分隔符

代码语言:javascript复制

awk -v RS="." '{print $1}' /tmp/hosts #指定.作为行分隔符

OFS

保存的是输出字段的分隔符(列分隔符),默认为空格

代码语言:javascript复制

awk -v OFS="-" '{print $1,$2}' /tmp/hosts #以"-"作为字段分隔符

ORS

保存的是输出记录(行)的分隔符

代码语言:javascript复制

awk -v ORS="-" '{print $1}' /tmp/hosts

print

可以输出常量和变量,如果是字符串常量需要用双引号括起来,数字常量可以直接打印

代码语言:javascript复制
awk '{print 123}' /tmp/hosts 
awk '{print "IP:",$1}' /tmp/hosts 
awk '{print "第1列:"$1,"t第2列:"$2}' /tmp/hosts 

正则

代码语言:javascript复制
awk 可以使用正则 
搜索 passwd 文件,以:分隔,输出以 a 字母开头的所有行 
[admin@ datas]$ awk -F ':' '/^a/{print $0}' passwd 
搜索 passwd 文件,以:分隔,输出以 a 字母开头的所有行的第 1 列和第 6 列,两列之间加上--字符 
[admin@ datas]$ awk -F ':' '/^a/{print $1"--"$6}' passwd 
注意:有正则的时候,只有匹配了 pattern 的行才会执行 action 
搜索 passwd 文件,输出以 a 字母开头的所有行的第 1 列和第 6 列,以--分割,且在开头第一行的上面添加一行列名“1 列”“6 列”,以--分隔,在最后一行的下面添加一行内容"这是所有的以 a 开头的行的 1、6 两列"。 
[admin@ datas]$ awk -F ':' 'BEGIN{print "1 列--6 列"} /^a/{print $1"--"$6} END {print "这是所有的以 a 开头的行的 1、6 两列"}' passwd 
显示xu第一次出现到ding第一次出现之间的行(包含xu和ding对应的行) 
awk '/xu/,/ding/ {print $1}' awktest 

BEGIN和END

BEGIN可以进行数据初始化,END可以进行数据汇总 BEGIN:读取所有数据之前执行一次(只会执行一次) END:读取完所有数据记录后执行一次(只会执行一次)

IF

代码语言:javascript复制
ps -eo user,pid,pcpu,comm | awk '{if($3>0.5) print}'
awk -F: '{if($3<1000){x  } else{y  }} END{print "系统用户个数:"x"","普通用户个数:"y""}' /etc/passwd

遍历数组

代码语言:javascript复制
awk 'BEGIN{  
> a[0]=1;a[11]=22;a["book"]=32;a["work"]="home";  
> for(i in a){print i,a[i]}  
> }' 

因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的

for循环

代码语言:javascript复制
for(表达式;表达式;表达式) { 
动作指令序列 
} 
awk 'BEGIN{ for (i=1;i<=4;i  ) {print i}}' 
awk -F: '{  
for(i=1;i<=NF;i  )  
> {if($i=="root") x  }  
> } END {print x}' /etc/passwd 

while循环

代码语言:javascript复制
while(条件判断){ 
动作指令序列; 
} 
awk 'BEGIN{ i=1; while(i<=5) {print i;i  }}' 

常用命令

打印各磁盘可用大小
代码语言:javascript复制
df | grep -v tmpfs | awk 'NR!=1 {disk[$1]=$4}  
END {for(i in disk) {printf "%-20s %-10sn",i,disk[i]/1024"M"} 
}' 

统计磁盘可用容量

代码语言:javascript复制
df | tail -n   | grep -v tmpfs | awk '{sum =$4} END{print "磁盘可用容量:"sum/1024/1024"G"}' 

统计/etc下文件总大小
代码语言:javascript复制
ls -l /etc | awk '/^-/{sum =$5} END{print "文件总大小:"sum/1024"M"}' 

统计访问Nginx的各IP访问次数
代码语言:javascript复制
awk '  
{IP[$1]  }  
END {  
for (i in IP) {print i,IP[i]}  
}' /var/log/nginx/access.log 

查看Nginx 1点到5点半的日志
代码语言:javascript复制
awk -F"[: /]" '$7":"$8 >= "01:00" && $7":"$8 <="05:30"' /var/log/nginx/access.log 

根据学号汇总查询每个学生的总成绩
代码语言:javascript复制
[admin@ datas]$ vim grade.txt 
#学号 科目 成绩 
1 语文 90 
1 数学 40 
1 英语 59 
2 语文 95 
2 数学 80 
2 英语 52 
3 语文 89 
4 数学 29 
(1)使用 awk 命令行,根据学号汇总查询每个学生的总成绩 
[admin@ datas]$ cat grade.txt | grep -v "#" | awk '{arr[$1] =$3} END{for(i in arr){printf("学号为%d 的学生的成绩是:%dn",i,arr[i])}}' 

统计每个接口的访问次数及平均时间
代码语言:javascript复制
//查询接口的访问次数 
less -r xxx | awk -F '|' '{API[$4]  } END{ for(i in API) {print i,API[i]}}' 
//查询接口的访问次数并排序 
cat product-service-action.2022-06-02_16.log | awk -F '|' '{API[$4]  } END{ for(i in API) {print i,API[i] | "sort -r -n -k2"}}' 
//查接口的访问次数。并统计每个接口的总时间 
cat product-service-action.2022-06-02_16.log | awk -F '|' '{API[$4]  ;TIME[$4]=int(TIME[$4])   int($5)} END{ for(i in API) {print i,API[i],TIME[i]}}' 
//查接口的访问次数。并统计每个接口的平均响应时间 
cat product-service-action.2022-06-02_16.log | awk -F '|' '{API[$4]  ;TIME[$4]=int(TIME[$4])   int($5)} END{ for(i in API) {print i,API[i],TIME[i],int(TIME[i] / API[i]) | "sort -r -n -k4" }}' 
//查2s以上接口的访问次数。并统计每个接口的平均响应时间 
cat product-service-action.2022-06-02_16.log | awk -F '|' '{if(int($5)>2000){ API[$4]  ;TIME[$4]=int(TIME[$4])   int($5)}} END{ for(i in API) {print i,int(TIME[i] / API[i])}}' 

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=23endckh44e8w

0 人点赞