shell脚本-正则表达式与文本处理器

一、正则表达式

1.正则表达式概述

正则表达式REGEXP(Regular Expressions)由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能,类似于增强版的通配符功能,但与通配符不同,通配符功能是用来处理文件名,而正则表达式是处理文本内容中字符。

正则表达式被很多程序和开发语言所广泛支持:vim,less,grep,sed,awk,nginx,mysql等。

正则表达式组成:

  • 普通字符
    大小写字母、数字、标点符号及一些其他符号
  • 元字符
    在正则表达式中具有特殊意义的专用字符

正则表达式的分类:

  • 基础正则表达式
  • 扩展正则表达式
  • 编程语言支持的高级正则表达式

BRE和ERE语法基本一致,只有部分元字符(预定义的带有特殊含义的一些符号)需要区别对待。

扩展正则中这些元字符可直接使用:?、+、{、}、|、(、)。

基础正则中这些元字符前需要加反斜杠转义:?、+、{、}、|、(、\)。

点击查看代码
grep 、 sed 默认使用基础正则表达式
grep -E、sed -r、egrep、awk 扩展正则表达式

2.基础正则 元字符(字符匹配)

元字符 含义
. 匹配任意单个字符,可以是一个汉字(. 在[ ]中仅代表.)默认情况下 . 无法匹配换行符
[] 匹配指定范围内的任意单个字符 [a-zA-Z0-9]
[^] 匹配指定范围外的任意单个字符 (取反 )
[4-59] 代表匹配4、5、9 这三个字符中的任意单个字符,而不是4~59范围内的字符
\n 匹配换行符
\t 匹配制表符
点击查看代码
[root@node1 ~]#  grep r..t /etc/passwd   //.表示任意单个字符
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

[root@node1 ~]#  ls /etc/|grep rc[.0-6]    //.在[]中仅仅代表点.这个字符
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

3.基础正则 表示次数(量词)

在基础正则表达式中,对于量词的元字符需要加上反斜线\进行转义。(扩展正则不用加\)量词写在字符串的后面,代表该字符串出现的次数

元字符 含义
* 匹配前面的字符任意次,包括0次
.* 任意长度的任意字符  不包括0次
? 匹配其前面的字符出现0次或1次
+ 匹配其前面的字符出现至少1次
匹配前面的字符至少m次,至多n次
匹配前面的字符至少n次
匹配前面的字符至多n次,<=n
点击查看代码
[root@node1 ~]#  echo goooooogle |grep 'go*gle'   //*表示0到+∞次
goooooogle

[root@node1 ~]#  echo gdadadadgle |grep "g.*gle"  //.*代表任意匹配所有
gdadadadgle

[root@node1 ~]#  echo ggle |grep "go\?gle"        //\?表示0次或1次
ggle
[root@node1 ~]#  echo gogle |grep "go\?gle"
gogle
[root@node1 ~]#  echo gooooogle |grep "go\?gle"      
//未显示结果

[root@node1 ~]#  echo gooooogle |grep "go\+gle"   //\+表示1个及以上
gooooogle

[root@node1 ~]#  echo gooooogle |grep 'go\{2,5\}gle' //代表前面的o出现2次以上5次以下
gooooogle

基础正则和扩展正则都支持贪婪匹配,不支持非贪婪匹配。如果想要在基础正则或扩展正则上实现非贪婪匹配,比较复杂,但也能实现。

4.位置锚点

符号 含义
^ 行首锚定, 用于模式的最左侧
$ 行尾锚定,用于模式的最右侧
^$ 空行
< 匹配单词开头处的位置
> 匹配单词结尾处的位置
\b 匹配单词边界处的位置(开头和结尾), \bword\b 等价于
点击查看代码
[root@node1 ~]#  cat /etc/fstab 

#
# /etc/fstab
# Created by anaconda on Thu May  9 20:58:23 2024
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0

[root@node1 ~]#  grep "^[^#]" /etc/fstab    //过滤不是以#开头的非空行
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0


[root@node1 ~]#  echo hello-123 |grep "\<123"  //除了字母、数字、下划线,其他都算单词的分隔符
hello-123
[root@node1 ~]#  echo hello 123 |grep "\<123"
hello 123

