shell那点事儿——运维工程师必会正则表达式及文本处理三剑客 |
发表者:admin分类:Shell脚本2021-12-09 15:31:02 阅读[951] |
正则表达式及文本处理三剑客
前言
玩转Linux操作系统必须会shell,会shell必须知道正则表达式及grep、sed、awk文本处理三剑客。好多年前有个大佬告诉我,不知道正则不要说自己会Linux,为此我专门学习这部分内容,这篇文章是对我学习内容的整理,也是我经常查阅的一篇笔记,也希望他可以帮助到大家。共同进步,瑞思拜~
一、正则表达式
基本正则表达式元字符:
元字符 功能 示例 ^ 行首定位符 ^love grep '^root' /etc/passwd $ 行尾定位符 love$ grep 'root$' /etc/passwd. 匹配单个字符 l..e grep '^r..t' /etc/passwd - 匹配0个或多个前导字符 ab*love grep 'ro*t' /etc/passwd .* 任意多个字符 grep 'r.*ot' /etc/passwd # r开头ot结尾[] 匹配指定范围内的一个字符 [lL]ove grep '[rx]oot' /etc/passwd[ - ] 匹配指定范围内的一个字符 [a-z0-9]ove grep '[a-z0-9]oot' /etc/passwd[^] 匹配不在指定组内的字符 [^a-z0-9]ove grep '[^a-z0-9]oot' /etc/passwd ^[^] 括号外尖号是指匹配,匹配括号内的任意一个字符 括号内尖号是指取反,匹配不在括号内的任意字符\ 用来转义元字符 love\. 匹配love. \< 词首定位符 \<love 定位单词首\> 词尾定位符 love\> 定位单词尾\(..\) 匹配稍后使用的字符的标签 :% s/172.16.130.1/172.16.130.5/ :% s/\(172.16.130.\)1/\15/ :% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/ :3,9 s/\(.*\)/#\1/ x\{ m\} 字符x重复出现m次 lo\{ 5\}ve x\{ m,\} 字符x重复出现m次以上 lo\{ 5,\}ve x\{ m,n\} 字符x重复出现m到n次 lo\{ 5,10\}ve
扩展正则表达式元字符(需要egrep或者转义)
元字符 功能 示例 + 匹配一个或多个前导字符 [a-z]+ove ? 匹配零个或一个前导字符 lo?ve a|b 匹配a或b love|hate() 组字符 loveable|rs love(able|rs) ov+ (ov)+(..)(..)\1\2 标签匹配字符 (love)able\1er x{ m} 字符x重复m次 lo{ 5}ve x{ m,} 字符x重复至少m次 lo{ 5,}ve x{ m,n} 字符x重复m到n次 lo{ 5,10}ve
二、文本处理三剑客
grep
grep使用的元字符:
grep: 使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{ \}, \+, \|egrep(grep -E): 使用扩展元字符集 ?, +, { }, |, ( )grep也可以使用扩展集中的元字符,仅需要对这些元字符前置一个反斜线(转义)\w 所有字母与数字,称为字符[a-zA-Z0-9] 'l[a-zA-Z0-9]*ve' 'l\w*ve'\W 所有字母与数字之外的字符,称为非字符 'love[^a-zA-Z0-9]+' 'love\W+'\b 词边界 '\<love\>' '\blove\b' grep不支持\d、\D、\s、\S,\d匹配数字,\D匹配非数字,\s匹配空白,\S匹配非空白
grep常用选项:
-i --ignore-case # 忽略大小写-v --invert-match # 反向查找,只显示不匹配的行-n --line-number # 输出的同时打印行号-e --regexp=PATTERNgrep -e "a" -e "b" file # 同时过滤多个条件 -f --file=FILEgrep -f a b # 输出b文件中与a相同的行-c --countgrep -c "1" a # 输出a文件中匹配到1的行数-o --only-matchingifconfig |grep -E -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" # 打印所有ip
sed
sed使用的元字符
使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{ \}使用扩展元字符集 ?, +, { }, |, ( )
sed选项
sed -r 打印结果,没有真正执行sed -i 真正执行sed -n 静默输出(只打印处理过的行)
sed常用用法
sed -n '10,20p' 只显示匹配到的行sed -r 's/root/alice/gi' /etc/passwd 忽略大小写且全局替换root>alicesed -r 's#root#alice#gi' /etc/passwd 替换 不需要定义分隔符 直接使用sed -r '\#root#d' /etc/passwd 查找 要定义分隔符sed -r '/^[ \t]*#/d' passwd 删除`#`开头的行sed -r '/^[ \t]*$/d' passwd 删除空行 sed -r '/^[ \t]*#/d; /^[ \t]*$/d' passwd 删除注释行和空行 ;号隔开sed -r '3,$ s/^#*/#/' passwd 将行首零个或多个#换成一个#sed -r '30,50 s/^[ \t]*#*/#/' passwd 将行首带有空格的零个#或多个#换成一个# 全部注释sed ':label;N;s/\n/ /;b label' 回车符转行为空格
sed定址
sed -r 'd' passwd 全部删除sed -r '3d' /etc/passwd 删除第3行sed -r '1,3d' /etc/passwd 删除1-3行sed -r '/root/d' /etc/passwd 删除带root的行sed -r 's/root/alice/g' /etc/passwd 全局替换root为alicesed -r '/^adm/,20d' /etc/passwd 从adm开头的行删除到第20行sed -r '/^adm/,+20d' /etc/passwd 删除adm开头的行再删除其行后20行
sed常用参数
d 删除行 s 用一个字符串替换另一个 s 替换标志 g 全局替换 i 忽略大小写sed -r 's/(.*)/#\1/' passwdsed -r 's/(.*)/#&/' passwd &代表在查找串中匹配到的内容sed -r 's/^/#/' passwd 全部注释sed -r 's/(.)(.)(.*)/\1oooo\2\3/' passwd 在第二个字母前添加内容 oooosed -r 's/(192)(.)/\1\2xx\2/' a.file 192.xx.168.1.1 10.19.200.200sed -n "s/192/xx/p" a 替换并打印 xx.168.1.1 10.19.200.200 r 读取文件 sed -r '2r 1.txt' a.txt 把a.txt的前2行放到1.txt最前面 w 写入文件 sed -r '3,$w 1.txt' a.txt 把a.txt的3行到最后一行保存到1.txt aic 插入 sed -r '2a 1111111111111' 1.txt 在第二行后面追加111111111111 sed -r '2i 1111111111111' 1.txt 在第二行前面插入一行1111111111111 sed -r '2c 1111111111111' 1.txt 第二行替换成1111111111 sed -r '/^192/a abc123' a 在以192开头的行后面追加abc123 192.168.1.1 10.19.200.200 abc123 192.168.1.1 10.19.200.200 abc123
sed空间操作
n 读取下一行到模式空间,如果没有下一行则执行 q 退出 N 追加下一行内容到模式空间,并以换行符\n分割 q 退出 p 打印模式空间所有内容 P 打印模式空间第一行 seq 6 |sed -n 'n;p' 打印偶数行 seq 6 |sed 'N;q' 打印1\n2 d 删除模式空间所有内容 D 删除模式空间第一行,以`\n`分割,放弃之后的命令,但是对剩余模式空间重新执行sed seq 6 |sed -n 'N;P' 打印奇数行 seq 6 |sed 'N;D' 打印最后一行 读取1,执行N,模式空间为1\n2,执行D,删除1剩余2,执行N,模式空间为2\n3,执行D,删除2剩余3,依此类推,得出5,执行N,条件失败退出。 h/H 模式空间**覆盖/追加**到暂存空间 g/G 暂存空间**覆盖/追加**到模式空间 sed -r '1h;$G' passwd 把第1行复制到最后一行 sed -r '1{h;d};$G' passwd 把第1行剪切到最后一行 sed -r '1h; 2,$g' passwd 把其它行全部替换成第1行 sed -r '1h; 2,3H; $G' passwd 把前3行复制到最后三行 sed -r '1!G;h;$!d' 1 倒序!!!!! 读取第一行 1 时,跳过 G 命令,执行 h 命令将模式空间 1 复制到保持空间,执行 d 命令删除模式空 间的 1。 读取第二行 2 时,模式空间是 2,执行 G 命令,将保持空间 1 追加到模式空间,此时模式空间是2\n1,执行 h 命令将 2\n1 覆盖到保持空间,d 删除模式空间。 以此类推,读到第 5 行时,模式空间是 5,执行 G 命令,将保持空间的 4\n3\n2\n1 追加模式空间, 然后复制到模式空间,5\n4\n3\n2\n1,不执行 d,模式空间保留,输出。 x 暂存空间和模式空间替换 sed -r '1h;4x;$G' passwd 把第4行替换成第1行并把第4行追加到最后一行 sed ':t;N;s/\n/,/;b t' 将换行符换成逗号
sed命令区分制表符和空格
sed -n l tab_space.txt this is tab\tfinish.$ this is several space finish.$
awk
awk内部变量:
$0: 保存当前记录的内容 awk -F: '{print $0}' passwdNF: 显示每一行的字段数 awk -F: '{print NR,$0,NF}' passwdNR: 显示总共行号 awk -F: '{print NR,$0}' passwd passwd1 FNR: 分别显示每个文件行号 awk -F: '{print FNR,$0}' passwd passwd1 FS: 输入字段分隔符 默认空格 awk -F"[ :\t]" '{print $1,$2,$3}' passwdOFS: 输出字段分隔符 awk 'BEGIN{FS=":";OFS="--"} {print $1,$2,$3}' passwdRS: 输入记录分隔符 默认换行 awk 'BEGIN{RS="/"} {print $0}' passwd # 以"/"和空格分成多行ORS: 输出记录分隔符 awk -F: 'BEGIN{ORS=" "} {print $0}' passwd # 每一行以空格分开 默认为回车
格式化输出:
awk -F: '{print "username is: " $1 "\t uid is: " $3}' passwd # 变量以外的其他所有字符串要用引号引起来tail /etc/services |awk 'BEGIN{print "Service\t\tPort\t\t\tDescription\n==="} {print $0}'# Service Port Description 3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service isnetserv 48128/tcp # Image Systems Network Servicesawk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' passwd %s 字符类型 %d 数值类型 %f 浮点类型 %.2f 保留两位小数点 占15字符 - 表示左对齐,默认是右对齐 printf默认不会在行尾自动换行,加\n
awk模式:
-
正则表达式
(1) 匹配一整行
awk '/^root/' passwdawk '!/^root/' passwd
(2) 匹配字段
awk -F":" '$1 ~/root/' passwdawk -F":" '$1 !~/bin/' passwd
-
比较表达式
awk -F":" '$3==7' passwdawk -F":" '$NF=="/sbin/nologin"' passwdawk -F":" '$NF ~"/nologin"' passwd
-
条件表达式
awk -F":" '{if($3>5){print $1,$3} else{print $1}}' passwd
-
算术运算:+ - * / %(模) (幂23)
awk -F":" '{if($3*1>5){print $1,$3}}' passwd
-
逻辑操作符
&& 逻辑与 a&&b || 逻辑或 a||b ! 逻辑非 !a
awk -F":" '$1 ~/^ro/ && $1 ~/ooot$/' passwdawk -F":" '$1 ~/^ro/ || $1 ~/^bin/' passwdawk -F":" '!($1 ~/^ro/ || $1 ~/^bin/)' passwdawk -F":" '!/^#|^$/' passwd
-
布尔值判断
seq 6 |awk 'i=!i' # 打印奇数行seq 6 |awk '!(i=!i)' # 打印偶数行i值未被定义所以为假,!i就为真,所以i为真,打印1 第二次,i已经为真,!i为假,所以i又为假,不打印2 ,依次循环
-
三目运算
awk 'BEGIN{print 1==1?"yes":"no"}' # 格式 条件?"真":"假"yesseq 4 |awk '{printf NR%2!=0?$0" ":$0" \n"}' # 一行拆分两行1 23 4
-
控制输出
awk '/^root/{next}{print $0}' file # 不打印root开头的行seq 5 |awk 'NR!=1{print $0}' # 不打印第一行tail -n5 /etc/services |awk '{print $2 > "a.txt"}' # 打印结果到一个文件tail -n5 /etc/services |awk '{print $2 |"grep tcp"}' # 过滤结果awk 'NR%2{printf $0",";next}{print $0}' # 两行合并为一行 ","分隔
awk脚本:
-
条件判断
awk -F: '{if($3>0 && $3<1000){i++}} END{print i}' passwdawk -F: '{if($3==0){i++} else{j++}} END{print "管理员个数: "i; print "系统用户数: "j}' passwdawk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员: "i; print "普通用户: "k; print "系统用户: "j}' passwd
-
循环
awk -F: '{i=1;while(i<=5) {print $1;i++}}' passwdawk -F":" '{i=1; while(i<=NF){print $i; i++}}' passwd # 分别打印每一行的每一列awk -F":" '/^root/{ for(i=1;i<=NF;i++) {print $i} }' passwd # 分别打印每一行的每一列
-
数组
awk -F: '{username[i++]=$1} END{print username[0]}' passwd
按索引遍历:
awk -F: '{username[$1]++} END{for(i in username) {print i,username[i]} }' passwd # 用户数量统计awk -F":" '{shells[$NF]++} END{for(i in shells){print i,shells[i]}}' passwd # shell类型的数量统计ss -antp |grep :22 |awk -F":" '!/LISTEN/{ip[$2]++} END {for (i in ip) {print i,ip[i]}}' |sort -rn -k3 |head |awk '{print $2,$3}' # 统计每个ip ssh连接的次数awk '/22\/Mar\/2018/{ips[$1]++} END{for(i in ips){print i,ips[i]}}' access.log |awk '$2>100' |sort -k2 -rn|head # 统计nginx访问次数大于100的ipawk '/22\/Mar\/2018/{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}}' access.log |sort -k2 -rn|head
cat /var/log/access_liang.log |awk '$7~/liang.php/ {print $1}' |sort |uniq -c # 统计含有liang.php页面的ip
awk '{sum += $1} END {print sum}' # 列相加awk '{line[NR]=$0}END{for(i=NR;i>=1;i--){print line[i]}}' 1 # 行倒序awk '{for(i=NF;i>=1;i--)if(i==1)printf $i"\n";else printf $i" "}' 1 # 列倒序
统计访问 IP 次数:
awk '{a[$1]++}END{for(v in a)print v,a[v]}' access.log
统计访问访问大于 100 次的IP:
awk '{a[$1]++}END{for(v in a){if(a[v]>100)print v,a[v]}}' access.log
统计访问 IP 次数并排序取前10:
awk '{a[$1]++}END{for(v in a)print v,a[v] |"sort -k2 -nr |head -10"}' access.log
统计时间段访问最多的IP:
awk '$4>="[02/Jan/2018:00:00:00" && $4<="[02/Jan/2018:00:03:00"{a[$1]++}END{for(v in a)print v,a[v]}' access.log
统计上一分钟访问量:
date=$(date -d '-1 minute' +%d/%d/%Y:%H:%M)awk -vdate=$date '$4~date{c++}END{print c}' access.log
统计访问最多的 10 个页面:
awk '{a[$7]++}END{for(v in a)print v,a[v] |"sort -k1 -nr|head -n10"}' access.log
统计每个 URL 数量和返回内容总大小:
awk '{a[$7]++;size[$7]+=$10}END{for(v in a)print a[v],v,size[v]}' access.log
统计每个 IP 访问状态码数量:
awk '{a[$1" "$9]++}END{for(v in a)print v,a[v]}' access.log
统计访问 IP 是 404 状态次数:
awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print v,a[v]}' access.log
找出 b 文件在 a 文件相同记录:
awk 'NR==FNR{a[$0]=1}NR!=FNR{if($0 in a) print $0}' a b
找出 b 文件在 a 文件不同记录:
awk 'FNR==NR{a[$0]=1;next}!a[$0]' a b
-
引用外部变量
使用-v参数可以将外部值传给awk中使用的变量awk -v user=root -F: '$1 == user' passwd
-
案例
例1
cat a.file 姓名 费用 数量 zhangsan 8000 1zhangsan 5000 1lisi 1000 1lisi 2000 1wangwu 1500 1zhaoliu 6000 1zhaoliu 2000 1zhaoliu 3000 1awk 'NR>1{name[$1]++;number[$1]+=$3;money[$1]+=$2}END{for(i in name)print i,number[i],money[i]}' a.file zhaoliu 3 11000zhangsan 2 13000wangwu 1 1500lisi 2 3000
例2
求每个人的平均成绩cat a.txt one 23 23 45 22 22two 23 23 45 22 22 28three 23 23 45 22 22 82 23four 23 23 45 22 22 23 45 32 23cat a.awk BEGIN{ print "姓名","平均成绩"}{ for(i=2;i<=NF;i++){ sum=sum+$i} avg=sum/(NF-1)print $1,avgsum=0}awk -f a.awk a.txt 姓名 平均成绩 one 27two 27.1667three 34.2857four 28.6667
例3
按列统计,列转行cat a.file 姓名:liang 性别:male 电话:18623432212 姓名:mobai 性别:male 电话:18223432122 姓名:right 性别:female 电话:18123432212cat a.file |awk -F":" '{print $2}' |sed "/^$/d" |awk 'BEGIN{print "姓名\t性别\t电话"} {printf NR%3!=0?$0"\t":$0"\n"}'姓名 性别 电话 liang male 18623432212mobai male 18223432122right female 18123432212
例4
获取每行的最大值cat c.txt 20 30 4055 44 3323 76 5413 19 9cat c.awk { max=$1 for (i=2;i<=NF;i++) { if ($i>max){ max=$i} }print max}awk -f c.awk c.txt 40557619按行从小到大排序cat c1.awk { num=0 if($1>$2) { num=$1$1=$2$2=num } if($1>$3) { num=$1$1=$3$3=num } if($2>$3) { num=$2$2=$3$3=num }print $1,$2,$3}awk -f c1.awk c.txt 20 30 4033 44 5523 54 769 13 19
转载请标明出处【shell那点事儿——运维工程师必会正则表达式及文本处理三剑客】。
《www.micoder.cc》
虚拟化云计算,系统运维,安全技术服务.
Tags: | [阅读全文...] |
最新评论