Linux正则表达式及三剑客笔记
【阿里云】爆款云产品,新客特惠全年最低价,云服务器低至0.4折起,11.1开售
【腾讯云】爆款1核2G云服务器首年48元,还有iPad Pro、Bose耳机、京东卡等你来抽!
【华为云】上云特惠巨划算,免单抽奖享豪礼
【七牛云】爆款云产品全年最低价,热门产品 0 元秒杀,参与抽奖赢新款 iPhone
一. 正则表达式RE
1. 正则表达式
1.1. 概述
正则表达式(regular expression,RE)是一种字符模式,用于在查找过程中匹配制定的字符。
在大多数程序里,正则表达式都被置于两个正斜杠之间,例如/l[oO]ve/
就是由正斜杠界定的正则表达式,
他将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念。
1.2. 示例
#匹配数字
^[0-9]+$ 123 456
#匹配Mail
[a-z0-9_]+@[a-z0-9]+\.[a-z]+ me@leeyiding.com
#匹配IP方式一
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
#匹配IP方式二
[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}
#例如
egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-lo
#IPADDR=127.0.0.1
#NETMASK=255.0.0.0
#NETWORK=127.0.0.0
#BROADCAST=127.255.255.255
2. 元字符
2.1. 概述
元字符是这样一类字符,它们可以表达的是不同于字面本身的含义。
Shell元字符(也称通配符):由Shell来解析。如:rm -rf *.pdf
,Shell将元字符*
解析为任意多个字符。
正则表达式元字符:由各种执行模式匹配操作的程序来解析,比如vi、grep、sed、awk、python
2.2. 正则表达式元字符
基本正则表达式元字符
扩展正则表达式元字符
POSIX字符类
表达式 | 功能 | 示例 |
---|---|---|
[:alnum:] | 字母与数字组合 | [[:alnum:]]+ |
[:alpha:] | 字母字符(包括大小写) | [[:alpha:]]{4} |
[:blank:] | 空格与制表符 | [[:blank:]]* |
[:digit:] | 数字字母 | [[:digit:]]? |
[:lower:] | 小写字母 | [[:lower:]]{5,} |
[:upper:] | 大写字母 | [[:upper:]]+ |
[:punct:] | 标点符号 | [[:punct:]] |
[:space:] | 包括换行符、回车等在内的所有空白 | [[:space:]]+ |
二. grep
1. 概述
grep
:在文件中全局查找指定的正则表达式,并打印所有包括该表达式的行
egrep
:扩展的grep,支持更多的正则表达式元字符
fgrep
:固定grep(fixed grep),有时也被称作快速grep(fast grep),它按字面解释所有的字符
2. 命令格式
grep [option] PATTERN filename filename
找到匹配字符:grep返回的退出状态为0
没找到匹配的字符:grep返回的退出状态为1
找不到指定文件:grep返回的退出状态为2
grep程序的输入可以来自标准输入或管打,而不仅仅是文件
3. grep使用元字符
grep:使用基本的元字符集
egrep(或grep -E):使用扩展元字符集
注:grep也可以使用扩展集中的元字符,仅需要对这些元字符前置一个反斜杠转义
元字符 | 描述 | 举例 | |
---|---|---|---|
w | 所有的字母与数字,称之为字符[a-zA-Z0-9] | 'l[a-zA-Z0-9]*ve' | 'lw*ve' |
W | 所有字母与数字之外的字符,称为非字符 | 'love[^a-zA-Z0-9]+' | 'loveW+' |
b | 词边界 | '\<love\>' | 'bloveb' |
4. grep选项
- -i 忽略大小写
- -l 只列出匹配行所在的文件名
- -n 在每一行前面加上它在文件中对应的行号
- -c 显示成功匹配的行数
- -s 禁止显示文件不存在或文件不可读的错误信息
- -q 静默
- -v 反向查找,只显示不匹配的行
- -R 针对目录,递归查找
- -o 只显示匹配的内容
三. 流编辑器 sed
1. 概述
sed是一种在线的、非交互式的编辑器,它一次处理一行的内容,当把处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区的内容,处理完毕后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed主要用来自动编辑一个或多个文件,简化对文件的反复操作;编写转换程序等。
2. 命令格式
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
注:sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0,只有当命令存在语法错误时,sed的退出状态才是非0
3. 支持正则表达式
与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是括在斜杠间的模式,用于查找和替换,sed支持基本元字符和扩展元字符
使用扩展元字符要加反斜杠转义或加-r选项
4. sed基本用法
4.1. 参数选项
参数选项 | 解释说明 |
---|---|
-n | 取消sed默认的输出,常与sed内置命令的p连用 |
-i | 直接修改文件内容,而不是输出到终端。如果不使用-i选项,则sed只是修改内存中的数据,并不会影响磁盘上的文件 |
-r | 使sed支持扩展元字符 |
-e | 允许多项编辑 |
-f | 指定sed脚本文件名 |
4.2. 内置命令字符
sed内置命令字符 | 解释说明 |
---|---|
a | 全拼append,表示追加文本,在指定行后添加一行或多行文本 |
d | 全拼delete,表示删除匹配行的文本 |
c | 替换指定行 |
i | 全拼insert,表示插入文本,在指定行前添加一行或多行文本 |
p | 全拼print,表示打印匹配行的内容,通常p会与选项-n一起使用 |
s/regexp/replacement/ | 匹配regexp部分的内容,用replacement替换regexp匹配的内容,regexp部分可以使用正则表达式,在replacement部分可以使用特殊字符&和1-9等匹配regexp部分的部分内容,结尾常与g匹配做全局的替换 |
n | 操作所匹配内容的下一行 |
! | 对所选以外的所有行应用命令 |
r | 读入一个文件 |
w | 将处理后的内容写入一个文件 |
4.3. 定址
地址用于决定对哪些行进行编辑,地址形式可以是数字、正则表达式或二者的结合。如果没有指定地址,sed将处理输入文件中所有行
5. 示例
sed -r 'd' passwd #删除所有行
sed -r '3d' passwd #删除第3行
sed -r '1,3d' passwd #删除1-3行
sed -r '/root/d' passwd #删除带有root的行
sed -r '/root/!d' passwd #删除不包括root的行
sed -r '/root/,5d' passwd #从root所在行开始删除到第5行
sed -r '/^root/,+5d' passwd #删除以root开始的行及以下5行
sed -r 's/root/0/g' passwd #将root替换成0
sed -r '1~2d' passwd #删除所有奇数行
sed -r '0~2d' passwd #删除所以偶数行
#删除配置文件中#号注释行
sed -ri '/^#/d' file.conf
sed -ri '/^[ \t]*#/d' file.conf
#删除配置文件中//号注释行
sed -ri '\Y^[ \t]*//Yd' file.conf
#删除无内容空行
sed -ri '/^[ \t]*$/d' file.conf
#删除注释行及空行
sed -ri '/^[ \t]*#/d; /^[ \t]*$/d' file..conf
sed -ri '/^[ \t]*#|^[ \t]*$/d' file.conf
sed -ri '/^[ \t]*($|#)/d' file.conf
#给文件添加注释
sed -ri '2,6s/^/#/' a.txt
sed -ri '2,6s/(.*)/#\1/' a.txt
sed -ri '2,6s/.*/#&/' a.txt #&匹配前面查找的内容
sed -ri '3,$ s/^#*/#/' a.txt #将行首零个或多个#换成一个#
sed -ri '3,6 s/^[ \t]*#*/#/' a.txt #行首有tab或空格
sed -ri '3,6 s/^[ \t#]*/#/' a.txt #同上
#sed使用外部变量
var1=1111
sed -ri '3a$var1' file.txt #强引用,第三行后加入$var1
sed -ri "3a$var1" file.txt #弱引用,第三行后加入1111
sed -ri 3a$var1 file.txt #同上
sed -ri "$a$var1" file.txt #弱引用,$a被当成变量
sed -ri '$a'"$var1" file.txt #最后一行加入1111
sed -ri "\$a$var1" file.txt #弱引用,将$转义
#文件行倒转,类似于tac命令
sed -ri '1!G; $!h; $!d' file.txt
四. 文本处理 awk
1. 概述
awk是一种编程语言,用于在Linux/Unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件、或其他命令的输出。它支持用户自定义函数和动态正则表达式等先进的功能,是Linux/Unix下的一个强大的编程工具。它在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它通过扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,寻炸匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出,如果没有指定模式,则所有被操作所指定的行都被处理。
2. 语法格式
awk [options] 'commands' filename
awk [options] -f awk-script-file filename
3. 命令及选项
3.1. 选项
-F:定义输入字段分割符,默认的分割符是空格或者制表符(Tab)
3.2. 命令
BEGIN{}
:行处理前{}
:行处理END{}
:行处理后
BEGIN{}
通常用于定义一些变量,例如BEGIN{FS=":";OFS="---"}
3.3. 命令格式
awk 'pattern' filename
#示例
awk -F: '/root/' /etc/passwd
awk '{action}' filename
#示例
awk -F: '{print $1}' /etc/passwd
awk 'pattern {action}' filename
#示例
awk -F: '/root/{print $1,$3}' /etc/passwd
awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd
command | awk 'pattern {action}'
#示例
df -P | grep '/' | awk '$4 > 25000 {print$4}'
4. 工作原理
awk -F: '{print $1,$3}' /etc/passwd
- awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束
- 然后,行被空格或制表符分解成字段或域,每个字段存储在已编号的变量中,从$1开始,最多达100个字段
- awk如何知道用空格来分隔字段呢?因为有一个内置变量
FS
来确定字段分割符。初始时,FS
赋值为空格 - awk打印字段时,它将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为
$1,$2
之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分割符OFS
,OFS
默认为空格 - awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分割成字段并进行处理。该过程将持续到所有行处理完毕
5. 字段及字段相关的内部变量
变量名 | 说明解释 | 示例 |
---|---|---|
$0 | awk变量$0保存当前记录的内容 | awk -F: '{print}' /etc/passwd |
NR | 输入总行的编号 | awk -F: '{print NR,$0}' /etc/passwd /etc/hosts |
FNR | 当前输入文件的行编号 | awk -F: '{print FNR,$0}' /etc/passwd /etc/hosts |
NF | 保存记录的字段数$1,$2...,$100 | awk -F; '{print $0,NF}' /etc/passwd |
FS | 输入字段分割符,默认空格或Tab | awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd |
OFS | 输出字段分割符 | awk 'BEGIN{FS=":";OFS="+++"} {print $1,$2,$3,$4}' /etc/passwd |
RS | 记录分割符,默认为换行符 | awk -F: 'BEGIN{RS=" "} {print $0}' /etc/passwd |
ORS | 输出记录分割符 | awk -F: 'BEGIN{ORS=" "} {print $0}' /etc/passwd |
#1.将文件每一行合并为一行
awk 'BEGIN{ORS=" "} {print $0}' /etc/passwd
#2.自定义输出分割符
head -1 /etc/passwd > passwd1
awk 'BEGIN{RS=":"} {print $0}' passwd1
6. 格式化输出
1. print函数
data | awk '{print "Month: " $2 "\nYear: " $NF}'
awk -F: '{print "username is: " $1 "\t uid is: " $3}' /etc/passwd
awk -F: 'print "\tusername and uid: " $1,$3' /etc/passwwd
2. printf函数
awk -F: '{printf "%-15s %-10s %-15s\n",$1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n",$1,$2,$3}' /etc/passwd
解释说明:
%s
:字符类型%d
:数值类型%f
:浮点类型%-15s
:占15字符-
:表示左对齐,默认为右对齐\n
:printf默认不会在行尾自动换行
7. awk模式和动作
任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作时刻保持执行状态。模式可以是任何条件语句或复合语句或正则表达式。模式包括两个特殊字段BEGIN和END。使用BEGIN语句设计计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文本开始执行。END语句用来在awk完成文本浏览动作后打印输出文本总数和结尾状态。
7.1. 正则表达式模式
#匹配记录整行
awk '/^root/' /etc/passswd
awk '$0 ~ /^tom/' /etc/passwd
awk '!/root/' /etc/passwd
awk '$0 !~ /^tom/' /etc/passwd
#匹配字段:匹配操作符(~ !~)
awk -F: '$1 ~ /^tom/' /etc/passwd
awk -F: '$NF !~ /bash$/' /etc/passwd
7.2. 比较表达式
比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,用于比较数字与字符串
运算符 | 含义 | 实例 |
---|---|---|
< | 小于 | x<y |
<= | 小于或等于 | x<=y |
== | 等于 | x==y |
!= | 不等于 | x!=y |
>= | 大于或等于 | x>=y |
> | 大于 | x>y |
awk -F: '$3 == 0' /etc/passwd
awk -F: '$3 < 10' /etc/passwd
awk -F: '$7 == "/bin/bash"' /etc/passwd
awk -F: '$1 == "root"' /etc/passwd
awk -F: '$1 ~ /root/' /etc/passwd
awk -F: '$1 !~ /root/' /etc/passwd
df -P | grep '/' | awk '$4 > 25000'
7.3. 条件表达式
awk -F: '$3 > 300 {print $0}' /etc/passwd
awk -F: '{if($3 > 300) print $0}' /etc/passwd
awk -F: '{if($3 > 300) {print $0} }' /etc/passwd
awk -F: '{if($3 > 300) {print $3} else{print $1} }' /etc/passwd
7.4. 算数运算
适用于 + - * / % ^
可以在模式中执行运算,awk都将按浮点数方式执行算数运算
awk -F: '{$3 * 10 >500 }' /etc/passwd
awk -F: '{if($3*10>500) {print $0} }' /etc/passwd
7.5. 逻辑操作符和复合模式
awk -F: '$1~root && $3<=15' /etc/passwd
awk -F: '$1~root || $3<=15' /etc/passwd
awk -F: '($1~root || $3<=15)' /etc/passwd
7.6. 范围模式
awk -F: '/root/' /etc/passwd
awk -F: '/^root/' /etc/passwd
awk -F: '$3 ~ /^root/' /etc/passwwd
awk -F: '/^(no|so)/' /etc/passwd
awk -F: '{print $3,$2}' /etc/passwd
awk '$8 ~ /[0-9][0-9]$/ {print $8}' file
8. awk脚本编程
8.1. 条件判断
#if语句
#{if(表达式) {语句;语句;....}}
awk -F: '{if($3==0) {print $1 "is administrator."}}' /etc/passwd
awk -F: '{if($3>0 && $3<1000){count++}} END{print count}' /etc/passwd #统计系统用户数
#if...else语句
#{if(表达式) {语句;语句;....} else{语句;语句;....}}
awk -F: '{if($3==0) {print $1} else {print $7}}' /etc/passwd
awk -F: 'id($3==0){count++} else{i++}' /etc/passwd
awk -F: '{if($3==0){count++} else{i++}} END{print "管理员个数:"count; print "系统用户数:"i}' /etc/passwd
#if..else if...else语句
#{if(表达式1) {语句;语句;....} else if(表达式2){语句;语句;....} else{语句;语句;....}}
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i;print k;print j}' /etc/passwd
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i;print "普通用户个数: "k;print "系统用户个数: "j}' /etc/passwd
8.2. 循环
#while
awk "BEGIN{i=1;while(i<=10){print i;i++}}"
awk -F: '{i=1;while(i<=7) {print $i;i++}}' /etc/passwd
awk '{i=1;while(i<=NF) {print $i;i++}}' /etc/hosts
awk -F: '{i=1;while(i<=10) {print $0;i++}}' /etc/passwd #每行打印10次
#for C语言风格
awk 'BEGIN{for(i=1;i<=5;i++){print i}}'
awk -F: '{for(i=1;i<=10;i++) {print $0}}' /ettc/passwd
awk -F: '{for(i=1;i<=NF;i++) {print $i}}' /ettc/passwd
8.3. 数组
数组
awk -F: '{username[++i]=$1} END{print username[1]}' /etc/passwd awk -F: '{username[i++]=$1} END{print username[1]}' /etc/passwd awk -F: '{username[i++]=$1} END{print username[0]}' /etc/passwd
遍历数组
#按元素个数遍历 awk -F: '{username[x++]=$1} END{for(i=0;i<x;i++) print i,username[i]}' /etc/passwd awk -F: '{username[++x]=$1} END{for(i=0;i<=x;i++) print i,username[i]}' /etc/passwd #按索引遍历 awk -F: '{username[x++]=$1} END{for(i in username) {print i,username[i]} }' /etc/passwd awk -F: '{username[++x]=$1} END{for(i in username) {print i,username[i]} }' /etc/passwd
示例
#统计/etc/passwd中各种shell的数量 awk -F: '{shells[$NF]++} END{for(i in shells){print i,shells[i]} }' /etc/passwd #统计网站访问状态 netstat -nat | grep :80 | awk '{access_stat[$NF]++} END{for(i in access_atat) {print i,access_stat[i]}}' | sort -k2 -n | head ss -nat | grep :80 | awk '{access_stat[$2]++} END{for(i in access_stat){print i,access_stat[i]}}' | sort -k2 -n #统计访问的每个IP的数量 netstat -nat | grep :80 | awk -F: '{ip_count[$8]++} END{for(i in ip_count){print i,ip_count[i]}}' | sort netstat -nat | grep :80 | awk -F: '!/LISTEN/{ip_count[$(NF-1)]++} END{for(i in ip_count){print i,ip_count[i]}}' | sort -k2 -rn | head #统计Apache/Nginx日志某一天不同IP的访问量 grep '04/Mar/2020' access.log | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}' | sort -k2 -rn | head grep '04/Mar/2020' access.log | awk '{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}' |sort -k2 -rn #统计用户名为4个字符的用户 awk -F: '$1~/^....$/{count++;print $1} END{print "count is: " count}' /etc/passwd awk -F: 'length($1)==4{count++;print $1} END{print "count is: " count}' /etc/passwd
9. awk变量
9.1. 自定义变量
awk -v user=root -F: '$1 == user' /etc/passwd
9.2. 调用外部变量
var="bash"
#方法一:双引号情况下使用
echo "unix script" | awk "gsub(/unix/,\"$var\")" #gsub()为替换函数,变量的双引号要加反斜杠转义
#方法二:单引号下使用
echo "unix script" | awk 'gsub(/unix/,"'"$var"'")'
#方法三:三个单引号
i=10
df -h | awk '{if(int($5)>'''$i'''){print $6":"$5}}'
版权属于:LeeYD · Blog
本文标题:Linux正则表达式及三剑客笔记
本文链接:https://www.leeyiding.com/archives/45/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 4.0 许可协议
若转载本文,请标明出处并告知本人
【阿里云】爆款云产品,新客特惠全年最低价,云服务器低至0.4折起,11.1开售
【腾讯云】爆款1核2G云服务器首年48元,还有iPad Pro、Bose耳机、京东卡等你来抽!
【华为云】上云特惠巨划算,免单抽奖享豪礼
【七牛云】爆款云产品全年最低价,热门产品 0 元秒杀,参与抽奖赢新款 iPhone
看到你的网站,觉得很不错,希望能与你互相友情链接…
我的网站:建站知道网-http://wozhidaole.com.cn/
如果同意的话,回复后互相上链接!