5.分组与后向引用

分组:用()将多个字符捆绑在一起,当作一个整体处理。

使用小括号包围一部分正则表达式(pattern),这部分正则表达式即成为一个分组整体,也即成为一个子表达式。

  • 小括号有两个隐含的功能
    分组
    自动捕获:保存分组的匹配结果,以后可以去引用这个捕获的结果
    根据左括号的位置决定第几个分组。例如:(abc)def、([a-d]){3}、(([0-9])abc(def){2})(hgi)。

分组后可以使用\N来反向引用对应的分组的匹配结果,N是1-9的正整数,\1表示第一个分组表达式的匹配结构,\2表示第二个分组表达式的匹配结果,以此类推。

后向引用:

点击查看代码
示例:
[root@node1 ~]#  echo abc123XYZ | sed -nr 's/(abc)(123)(XYZ)/\3\2\1/p' 
XYZ123abc
//(abc)对应\1   (123)对应\2   (XYZ)对应\3

注意:只有扩展正则表达式(-r)才可以使用后向引用

二、文本三剑客只grep

命令格式:grep [选项] ... 匹配式/表达式 [文件名]或标准输入

选项 含义
-m 匹配到#行停止  (grep -m 1 root /etc/passwd  //多个匹配只取第一行 )
-v 反向匹配 ,输出与查找条件相反的行-i忽略字符大小写-n显示匹配的行号
-c 统计匹配的行数
-o 仅显示匹配到的字符串
-q 静默模式,不输出任何信息(写脚本会用)
-A after, 后#行 (grep -A3 root /etc/passwd   //匹配到的行的后3行也显示出来)
-B before, 前#行-Ccontext, 前后各#行
-e 实现多个选项间的逻辑or或关系 (grep -e root -e bash /etc/passwd //包含root或者包含bash 的行)
-w 匹配整个单词  (grep -w root /etc/passwd )
-E 使用扩展正则表达式
-f 以文件作为匹配的条件  (过滤两个文件中重合的部分)
-r 递归目录,但不处理软链接
点击查看代码
面试题:
统计当前主机的连接状态
[root@localhost ~]# ss -nta | grep -v '^State' |cut -d" " -f1|sort |uniq -c
      3 ESTAB
     17 LISTEN
     
统计当前连接主机数
[root@node1 ~]#  ss -nt |tr -s " "|cut -d " " -f5|cut -d ":" -f1 |sort|uniq -c
      2 192.168.204.1
      1 Address

三、文本三剑客只sed

sed是行编辑器

1.sed原理

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间(PatternSpace),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。
一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。如果使用vi命令打开几十M上百M的文件,明显会出现有卡顿的现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开。Sed就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快

2.sed基本原理

sed 选项 '语法(地址+脚本命令)' 文本名/标准输入
常用选项:

选项 含义
-n 不输出模式空间内容到屏幕,即不自动打印
-e 多点编辑 (sed -n -e '/^r/p'  -e'/^b/p' /etc/passwd)
-f FILE从指定文件中读取编辑脚本
-r 或 -E 使用扩展正则表达式
-i 直接修改目标文本文件
-i.bak 备份文件并原处编辑
点击查看代码
[root@node1 ~]# sed ' '   //默认将输入内容打印出来,系统自带自动打印
[root@node1 ~]# sed ' ' /etc/fstab    //查看文件内容
[root@node1 ~]# sed ' ' </etc/fstab   //支持重定向
[root@node1 ~]# cat /etc/issue |sed ' '    //支持管道符

3.sed脚本格式
'地址+命令'单引号中间需要写脚本,脚本格式如下:

点击查看代码
`地址`
1. 不给地址:对全文进行处理(比如行号)
2. 单地址:
   #:指定的行
   $:最后一行
   /pattern/:被此处模式所能够匹配到的每一行,正则表达式
3. 地址范围:
   #,#     #从#行到第#行,3,6 从第3行到第6行
   #,+#    #从#行到+#行, 3,+4 表示从3行到第7行
   /pat1/,/pat2/    第一个正则表达式  到  第二个正则表达式之间的行   //后3种不推荐   
   #,/pat/  从#号行为开始找到 pat为止  
   /pat/,#  找到#号个pat为止
4. 步进:~
     1~2 奇数行
     2~2 偶数行

点击查看代码
`命令`
p 打印当前模式空间内容,追加到默认输出之后
Ip 忽略大小写输出
d 删除模式空间匹配的行,并立即启用下一轮循环
a [\]text 在指定行后面追加文本,支持使用\n实现多行追加
i [\]text 在行前面插入文本
c [\]text 替换行为单行或多行文本
w file 保存模式匹配的行至指定文件                       seq 10 |sed -n '2wa.txt'
r file 读取指定文件的文本至模式空间中匹配到的行后         seq 10|sed '2r /etc/issue'
= 为模式空间中的行打印行号      sed '2=' /etc/passwd    sed -n -e '=;p' /etc/passwd
! 模式空间中匹配行取反处理      seq 10 |sed -n '1~2!p'
q           结束或退出sed      seq 10 | sed '3q'

4.搜索代替
格式:'s///'

点击查看代码
行范围 s/旧字符串/新字符串/替换标记

#4种替换标记:
数字:表明新字符串将替换第几处匹配的地方
g:表面新字符串将会替换所有匹配的地方
p:打印与替换命令匹配的行,与-n一起使用
w 文件:将替换的结果写入文件中

补充:&用法
点击查看代码
s /旧字符(可用正则)/新字符(不可用正则!)/    

[root@node1 ~]#  sed -n '/r..t/p'  /etc/passwd    //匹配以r开头以t结尾,中间2个任意字符
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

想要给匹配到的内容后面都加上er怎么做?
sed  -n  ‘s/r..t/&er/p’  /etc/passwd    //&指代前面匹配到的内容!!!

5.变量

sed可以识别bash环境里的变量

点击查看代码
[root@node1 ~]#  name=root   //自定义变量
[root@node1 ~]#  sed -nr '/$name/p' /etc/passwd   //此处使用单引号不显示结果
[root@node1 ~]#  sed -nr "/$name/p" /etc/passwd   //使用双引号,变量直接引用
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@node1 ~]#  sed -nr '/'$name'/p' /etc/passwd //变量处使用单引号把变量括起来
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

