一、SED流式编辑器
1、sed语法
sed是流式编辑器,处理文件内容时逐行处理
sed [选项] '[定位符]指令' 文件
[root@SHELL ~]# sed '' /etc/resolv.conf #打印文件所有的行,由sed默认的输出功能打印
[root@SHELL ~]# sed -n 'p' /etc/resolv.conf #打印文件所有行,—n屏蔽了默认数据,由p指令实现打印
2、sed的定位符
行号定位
[root@SHELL ~]# sed -n '3p' /etc/passwd #打印第3行
[root@SHELL ~]# sed -n '1,3p' /etc/passwd #打印1到3行
[root@SHELL ~]# sed -n '1~2p' /etc/passwd #打印从第1行开始,步长为2的行,奇数行
[root@SHELL ~]# sed -n '2~2p' /etc/passwd #打印从第2行开始,步长为2的行,偶数行
[root@SHELL ~]# sed -n '2,+3p' /etc/passwd #第2行及其后边的3行
[root@SHELL ~]# sed -n '1p;4p;8p' /etc/passwd #打印第1行、第4行、第8行
正则定位(sed默认使用标准正则)
[root@SHELL ~]# cat /etc/passwd | grep "^root"
[root@SHELL ~]# sed -n '/^root/p' /etc/passwd #过滤root开头的行
[root@SHELL ~]# cat /etc/passwd | grep "[0-9]\{3\}"
[root@SHELL ~]# cat /etc/passwd | sed -n '/[0-9]\{3\}/p' #过滤包含3个数字的行
3、sed指令
##处理级别:行
#p指令打印
[root@SHELL ~]# sed -n '/IPADDR/p' /etc/sysconfig/network-scripts/ifcfg-eth0 #打印符合条件指定行
[root@SHELL ~]# free -m | sed -n '/Mem/p' #打印符合条件指定行
[root@SHELL ~]# df -h | sed -n '/\/$/p' #打印符合条件指定行
[root@SHELL ~]# sed -n '/localhost/!p' /etc/hosts #打印不包括localhost的行
[root@SHELL ~]# sed -n '2!p' /etc/hosts #打印不是第2行的行
#d指令删除
[root@SHELL ~]# cp /etc/fstab /tmp/
[root@SHELL ~]# ls /tmp/fstab
[root@SHELL ~]# sed 'd' /tmp/fstab #把该默认打印的行删除,删除所有
[root@SHELL ~]# sed '1d' /tmp/fstab #删除指定行
[root@SHELL ~]# sed '/dev/d' /tmp/fstab #删除带dev的行
[root@SHELL ~]# sed '/dev/!d' /tmp/fstab #删除不带dev的行
[root@SHELL ~]# sed '/^#/d' /tmp/fstab #删除#开头行(注释行)
[root@SHELL ~]# sed '/^$/d' /tmp/fstab #删除空行
#c指令替换整行
[root@SHELL ~]# sed 'c123456' /tmp/fstab
[root@SHELL ~]# sed '/IPADDR/cIPADDR=1.1.1.1' /etc/sysconfig/network-scripts/ifcfg-eth0
[root@SHELL ~]# sed '4cxxxxxx' /etc/shells
#s指令替换关键词
[root@SHELL day04]# cat test.txt
2046 2048 2046 2046
1001 2046 2999 1888
2046 2046 2046 2046
[root@SHELL day04]# sed 's/2046/XXXX/' test.txt #替换每行第1个
XXXX 2048 2046 2046
1001 XXXX 2999 1888
XXXX 2046 2046 2046
[root@SHELL day04]# sed 's/2046/XXXX/g' test.txt #替换每行所有
XXXX 2048 XXXX XXXX
1001 XXXX 2999 1888
XXXX XXXX XXXX XXXX
[root@SHELL day04]# sed 's/2046/XXXX/2' test.txt #替换每行指定第几个匹配结果替换
2046 2048 XXXX 2046
1001 2046 2999 1888
2046 XXXX 2046 2046
[root@SHELL day04]# sed 's/2046/(&)/g' test.txt #&代替旧的字符
(2046) 2048 (2046) (2046)
1001 (2046) 2999 1888
(2046) (2046) (2046) (2046)
[root@SHELL day04]# sed '2s/2046/XXXX/g' test.txt #指定替换第2行
2046 2048 2046 2046
1001 XXXX 2999 1888
2046 2046 2046 2046
[root@SHELL day04]# sed '2s/2046//g' test.txt #替换为空
2046 2048 2046 2046
1001 2999 1888
2046 2046 2046 2046
[root@SHELL day04]# sed -n '2s/2046//gp' test.txt #指定行替换为空
1001 2999 1888
[root@SHELL day04]# sed 's.2046.XXXX.' test.txt #s///中/可以写为任意字符
[root@SHELL day04]# sed 's2\20462\20\2\22g' test.txt
s/2046/2022/
sa2046a2022a
s2 \2046 2 \20\2\2 2
[root@SHELL day04]# echo "hello world" | sed -rn 's/^(.)(.*)(.)$/\3\2\1/p'
dello worlh
#=等于号用于打印行号
[root@SHELL day04]# sed -n '/root/=' /etc/passwd #匹配带root的行,打印行号
1
10
[root@SHELL day04]# sed -n '/bash$/=' /etc/passwd #匹配bash结尾的行,打印行号
[root@SHELL day04]# sed -n '$=' /etc/passwd #打印最后一行行号,即行数
#i行前写入
[root@SHELL day04]# sed 'iabc_xyz' test.txt #所有行前加入指定内容
abc_xyz
2046 2048 2046 2046
abc_xyz
1001 2046 2999 1888
abc_xyz
2046 2046 2046 2046
[root@SHELL day04]# sed '2iabc_xyz' test.txt #指定行号前加入指定内容
2046 2048 2046 2046
abc_xyz
1001 2046 2999 1888
2046 2046 2046 2046
[root@SHELL day04]# sed '/2046/i abc_xyz' test.txt #正则匹配行前写入
abc_xyz
2046 2048 2046 2046
abc_xyz
1001 2046 2999 1888
abc_xyz
2046 2046 2046 2046
#a指令行后写入
[root@SHELL day04]# sed 'aabc_xyz' test.txt #所有行后写入
2046 2048 2046 2046
abc_xyz
1001 2046 2999 1888
abc_xyz
2046 2046 2046 2046
abc_xyz
[root@SHELL day04]# sed '2aabc_xyz' test.txt #指定行后写入
2046 2048 2046 2046
1001 2046 2999 1888
abc_xyz
2046 2046 2046 2046
[root@SHELL day04]# sed '/2046/aabc_xyz' test.txt #匹配行后写入
2046 2048 2046 2046
abc_xyz
1001 2046 2999 1888
abc_xyz
2046 2046 2046 2046
abc_xyz
[root@SHELL day04]#
#r从文件读入
[root@SHELL day04]# sed 'r /etc/hosts' test.txt #读入文件内容,所有行后
[root@SHELL day04]# sed '2r /etc/hosts' test.txt #读入文件内容,指定行后
[root@SHELL day04]# sed '/1888/r /etc/hosts' test.txt #读入文件内容,匹配行后
#w另存为
[root@SHELL day04]# sed 'w copy_test.txt' test.txt #所有行另存为
[root@SHELL day04]# cat copy_test.txt
[root@SHELL day04]# sed '2w test2.txt' test.txt #指定行另存为
[root@SHELL day04]# cat test2.txt
[root@SHELL day04]# sed '/1888/w 1888.txt' test.txt #匹配行另存为
[root@SHELL day04]# cat 1888.txt
爬取图片案例
[root@SHELL day04]# cat spider.sh #!/bin/bash curl -s "https://www.tmooc.cn/" > index.html echo "开始处理文件内容" sed -ri '/<img/!d' index.html sed -ri 's/.*src="//' index.html sed -ri '/<img id/d' index.html sed -ri 's/".*//' index.html sed -ri '/^$/d' index.html if [ -e "./pic" ];then echo "pic目录已存在" else mkdir ./pic echo "创建pic目录" fi echo "下载图片" for i in $(cat index.html) do wget -P ./pic -q $i done rm -rf index.html [root@SHELL day04]#
二、awk工具
1、awk基础语法
用于获取整行数据中的部分数据(获取列)
语法:命令执行结果 | awk [选项] '[条件]{指令}'
[root@SHELL ~]# df -hT | grep "/$"
/dev/sda1 xfs 20G 1.9G 19G 10% /
[root@SHELL ~]# df -hT | grep "/$" | tr -s " " | cut -d" " -f5
19G
[root@SHELL ~]# df -hT | grep "/$" | awk '{print $5}'
#语法:awk [选项] '[条件]{指令}' 文件
[root@SHELL ~]# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
2、awk内置变量
[root@SHELL ~]# awk -F: '{print NR,NF}' /etc/passwd
[root@SHELL ~]# awk -F: '{print $NF}' /etc/hosts
[root@SHELL ~]# awk -F: '{print "用户名是:"$1",""解释器是:"$7}' /etc/passwd
3、awk流程控制语法(时机)
语法 awk 'BEGIN{} {} END{}' 文件
awk 'BEGIN{处理文件前做的事} {处理文件时做的事} {处理文件后做的事}' 文件
awk '处理前 处理时 处理后' 文件
[root@SHELL ~]# awk 'BEGIN{a=12;print a+23}'
35
[root@SHELL ~]# awk 'BEGIN{print NR} END{print NR}' /etc/passwd
0
27
[root@SHELL ~]# awk -F: 'BEGIN{print "用户名","\t","解释器"} {print $1,"\t",$7}' /etc/passwd
[root@SHELL ~]# awk -F: 'BEGIN{x=0} /bash$/{x++} END{print x}' /etc/passwd
4、awk基础案列
#内存信息
[root@SHELL ~]# free -m | awk '{print $7}'
735
[root@SHELL ~]# free -m | grep "Mem" | awk '{print $7}'
734
[root@SHELL ~]# free -m | awk /Mem/'{print $7}'
737
[root@SHELL ~]#
#硬盘信息
[root@SHELL ~]# df | awk '/\/$/''{print $4}'
18979752
[root@SHELL ~]# df | awk '/\/$/{print $4}'
18979752
[root@SHELL ~]#
#CPU信息
[root@SHELL ~]# LANG=C lscpu | grep "Model name" | awk -F: '{print $2}'
12th Gen Intel(R) Core(TM) i7-1260P
[root@SHELL ~]# LANG=C lscpu | awk -F: '/Model name/{print $2}'
12th Gen Intel(R) Core(TM) i7-1260P
[root@SHELL ~]# LANG=C lscpu | awk -F: '/Model name/{print $2}' | sed -rn 's/(\s*)(.*)/\2/p'
12th Gen Intel(R) Core(TM) i7-1260P
[root@SHELL ~]#
[root@SHELL ~]# uptime
01:12:33 up 2:38, 1 user, load average: 0.00, 0.01, 0.02
[root@SHELL ~]# uptime | awk -F, '{print $5}'
0.02
[root@SHELL ~]# uptime | awk '{print NF}'
10
[root@SHELL ~]# uptime | awk '{print $NF}'
0.02
[root@SHELL ~]#
5、awk条件判断
awk [选项] '[条件]{指令}' 文件
#正则
匹配:[字段] ~ /正则/
不匹配:[字段] !~ /正则/
[root@SHELL ~]# awk -F: '/^ro/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@SHELL ~]# awk -F: '$1 ~ /^ro/{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@SHELL ~]# awk -F: '$7 !~ /bash$/{print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
#数字、字符串比较
==、!=、>、>=、<、<=
[root@SHELL ~]# awk -F: 'NR==2{print $0}' /etc/passwd #打印行号是2的行
bin:x:1:1:bin:/bin:/sbin/nologin
[root@SHELL ~]# sed -n '2p' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
[root@SHELL ~]# awk -F: '$7 != "/bin/bash"{print $0}' /etc/passwd #打印解释器不是/bin/bash的
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@SHELL ~]# awk -F: '$3>=1000{print $1}' /etc/passwd #打印uid大于1000的用户名
tom
jim
#逻辑组合
&&、||
[root@SHELL ~]# awk -F: '$3==0 || $3==7{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
halt:x:7:0:halt:/sbin:/sbin/halt
[root@SHELL ~]# awk -F: '$3>=0 && $3<=3{print $0}' /etc/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
[root@SHELL ~]#
#运算符
+-*/% ++ -- += -= *= /=
[root@SHELL ~]# awk -F: 'NR%2==1{print $0}' /etc/passwd
[root@SHELL ~]# seq 200 | awk 'BEGIN{i=0} {i++} END{print i}'
200
[root@SHELL ~]# seq 200 | awk 'BEGIN{i=0} $0%3==0{i++} END{print i}'
66
[root@SHELL ~]# seq 200 | awk 'BEGIN{i=0} $0%3==0 && $0%13==0{i++} END{print i}'
5
[root@SHELL ~]#
三、awk流程控制
1、awk if判断
awk '{ if(判断条件){动作} }' 文件
awk '{ if(判断条件){条件成功动作} else{条件失败动作} }' 文件
awk '{if(){}else if(){}..else{}}' 文件
#将条件判断改写为if语句
[root@SHELL ~]# awk -F: 'BEGIN{i=0} $3>=1000{i++} END{print i}' /etc/passwd
7
[root@SHELL ~]# awk -F: 'BEGIN{i=0} {if($3>=1000){i++}} END{print i}' /etc/passwd
7
[root@SHELL ~]#
[root@SHELL ~]# awk -F: '$1=="root"{print $1,$3}' /etc/passwd #打印root开头第1/3列
root 0
[root@SHELL ~]# awk -F: '{if($1=="root"){print $1,$3}}' /etc/passwd
root 0
[root@SHELL ~]# uptime | awk '$NF>=0.01{print $NF}'
0.01
[root@SHELL ~]# uptime | awk '$NF>=0.01{print "系统十五分钟平均负载:"$NF}' #NF当前行最后1列
系统十五分钟平均负载:0.01
[root@SHELL ~]# uptime | awk '{if($NF>=0.01){print "系统十五分钟平均负载:"$NF}}'
系统十五分钟平均负载:0.01
[root@SHELL ~]#
[root@SHELL ~]# awk -F: 'BEGIN{i=0;j=0} $3<1000{i++} $3>=1000{j++} END{print "系统 用户数量为:"i,"\n普通用户个数为:",j}' /etc/passwd
系统用户数量为:20
普通用户个数为: 7
[root@SHELL ~]# awk -F: 'BEGIN{i=0;j=0} {if($3<1000){i++}else{j++}} END{print "系统用户数量为:i,"\n普普通用户个数为:",j}' /etc/passwd
系统用户数量为:20
普通用户个数为: 7
#双分支案例
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0} /^-/{i++} END{print i}'
93
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0} $1~/^-/{i++} END{print i}'
93
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0} $1~/^-/{i++} $1!~/^-/{j++} END{print i,j}'
93 89
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0} $1~/^-/{i++} $1!~/^-/{j++} END{print "文件数量为:"i,"非文件数量为:"j}'
文件数量为:93 非文件数量为:89
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0} $1~/^-/{i++} $1!~/^-/{j++} END{print "文件数量为:"i,"\n非文件数量为:"j}'
文件数量为:93
非文件数量为:89
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0} {if($1~/^-/){i++}else{j++}} END{print "文件数量为:"i,"\n非文件数量为:"j}'
文件数量为:93
非文件数量为:89
[root@SHELL ~]#
#多分支
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0;k=0} /^-/{i++} /^d/{j++} /!^[-d]/{k++}END{print i,j,k}'
93 72 0
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0;k=0} /^-/{i++} /^d/{j++} !/^[-d]/{k++}END{print i,j,k}'
93 72 17
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0;k=0} $1~/^-/{i++} $1~/^d/{j++} $1!~/^[-d]/{k++} END{print i,j,k}'
93 72 17
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0;k=0} {if($1~/^-/){i++}else if($1~/^d/){j++}else{k++}} END{print i,j,k}'
93 72 17
[root@SHELL ~]# ll /etc/ | awk 'BEGIN{i=0;j=0;k=0} {if($1~/^-/){i++}else if($1~/^d/){j++}else{k++}} END{print "文件数量为:"i,"\n目录数量为:"j,"\n其他类型文件数量为:"k}'
文件数量为:93
目录数量为:72
其他类型文件数量为:17
[root@SHELL ~]#
2、awk for循环
awk '{for(exp1;exp2;exp3){指令}}' 文件
[root@SHELL ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i}}'
1
2
3
4
5
[root@SHELL ~]# awk 'BEGIN{for(i=5;i>=1;i--){print i}}'
5
4
3
2
1
[root@SHELL ~]#
四、awk数组
awk 'BEGIN{初始化数组} {数组[下标]++} END{for(下标 in 数组){print 下标,数组[下标]}}' 文件
格式记忆法:[a:1,b:2,c:3],不是真的这样,这个格式仅用于记忆数据结构
[root@SHELL ~]# awk 'BEGIN{name[0]="tom";name[1]="bob";print name[0],name[1]}'
tom bob
[root@SHELL ~]# awk 'BEGIN{name[0]="tom";name[1]="bob";print name[0];print name[1]}'
tom
bob
[root@SHELL ~]# awk 'BEGIN{age["tom"]=22;age["bob"]=18;print age["tom"],age["bob"]}'
22 18
[root@SHELL ~]# awk 'BEGIN{x[0]=0;x[1]=1;x[2]=2; for(i in x){print i,x[i]}}'
0 0
1 1
2 2
[root@SHELL ~]# awk 'BEGIN{age["tom"]=22;age["bob"]=18;age["john"]=35;for(i in age){print i,age[i]}}' #age=(tom:22 bob:18 john:35)
john 35
tom 22
bob 18
[root@SHELL ~]#
#awk数组应用
[root@SHELL ~]# scp 192.168.4.250:/var/log/httpd/access_log /root/shell/day05/
#初始化数组,所有IP地址在处理前都出现了0次
[root@SHELL ~]# awk 'BEGIN{print "IP地址\t次数";IP[$1]=0} {IP[$1]=IP[$1]+1} END{for(ip in IP){print ip"\t"IP[ip]}}' /root/shell/day05/access_log
[root@SHELL ~]# awk 'BEGIN{print "IP地址\t次数"} {IP[$1]++} END{for(ip in IP){print ip"\t"IP[ip]}}' /root/shell/day05/access_log
IP地址 次数
192.168.8.154 33
192.168.8.155 60
192.168.8.156 30
192.168.8.170 3
192.168.8.157 68
[root@SHELL ~]# awk 'BEGIN{print "IP地址\t次数"} {IP[$1]=IP[$1]+1} END{for(ip in IP){print ip"\t"IP[ip]}}' /root/shell/day05/access_log
##格式:
awk 'BEGIN{print 表头} {A[$n]++} {for(i in A){print i,A[i]}}' file
[root@SHELL ~]# awk 'BEGIN{print "资源\t次数"} {A[$7]++} {for(i in A){print i,A[i]}}' /root/shell/day05/access_log
[root@SHELL ~]# who | awk 'BEGIN{print "用户名\t次数"} {A[$1]++} {for(i in A){print i,A[i]}}'
用户名 次数
root 1
root 2
[root@SHELL ~]#
五、awk变量
[root@SHELL ~]# a=1
[root@SHELL ~]# echo $a
1
[root@SHELL ~]# awk 'BEGIN{print a}'
[root@SHELL ~]# awk 'BEGIN{print $a}'
[root@SHELL ~]# awk "BEGIN{print $a}"
1
[root@SHELL ~]# awk -v tmp=$a 'BEGIN{print tmp}'
1
[root@SHELL ~]# who | awk '{print $1}'
root
root
[root@SHELL ~]# who | awk "{print $a}"
1
1
[root@SHELL ~]# who | awk -v tmp=$a '{print $tmp}'
root
root
[root@SHELL ~]# who | awk "{print \$$a}"
root
root
[root@SHELL ~]#