MENU

Linux正则表达式及三剑客笔记



一. 正则表达式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
  1. awk使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束
  2. 然后,行被空格或制表符分解成字段或域,每个字段存储在已编号的变量中,从$1开始,最多达100个字段
  3. awk如何知道用空格来分隔字段呢?因为有一个内置变量FS来确定字段分割符。初始时,FS赋值为空格
  4. awk打印字段时,它将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$2之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分割符OFSOFS默认为空格
  5. awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分割成字段并进行处理。该过程将持续到所有行处理完毕

5. 字段及字段相关的内部变量

变量名说明解释示例
$0awk变量$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...,$100awk -F; '{print $0,NF}' /etc/passwd
FS输入字段分割符,默认空格或Tabawk '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. 数组

  1. 数组

    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
  2. 遍历数组

    #按元素个数遍历
    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
  3. 示例

    #统计/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 许可协议
若转载本文,请标明出处并告知本人

返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码