sed可以调用变量来改文件

点击查看代码
[root@node1 ~]# port=8080   //可以使用变量来修改端口号
[root@node1 ~]# sed -ri 's/^Listen 80/Listen '$port'/' httpd.conf

6.sed的高级用法

sed中除了模式空间,还支持保持空间(Hold Space),利用此空间,可以将模式空间中的数据,临时保存至保存空间,从而后续接着处理,实现更为强大的功能。

点击查看代码
`常见的高级命令`
P 打印模式空间开端至\n内容,并追加到默认输出之前
h 把模式空间中的内容覆盖至保持空间中
H 把模式空间中的内容追加至保持空间中
g 从保持空间取出数据覆盖至模式空间
G 从保持空间取出内容追加至模式空间
x 把模式空间中的内容与保持空间中的内容进行互换
n 读取匹配到的行的下一行覆盖至模式空间
N 读取匹配到的行的下一行追加至模式空间
d 删除模式空间中的行
D 如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使
用合成的模式空间重新启动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环

示例:打印偶数行

点击查看代码
seq 10 | sed -n 'n;p'   //高级用法
seq 10 | sed -n '2~2p'  
seq 10 | sed '1~2d'
seq 10 | sed -n '1~2!p'

四、文本三剑客之awk

awk是列处理器

1.awk概述

awk的工作原理

  • 逐行读取文本,默认以空格或tab键为分隔进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。
  • awk倾向于将一行分成多个“字段”然后再进行处理。
  • awk信息的读入也是逐行读取的,执行结果可以通过print的功能将字段数据打印显示。
  • 使用awk命令的过程中,可以使用逻辑操作符“&&”表示与、“|”表示或、“!”表示非,还可以进行简单的数学运算,如+、-、*、/、%、^分别表示加、减、乘、除、取余和乘方。

awk的命令格式:

点击查看代码
awk  [选项]  'program'  [文件名]

`常见选项:`
-F分隔符 :指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符
-v  :var=value 变量赋值

'program'格式:
pattern{action statements;..}
表达式 + 处理动作
pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END,正则表达式等
action statements:对数据进行处理,放在{}内指明,常见:print

基本用法

点击查看代码
[root@node1 ~]#  awk '{print "hello"}'   //字符串需要添加双引号
1
hello
2
hello

`示例:打印分区利用率`
[root@node1 ~]#  df 
文件系统                   1K-块    已用    可用 已用% 挂载点
/dev/mapper/centos-root 10475520 5201356 5274164   50% /
devtmpfs                  917604       0  917604    0% /dev
tmpfs                     933524       0  933524    0% /dev/shm
tmpfs                     933524    9280  924244    1% /run
tmpfs                     933524       0  933524    0% /sys/fs/cgroup
/dev/sda1                1038336  182376  855960   18% /boot
tmpfs                     186708      44  186664    1% /run/user/0
/dev/sr0                 4414592 4414592       0  100% /run/media/root/CentOS 7 x86_64
[root@node1 ~]#  df | awk '{print $5}' |awk -F% '{print $1}'  //先打印第五列,再以%为分隔符打印第一列
已用
50
0
0
1
0
18
1
100
[root@node1 ~]#  df | awk -F"[ %]+" '{print $5}'  //以空格和%为分隔符,+代表一个及以上,打印第五列
已用
50
0
0
1
0
18
1
100

3.内置变量

内置变量是awk预定义好的、内置在awk内部的变量
注意-v选项

|内置变量|含义|
|FS|表示每行文本的字段分隔符,默认是空格或制表符Tab|
|NF(number of Field)|表示字段数量变量,在处理记录时,它表示当前记录的字段数|
|NR|表示记录的数量变量,在处理文件时,它表示当前处理的是第几行|
|FNR|显示多个文件的行号|
|$0|表示整个输入记录|
|$n|当前处理行的第n个字段(第n列)|
|OFS|输出时的分隔符,默认是空格|
|RS|行分隔符,默认是换行符,预设值是\n|
|FILENAME|被处理的文件名|

3.FS输入分隔符

点击查看代码
[root@node1 ~]#  awk -v FS=':' '{print $1FS$3}' /etc/passwd |head  //-v 变量赋值,以:为分隔符,打印第一列和第三列
root:0
bin:1
daemon:2
adm:3
lp:4
sync:5
shutdown:6
halt:7
mail:8
operator:11

点击查看代码
`传参:`
[root@node1 ~]#  fs=:
[root@node1 ~]#  echo $fs
:
[root@node1 ~]#  awk -v FS=$fs '{print $1}' /etc/passwd |head  //定义变量传给FS
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator

OFS输出分隔符

点击查看代码
[root@node1 ~]#  awk -F:  '{print $1,$3}' /etc/passwd |head -n5  //默认输出分隔符为空格
root 0
bin 1
daemon 2
adm 3
lp 4

[root@node1 ~]#  awk -F: -v OFS='==' '{print $1,$3}' /etc/passwd |head -n5  //OFS指定输出分隔符为==
root==0
bin==1
daemon==2
adm==3
lp==4

[root@node1 ~]#  awk -F:  '{print $1"=="$3}' /etc/passwd |head -n5   
root==0
bin==1
daemon==2
adm==3
lp==4

RS行分隔符

点击查看代码
[root@node1 ~]#  echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@node1 ~]#  echo $PATH | awk -v RS=':' '{print $0}'  //指定:为行分隔符
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin


NF(number of Fieid)

点击查看代码
[root@node1 ~]#  df | awk '{print NF}'   //NF代表字段的个数
6
6
6
6
6
6
6
6
8
[root@node1 ~]#  df | awk '{print $NF}'  //$NF等于$6,打印第六列
挂载点
/
/dev
/dev/shm
/run
/sys/fs/cgroup
/boot
/run/user/0
x86_64
[root@node1 ~]#  df | awk '{print $(NF-1)}'  //打印第五列
已用%
50%
0%
0%
1%
0%
18%
1%
7

NR

点击查看代码
[root@node1 ~]#  awk '{print NR}' /etc/fstab   //NR表示每一行的行号
1
2
3
4
5
6
7
8
9
10
11

[root@node1 ~]#  awk 'NR==2{print $1}' /etc/passwd      //NR==2代表第二行记录
bin:x:1:1:bin:/bin:/sbin/nologin
[root@node1 ~]#  awk -F: 'NR==2{print $1}' /etc/passwd  //以:为分隔符,打印第二行的第一列
bin
[root@node1 ~]#  awk -F: 'NR==2{print NR,$1}' /etc/passwd //print后面加NR可以显示行号
2 bin
[root@node1 ~]#  awk -F: 'NR==2{print $0}' /etc/passwd  //$0表示当前处理的行的整行内容
bin:x:1:1:bin:/bin:/sbin/nologin

[root@node1 ~]#  awk -F: 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd  //取大于等于3小于且等于5的行
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
# &&代表且,||代表或

[root@node1 ~]#  awk -F: 'NR%2==0{print NR,$0}' /etc/fstab   //打印出函数取余数为0的行,即偶数行
2 #
4 # Created by anaconda on Thu May  9 20:58:23 2024
6 # Accessible filesystems, by reference, are maintained under '/dev/disk'
8 #
10 UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f /boot                   xfs     defaults        0 0
[root@node1 ~]#  awk -F: 'NR%2==1{print NR,$0}' /etc/fstab   //打印出函数取余数为1的行,即奇数行
1 
3 # /etc/fstab
5 #
7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
9 /dev/mapper/centos-root /                       xfs     defaults        0 0
11 /dev/mapper/centos-swap swap                    swap    defaults        0 0


[root@node1 ~]#  awk -F: '$1 ~ /root/{print NR,$0}' /etc/passwd  //打印第一列中含有root的行
1 root:x:0:0:root:/root:/bin/bash

4.awk自定义变量
awk -v 自定义变量 'program'[文件名]

5.模式PATTERN
1.模式为空
如果模式为空表示每一行都匹配成功,相当于没有额外条件

点击查看代码
例: awk -F: '{print $1,$3}' /etc/passwd

2.正则匹配
/regular expression/:仅处理能够模式匹配到的行,需要用//括起来

点击查看代码
例:
[root@node1 ~]#  awk  '/^UUID/{print $1}'  /etc/fstab  //打印第一列以UUID开头的行
UUID=d13887da-6764-43a1-b6a8-f6d3dbf5ae4f

3.line ranges 行范围
不支持使用行号,但是可以使用变量NR间接指定行号加上比较操作符或者逻辑关系

点击查看代码
`算术操作符`
x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

`比较操作符`
==, !=, >, >=, <, <=

`逻辑`
与:&&,并且关系
或:||,或者关系
非:!,取反

`模式匹配符`
~  左边是否和右边匹配,包含关系
!~ 是否不匹配

4.BEGIN、END

  • BEGIN{ }:仅在开始处理文件中的文本之前执行一下
  • END{ }:仅在文本处理完成之后执行一次
点击查看代码
awk 'BEGIN{...};{...};END{...}' 文件

#处理过程:
1、在awk处理指定的文本之前,需要先执行BEGIN{...}模式里的命令操作;
2、中间的{...} 是真正用于处理文件的命令操作;
3、在awk处理完文件后才会执行END{...}模式里的命令操作。END{ }语句块中,往往会放入打印结果等语句。

6.关系表达式
关系表达式结构为 真 才会被处理
真:结果为非0值,非空字符串
假:结果为空字符串或0值

点击查看代码
[root@node1 ~]#  seq 3 |awk '1'   //1为真,打印
1
2
3
[root@node1 ~]#  seq 3 |awk '0'   //0为假,不打印

[root@node1 ~]#  seq 3 |awk 'n++' //除了第一行,都打印
2
3
[root@node1 ~]#  seq 3 |awk '!n++'  //只打印第一行
1

[root@node1 ~]#  seq 10 |awk 'i=!i' //打印奇数行
1
3
5
7
9
[root@node1 ~]#  seq 10 |awk '!(i=!i)'      //打印偶数行
[root@node1 ~]#  seq 10 |awk -v i=1 'i=!i'  //打印偶数行
2
4
6
8
10

posted @ 2024-05-16 16:40  leikj  阅读(8)  评论(0编辑  收藏  举报