正则表达式与文本三剑客

目录

一、正则表达式

[1. 定义](#1. 定义)

[2. 字符匹配](#2. 字符匹配)

[3. 重复限定符](#3. 重复限定符)

[4. 位置锚点](#4. 位置锚点)

[5. 分组和引用](#5. 分组和引用)

[6. 扩展正则表达式](#6. 扩展正则表达式)

二、文本三剑客

[1. grep](#1. grep)

[1.1 定义](#1.1 定义)

[1.2 语法](#1.2 语法)

[1.3 选项](#1.3 选项)

[1.4 示例](#1.4 示例)

[2. sed](#2. sed)

[2.1 定义](#2.1 定义)

[2.2 通式](#2.2 通式)

[2.3 选项](#2.3 选项)

[2.4 脚本格式(脚本语法)](#2.4 脚本格式(脚本语法))

[2.4.1 地址了解](#2.4.1 地址了解)

[2.4.2 命令了解](#2.4.2 命令了解)

[2.5 搜索替代](#2.5 搜索替代)

[2.6 分组替换](#2.6 分组替换)

[2.7 调用变量](#2.7 调用变量)

[2.8 修改配置文件](#2.8 修改配置文件)

[2.9 修改网卡名](#2.9 修改网卡名)

[3. awk](#3. awk)

[3.1 定义和格式](#3.1 定义和格式)

[3.2 常见选项和执行流程](#3.2 常见选项和执行流程)

[3.3 基本用法](#3.3 基本用法)

[3.4 常规应用](#3.4 常规应用)

[3.5 常见的内置变量](#3.5 常见的内置变量)

[3.6 自定义变量](#3.6 自定义变量)

[3.7 模式PATTERN](#3.7 模式PATTERN)

[3.7.1 模式为空](#3.7.1 模式为空)

[3.7.2 正则匹配](#3.7.2 正则匹配)

[3.7.3 line ranges:行范围](#3.7.3 line ranges:行范围)

[3.7.4 /pat1/,/pat2/ 案例:找到10:00到11:00之间的日志](#3.7.4 /pat1/,/pat2/ 案例:找到10:00到11:00之间的日志)

[3.7.5 关系表达式](#3.7.5 关系表达式)

[3.8 条件判断](#3.8 条件判断)

[3.9 循环:for | while](#3.9 循环:for | while)

[3.10 数组](#3.10 数组)

[3.10.1 访问、赋值数组元素](#3.10.1 访问、赋值数组元素)

[3.10.2 数组长度](#3.10.2 数组长度)

[3.10.3 遍历数组](#3.10.3 遍历数组)

[3.10.4 实际案例](#3.10.4 实际案例)

[3.11 awk脚本](#3.11 awk脚本)


一、正则表达式

1. 定义

正则表达式是一种用于匹配和操作文本的强大工具,它使用特定的语法和模式来描述文本的结构和模式。正则表达式被很多程序和开发语言所广泛支持:vim,less,grep,sed,awk,nginx,mysql等。

2. 字符匹配

正则表达式可以匹配普通字符和元字符。

普通字符:任何普通字符都可以直接匹配自身

元字符:一些字符具有特殊含义,如点号(.)匹配除了换行符之外的任意字符,星号(*)匹配前一个字符的零个或多个重复,问号(?)匹配前一个字符的零个或一个重复

|--------------|--------------------------------------------------------------------------|
| 元字符 | 说明 |
| . | 匹配任意单个字符,可以是一个汉字 |
| [ ] | 匹配指定范围内的任意单个字符,[0-9],[a-zA-Z] |
| [^] | 匹配指定范围外的任意单个字符,[^a.z]:非a和z |
| [:alnum:] | 字母和数字 |
| [:alpha:] | 代表任何英文大小写字符,亦即A-Z,a-z |
| [:lower:] | 小写字母,示例:[[:lower:]],相当于[a-z] |
| [:upper:] | 大写字母 |
| [:blank:] | 空白字符(空格和制表符) |
| [:space:] | 包括空格、制表符 (水平和垂直)、换行符、回车符等各种类型的空白,比[:blank:]包含的范围广 |
| [:cntrl:] | 不可打印的控制字符(退格、删除、警铃...) |
| [:digit:] | 十进制数字十进制数字 |
| [:xdigit:] | 十六进制数字 |
| [:graph:] | 可打印的非空白字符 |
| [:print:] | 可打印字符 |
| [:punct:] | 标点符号 |
| \w | 匹配单词构成部分,等价于[[:alnum:]] |
| \W | 匹配非单词构成部分,等价于[^
[:alnum:]] |
| \S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] |
| \s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意Unicode 正则表达式会匹配全角空格符 |

示例:

元字符点 .

bash 复制代码
[root@localhost ~]# ls /etc/|grep rc[.0-3]    #此处的点代表字符
rc0.d
rc1.d
rc2.d
rc3.d
rc.d
rc.local
[root@localhost ~]# ls /etc/ | grep 'rc\.'    #点值表示点需要转义
rc.d
rc.local
[root@localhost ~]# grep r..t /etc/passwd     #r..t ..代表任意两个字符
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

元字符[ ]

bash 复制代码
[root@localhost opt]# touch {a..z}.txt
[root@localhost opt]# ls | grep "[fql].txt"  #fql三个字母任意一个
f.txt
l.txt
q.txt

元字符[^]

bash 复制代码
[root@localhost opt]# touch {1..3}.txt
[root@localhost opt]# touch {a..c}.txt
[root@localhost opt]# ls | grep '[^0-9].txt'  #除去0-9任意字符.txt文件
a.txt
b.txt
c.txt

3. 重复限定符

① 星号(*):匹配前一个字符的零个或多个重复,即:匹配前面的字符任意次,包括0次

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab*c'       #匹配前面一个字符b,0次
ac
[root@localhost ~]# echo abbbc | grep 'ab*c'    #匹配前面一个字符b,3次
abbbc

② 点星(.*):任意长度的任意字符 不包括0次

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab.*c';echo $? 
1                                          #ac之间没有b匹配不到
[root@localhost ~]# echo abbbc | grep 'ab.*c'
abbbc                                      #匹配前面的字符b,3次

③ \? :匹配前面的字符1次或0次,即:可有可无

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab\?c'            #匹配前面的字符b,0次
ac
[root@localhost ~]# echo abc | grep 'ab\?c'           #匹配前面的字符b,1次
abc
[root@localhost ~]# echo abbc | grep 'ab\?c';echo $?  #无法匹配
1

④ \+:匹配其前面的字符出现最少1次,即:肯定有且>=1次

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab\+c';echo $?  #无法匹配
1
[root@localhost ~]# echo abc | grep 'ab\+c'         #匹配前面的字符b,1次
abc
[root@localhost ~]# echo abbc | grep 'ab\+c'        #匹配前面的字符b,两次
abbc

⑤ \{n\}:匹配前面的字符n次

bash 复制代码
[root@localhost ~]# echo abbc | grep 'ab\{1\}c';echo $? 
1                                        #匹配前面的字符b,1次,无法匹配,\转义{和} 
[root@localhost ~]# echo abbc | grep 'ab\{2\}c'
abbc                                     #匹配前面的字符b,两次
 

⑥ \{m,n\}:匹配前面的字符至少m次,至多n次

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab\{1,3\}c';echo $?      
1                                #匹配前面的字符b至少1次,至多3次,这里0次无法匹配
[root@localhost ~]# echo abc | grep 'ab\{1,3\}c'
abc
[root@localhost ~]# echo abbbc | grep 'ab\{1,3\}c'
abbbc
[root@localhost ~]# echo abbbbc | grep 'ab\{1,3\}c';echo $?
1                                #匹配前面的字符b至少1次,至多3次,这里4次无法匹配

⑦ \{,n\}:匹配前面的字符至多n次,<=n

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab\{,1\}c'           
ac                              #匹配前面的字符b至多1次,0次可以匹配
[root@localhost ~]# echo abc | grep 'ab\{,1\}c'
abc                             
[root@localhost ~]# echo abbc | grep 'ab\{,1\}c';echo $?
1                               #匹配前面的字符b至多1次,两次无法匹配

⑧ \{n,\}:匹配前面的字符至少n次

bash 复制代码
[root@localhost ~]# echo ac | grep 'ab\{1,\}c';echo $?
1                               #匹配前面的字符b至少1次,0次无法匹配
[root@localhost ~]# echo abc | grep 'ab\{1,\}c'
abc
[root@localhost ~]# echo abbc | grep 'ab\{1,\}c'
abbc

4. 位置锚点

① ^:行首锚定, 用于模式的最左侧

② $:行尾锚定,用于模式的最右侧

③ ^PATTERN$:表示用于模式匹配整行(单独一行 只有PATTERN字符)

④ ^$:表示空行

⑤ ^[[:space:]]*$:空白行,如tab、换行、回车

⑥ \< 或 \b:词首锚定,用于单词模式的左侧(连续的数字,字母,下划线都算单词内部)

⑦ \> 或 \b:词尾锚定,用于单词模式的右侧

⑧ \<PATTERN\>:匹配整个单词

示例:

bash 复制代码
[root@localhost opt]# vim file.txt
root$
123root456$
123ro456ot$
abc  cba$
efg  ghe$
$                      #空行
^I^I$                  #TabTab
:set list              #显示控制字符

[root@localhost opt]# cat file.txt | grep "^a"              #过滤以a开头行
abc  cba
[root@localhost opt]# cat file.txt | grep "a$"              #过滤以a结尾行
abc  cba
[root@localhost opt]# cat file.txt | grep "^root$"          #过滤只有root字符的行
root
[root@localhost opt]# cat file.txt | grep "^$"              #过滤空行        
#显示空行
[root@localhost opt]# cat file.txt | grep "^[[:space:]]*$"  #过滤空白行
#显示空行		
#显示空行
[root@localhost opt]# cat file.txt | grep "g\b"             #过滤以g结尾的词,efg
efg  ghe
[root@localhost opt]# cat file.txt | grep "\bc"             #过滤以c开头的词,cba
abc  cba
[root@localhost opt]# cat file.txt | grep "\broot\b"        #过滤单词root
root

5. 分组和引用

圆括号( ):用于分组,可以将多个字符作为一个整体进行处理

反向引用:使用\n(n 为数字)来引用分组中匹配的内容,如:\1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符

示例:

bash 复制代码
[root@localhost ~]# echo abccc | grep "abc\{3\}"
abccc
[root@localhost ~]# echo abccc | grep "\(abc\)\{3\}";echo $?
1
[root@localhost ~]# echo abcabcabccc | grep "\(abc\)\{3\}"
abcabcabccc                       #abcabcabc高亮

[root@localhost ~]# echo 1abc |grep  "1\|2abc"
1abc                              #1高亮
[root@localhost ~]# echo 1abc |grep  "\(1\|2\)abc"
1abc                              #1abc高亮

应用:过滤ens33ip地址

bash 复制代码
[root@localhost ~]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.190.100  netmask 255.255.255.0  broadcast 192.168.190.255
        inet6 fe80::e743:cb44:9825:6230  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:e1:cb:86  txqueuelen 1000  (Ethernet)
        RX packets 781  bytes 58624 (57.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 386  bytes 38313 (37.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
[root@localhost ~]# ifconfig ens33 | grep netmask | grep -o '\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}' | head -1
#显示被匹配到的字符串,表示匹配1-9之间任意数组成的1位、2位或3位数+点,{1,3}表示出现1-3次;
192.168.190.100
[root@localhost ~]# ifconfig ens33 |grep netmask | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1
192.168.190.100
#-E支持扩展正则表达式

6. 扩展正则表达式

① 使用方法

grep -E或者egrep 默认使用的是扩张正则表达式

② 表示次数

*:匹配前面字符任意次

?:0或1次

+:1次或多次

{n}:匹配n次

{m,n}:至少m,至多n次

{,n}:匹配前面的字符至多n次,<=n,n可以为0

{n,}:匹配前面的字符至少n次,<=n,n可以为0

③ 表示分组

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

| 或者

a|b #a或b

C|cat #C或cat

(C|c)at #Cat或cat

二、文本三剑客

1. grep

1.1 定义

grep是Linux系统中一个常用的文本搜索工具,用于在文件中查找指定的字符串或匹配某个正则表达式的文本行。

1.2 语法

bash 复制代码
grep [选项]... 查找条件 目标文件

1.3 选项

|-------------|------------------------------------|
| 选项 | 说明 |
| -m | -m n,匹配n次(行)后停止 |
| -v | 显示不被pattern匹配到的行,即取反 |
| -i | 忽略字符大小写 |
| -n | 显示匹配的行号 |
| -c | 统计匹配的行数 |
| -o | 仅显示匹配到的字符串 |
| -q | 静默模式,不输出任何信息 ,常用于脚本 |
| -A | after,后#行 |
| -B | before,前#行 |
| -C | context,前后各#行 |
| -e | 实现多个选项间的逻辑or关系 |
| -w | 匹配整个单词 |
| -E | 使用ERE,相当于egrep |
| -F | 不支持正则表达式,相当于fgrep |
| -f | file根据模式文件,处理两个文件相同内容 把第一个文件作为匹配条件 |
| -r | 递归目录,但不处理软链接,开始搜索目录 |
| -R | 递归目录,但处理软链接 |
| -color=auto | 对匹配到的文本着色显示 |

1.4 示例

bash 复制代码
[root@localhost opt]# cat file.txt 
a 1
a 2
a 3
[root@localhost opt]# cat file.txt | grep -m 2 a   #以a为过滤条件匹配到第二行停止
a 1
a 2

[root@localhost opt]# cat file.txt 
a 1
A 2
b 3
[root@localhost opt]# cat file.txt | grep -i a     #以a为过滤条件忽略字符大小写
a 1
A 2

[root@localhost opt]# cat file.txt
abc123
Aabc12
[root@localhost opt]# cat file.txt | grep -o abc   #仅显示匹配到的字符串abc
abc
abc

[root@localhost ~]# grep -e root -e bash /etc/passwd #包含root或者包含bash的行
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
fql:x:1000:1000:fql:/home/fql:/bin/bash

2. sed

2.1 定义

sed是一个流式文本编辑器,它可以对文本文件进行各种编辑操作。它主要用于对文本文件进行替换、插入、删除、打印等操作,它可以通过管道符(|)将多个命令串联起来使用。

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

2.2 通式

bash 复制代码
sed [option]... 'script;script;...' [input  file...]
     选项         '自身脚本语法'         支持标准输入管道或者文件
#如果不知道语法可以写' ',' ' 文件则是打印文件内容

2.3 选项

|---------|---------------------|
| 选项 | 说明 |
| -n | 不输出模式空间内容到屏幕,即不自动打印 |
| -e | 多点编辑 |
| -f FILE | 从指定文件中读取编辑脚本 |
| -r,-E | 使用扩展正则表达式 |
| -i.bak | 备份文件并原处编辑 |

注意:

  • -ir:不支持
  • -i -r:支持
  • -ri:支持
  • -ni:会清空文件

2.4 脚本格式(脚本语法)

脚本格式由'地址+命令'组成。

2.4.1 地址了解

① 如果不给地址,即对全文进行处理(比如行号)

bash 复制代码
[root@localhost ~]# seq 3 | sed ' '    #默认将输入内容打印出来,系统自带自动打印
1                                      #支持管道符
2
3
[root@localhost ~]# sed ' ' /etc/passwd |wc -l     #查看文件内容
41
[root@localhost ~]# sed ' ' < /etc/passwd |wc -l   #支持重定向
41

② 单地址:指定的行,$代表最后一行

bash 复制代码
[root@localhost ~]# sed 'p' /etc/passwd | wc -l   
82
#p代表打印内容,因为没有规定范围' '默认也会打印一遍,所以这里会造成打印两倍
[root@localhost ~]# sed -n 'p' /etc/passwd | wc -l
41
#-n代表关闭自动打印
[root@localhost ~]# sed -n '2p' /etc/passwd  #打印文件的第二行(单地址写法)
bin:x:1:1:bin:/bin:/sbin/nologin              
[root@localhost ~]# sed -n '$p' /etc/passwd  #打印文件的最后一行(单地址写法)
fql:x:1000:1000:fql:/home/fql:/bin/bash
[root@localhost ~]# sed '2q' /etc/passwd       #到第二行退出,也就是打印1到2行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

③ 地址范围:

  • #,#:#从#行到第#行,3,6 从第3行到第6行
  • #,+#:#从#行到+#行,3,+4 表示从3行到第7行
  • /pat1/,/pat2/:第一个正则表达式和第二个正则表达式之间的行
  • #,/pat/:从#号行为开始找到pat为止 (可能有bug)
  • /pat/,#:找到#号个pat为止

示例:

bash 复制代码
[root@localhost ~]# sed -n '1,2p' /etc/passwd        #打印一到二行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@localhost ~]# sed -n '1,+2p' /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
[root@localhost opt]# cat file.txt 
a123
b456
c789
d000
[root@localhost opt]# sed -n '/^a/,/^b/p' file.txt   #从a开头行到b开头行
a123
b456

应用:提取时间段的日志内容

bash 复制代码
[root@localhost log]# tail messages 
Jan 29 22:38:17 localhost chronyd[708]: Source 2001:1600:4:1::123 replaced with 119.28.183.184
Jan 29 22:40:01 localhost systemd: Started Session 18 of user root.
Jan 29 22:40:01 localhost systemd: Starting Session 18 of user root.
Jan 29 22:43:42 localhost chronyd[708]: Selected source 119.28.183.184
Jan 29 22:50:01 localhost systemd: Started Session 19 of user root.
Jan 29 22:50:01 localhost systemd: Starting Session 19 of user root.
Jan 29 23:00:01 localhost systemd: Started Session 20 of user root.
Jan 29 23:00:01 localhost systemd: Starting Session 20 of user root.
Jan 29 23:01:01 localhost systemd: Started Session 21 of user root.
Jan 29 23:01:01 localhost systemd: Starting Session 21 of user root.
[root@localhost log]# sed -n '/Jan 29 22:38:17/,/Jan 29 22:43:42/p' messages 
Jan 29 22:38:17 localhost chronyd[708]: Source 2001:1600:4:1::123 replaced with 119.28.183.184
Jan 29 22:40:01 localhost systemd: Started Session 18 of user root.
Jan 29 22:40:01 localhost systemd: Starting Session 18 of user root.
Jan 29 22:43:42 localhost chronyd[708]: Selected source 119.28.183.184
#提取Jan 29 22:38:17到Jan 29 22:43:42时间段的日志内容

④ 步进:~

  • 1~2 奇数行
  • 2~2 偶数行

示例:

bash 复制代码
[root@localhost ~]# seq 4 | sed -n '1~2p'  #从1开始,步长为2,即每次加2,奇数
1
3
[root@localhost ~]# seq 4 | sed -n '2~2p'  #从2开始,步长为2,即每次加2,偶数
2
4
[root@localhost ~]# seq 4 | sed -n '1~2!p' #取反,也是打印偶数
2
4
[root@localhost ~]# seq 4 | sed -n 'n;p'   
2
4
#高级用法,将第一个数放入第二空间(第一个是匹配空间)里不输出,打印处理下一行。当处理完后1,2行后,这两行已经不存在了,处理第3行,依然将第3行放入第二空间,打印下一行。
[root@localhost ~]# seq 4 | sed -n '2,${n;p}'
3
#与上面同理,从2开始,打印下一行,代表奇数行
2.4.2 命令了解

|--------------|--------------------------|
| 命令 | 说明 |
| p | 打印当前模式空间内容,追加到默认输出之后 |
| Ip | 忽略大小写输出 |
| d | 删除模式空间匹配的行,并立即启用下一轮循环 |
| a [\]text | 在指定行后面追加文本,支持使用\n实现多行追加 |
| i [\]text | 在行前面插入文本 |
| c [\]text | 替换行为单行或多行文本 |
| w file | 保存模式匹配的行至指定文件 |
| r file | 读取指定文件的文本至模式空间中匹配到的行后 |
| = | 为模式空间中的行打印行号 |
| ! | 模式空间中匹配行取反处理 |
| q | 结束或退出sed |

示例:

bash 复制代码
[root@localhost ~]# seq 4 | sed '3d'    #删除第三行(只是删输出的内容,实际依然存在)
1
2
4
[root@localhost ~]# sed 4 | sed -i '3d'       #真实删除原内容 
[root@localhost ~]# sed 4 | sed -i.bak '3d'   #删除之前先备份

[root@localhost ~]# seq 3 | sed  '2a22'       #在第二行后添加22
1
2
22
3
[root@localhost ~]# seq 3 | sed  '2a22\n33'   #下一行追加33
1
2
22
33
3

[root@localhost ~]# seq 3 > file.txt
[root@localhost ~]# cat file.txt 
1
2
3
[root@localhost ~]# sed -i '3ccc' file.txt    #替换第三行内容为cc
[root@localhost ~]# cat file.txt 
1
2
cc

[root@localhost opt]# cat file.txt 
a123
b456
[root@localhost opt]# sed -n '1,2wbbb.txt' file.txt  
#将file.txt文件的一到二行保存到bbb.txt文件中
[root@localhost opt]# ls
bbb.txt  file.txt  rh

[root@localhost opt]# cat a.txt 
a
b
[root@localhost opt]# cat b.txt 
c
d
[root@localhost opt]# sed '2r b.txt' a.txt  #将b.txt插入到a.txt第二行后
a
b
c
d

2.5 搜索替代

s/pattern/string/修饰符 查找替换,即:s/旧内容/新内容/修饰符。支持使用其它分隔符,可以是其它形式:s@@@,s###。

替换修饰符:

g:行内全局替换

p:显示替换成功的行

w /PATH/FILE:将替换成功的行保存至文件中

I,i:忽略大小写

bash 复制代码
[root@localhost ~]# cat /etc/selinux/config

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing
......
[root@localhost ~]# sed -i.bak 's/SELINUX=enforcing/SELINUX=enforcing/' /etc/selinux/config
[root@localhost ~]# cat /etc/selinux/config | grep SELINUX=enforcing
SELINUX=enforcing
#修改主配置文件,SELinux设为enforcing强制模式

[root@localhost opt]# cat file.txt 
root
rcot
rptt
r\ft
[root@localhost opt]# sed 's/r..t/&er/g' file.txt  
#&代表前面匹配到的内容,指代之前找到的内容
rooter
rcoter
rptter
r\fter

注意:新内容一定是固定的字符串

2.6 分组替换

可以使用分组机制来进行文本替换操作分组可以将匹配的部分文本捕获并在替换操作中使用,注意只有扩展表达式才可以实现分区替换。允许在模式匹配和替换中引用已匹配的文本。使用分组和后向引用,可以在替换文本中包含匹配到的特定部分。

语法:

bash 复制代码
sed -nr 's/正则匹配/\1/p'

示例:

bash 复制代码
[root@localhost opt]# echo 123abcDEF | sed -nr 's/123(abc)DEF/\1/p'
abc
[root@localhost opt]# echo 123abcDEF | sed -nr 's/123(abc)(DEF)/\2/p'
DEF
#\1\2代表引用前面括号里的内容,固定格式
[root@localhost opt]# echo 123abcDEF | sed -nr 's/(123)(abc)(DEF)/\2\1\3/p'
abc123DEF

应用:sed命令提取网卡ip地址

bash 复制代码
[root@localhost ~]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.190.100  netmask 255.255.255.0  broadcast 192.168.190.255
        inet6 fe80::e743:cb44:9825:6230  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:e1:cb:86  txqueuelen 1000  (Ethernet)
        RX packets 12577  bytes 931901 (910.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5565  bytes 1298791 (1.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@localhost ~]# ifconfig ens33 | sed -rn '2s/.*inet (.*) netmask.*/\1/p'
192.168.190.100
#sed命令提取网卡ip地址(注意空格)
[root@localhost opt]# ifconfig ens33 | sed -rn '2s/.*inet ([0-9.]+).*/\1/p'
192.168.190.100
#同样可以实现:(0-9数字.)一个以上
[root@localhost opt]# echo /etc/sysconfig/network-scripts/ifcfg-ens33 | sed -nr 's#.*/(.*)#\1#p'
ifcfg-ens33
#提取ifcfg-ens33

应用:提取文件的版本号

bash 复制代码
[root@localhost opt]# cat test
ant-1.9.7.jar
ant-launcher-1.9.7.jar
antlr-2.7.7.jar
antlr-runtime-3.4.jar
aopalliance-1.0.jar
[root@localhost opt]# cat test | sed -nr 's/.*-(.*).jar/\1/p'
1.9.7
1.9.7
2.7.7
3.4
1.0
#提取文件的版本号
#使用cat test | grep -e "[0-9]\+\."也可以提取

应用:提取文件属性以数字形式显示

bash 复制代码
[root@localhost opt]# stat test
  文件:"test"
  大小:95        	块:8          IO 块:4096   普通文件
设备:fd00h/64768d	Inode:68786720    硬链接:1
权限:(0644/-rw-r--r--)  Uid:(    0/    root)   Gid:(    0/    root)
环境:unconfined_u:object_r:usr_t:s0
最近访问:2024-01-30 13:48:56.384782770 +0800
最近更改:2024-01-30 13:48:53.689798114 +0800
最近改动:2024-01-30 13:48:53.690798109 +0800
创建时间:-
[root@localhost opt]# stat test | sed -nr '4s/.*([0-9]{4}).*/\1/p'
0644
#提取文件属性以数字形式显示

2.7 调用变量

要在"sed"命令中使用变量,您需要使用双引号来引用变量,不能使用单引号,并使用符号&来引用变量名。其中有一个-e选项代表多点编辑,类似grep用法,是"或"的意思。

示例:

bash 复制代码
[root@localhost opt]# name=root
[root@localhost opt]# sed -n "/$name/p" passwd     #双引号可以查询
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost opt]# sed -n '/'$name'/p' passwd   #两单引号也可以查询
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

2.8 修改配置文件

sed可以用于修改配置文件,它可以从标准输入或文件中读取文本,并对其进行编辑和转换。

示例:不打开配置文件,修改httpd80端口

bash 复制代码
[root@localhost ~]# sed -i.bak 's/^Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf
[root@localhost ~]# grep 8080 /etc/httpd/conf/httpd.conf
Listen 8080

2.9 修改网卡名

bash 复制代码
[root@localhost ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
[root@localhost ~]# sed -ri.bak '/^GRUB_CMDLINE_LINUX/s#(.*)"#\1 net.ifnames=0"#' /etc/default/grub
[root@localhost ~]# grep GRUB_CMDLINE_LINUX  /etc/default/grub
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet net.ifnames=0"

3. awk

3.1 定义和格式

awk是一个功能强大的编辑工具,逐行读取输入文本,默认以空格或tab键作为分隔符作为分隔,并按模式或者条件执行编辑命令。 用于从文件、管道或标准输入中读取文本,并根据用户指定的模式和操作进行处理。

前面提到sed命令常用于一整行的处理,而awk比较倾向于将一行分成多个"字段"然后再进行处理,且默认情况下字段的分隔符为空格或tab键。awk执行结果可以通过print的功能将字段数据打印显示。sed是行处理工具,不能直接处理列数据,需要分组后向引用,而awk可以直接处理列数据。

格式:

bash 复制代码
awk [options]   'program' var=value   file...
      选项         语法      值       文件,支持标准输入、输出
awk         'pattern { action }'      input_file

语法program通常是被放在单引号中,可分为pattern和action:

pattern:用于匹配输入文本的模式。可以使用正则表达式或字符串进行匹配。如果省略模式,则默认匹配所有行。

action:对数据进行处理,放在{}内指明,在满足模式的情况下要执行的动作。可以是单个命令或多个命令组合。如果省略动作,则默认打印整行。处理动作默认写print即可。

3.2 常见选项和执行流程

常见选项:

-F:"分隔符" 指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符

-v:var=value 变量赋值

执行流程:

① 执行BEGIN{action;... }{print}语句块中的语句

② 从文件或标准输入(stdin)读取一行,然后执行pattern{ action;... }语句块,它逐行扫描文件,

从第一行到最后一行重复这个过程,直到文件全部被读取完毕。

③ 当读至输入流末尾时,执行END{action;...}语句块
BEGIN 语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
END 语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

示例:

bash 复制代码
[root@localhost opt]# cat file.txt 
1:a
2:b
3:c
[root@localhost opt]# awk -F: 'BEGIN{print "123abc"}{print}' file.txt 
123abc
1:a
2:b
3:c
#以冒号为分割符,awk开始从输入流中读取行之前执行打印123abc,即在表头添加123abc

3.3 基本用法

bash 复制代码
[root@localhost ~]#  echo 123 | awk '{print}'
123                                            #pattern什么都不写,打印全部
[root@localhost opt]# cat file.txt 
1234
abCD
[root@localhost opt]# cat file.txt | awk '/[a-z]/{print}'
abCD                                           #匹配包含小写字母的行
[root@localhost opt]# awk '{print "hello"}' < file.txt 
hello                                          #文件有几行打印几次hello
hello
hello
[root@localhost ~]# awk 'BEGIN{print 1+2}' 
3                                              #支持运算

[root@localhost opt]# cat file.txt 
1:a
2:b
[root@localhost opt]# awk -F: '{print $1,$2}' file.txt
1 a                                            #默认以空格为分隔符
2 b
[root@localhost opt]# wk -F: '{print $1"@@"$2}' file.txt
1@@a                                           #指定@@作为分隔符,打印第一列和第二列
2@@b

小结:

bash 复制代码
[root@localhost ~]#awk -F: '{print $0}' /etc/passwd
#$0代表全部元素
[root@localhost ~]#awk -F: '{print $1}' /etc/passwd
#代表第一列
[root@localhost ~]#awk -F: '{print $1,$3}' /etc/passwd
#代表第一第三列
[root@localhost ~]#awk '/^root/{print}' passwd 
#已root为开头的行
[root@localhost ~]#grep -c "/bin/bash$" passwd 
#统计当前已/bin/bash结尾的行
2

3.4 常规应用

提取磁盘使用率:

bash 复制代码
[root@localhost ~]# df -h
文件系统                 容量  已用  可用 已用% 挂载点
/dev/mapper/centos-root   50G  3.5G   47G    7% /
devtmpfs                 897M     0  897M    0% /dev
tmpfs                    912M     0  912M    0% /dev/shm
tmpfs                    912M  9.0M  903M    1% /run
tmpfs                    912M     0  912M    0% /sys/fs/cgroup
/dev/sda1                5.0G  179M  4.9G    4% /boot
tmpfs                    183M   12K  183M    1% /run/user/42
tmpfs                    183M     0  183M    0% /run/user/0
[root@localhost ~]# df | awk '{print $5}'| cut -d% -f1 |tail -n +2
7
0
0
1
0
4
1
0
df | awk -F"( +|%)" '{print $5}' | tail -n +2 以空格或者%为分隔符
df | awk -F"[ %]+" '{print $5}' | tail -n +2
df | awk -F"[[:space:]]+|%" '{print $5}' | tail -n +2
#均可以实现提取

提取网卡ip地址:

bash 复制代码
[root@localhost opt]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.190.100  netmask 255.255.255.0  broadcast 192.168.190.255
[root@localhost opt]# ifconfig ens33| sed -n '2p' | awk '{print $2}'
192.168.190.100
[root@localhost opt]# ifconfig ens33 | awk '/netmask/{print $2}'
192.168.190.100
#找到第二行第二列

3.5 常见的内置变量

awk提供了一些内置变量,用于方便地处理文本数据。

  • FS :指定每行文本的字段分隔符,缺省默认为空格或制表符(tab)。与 "-F"作用相同 -v "FS=:"
  • OFS:输出时的分隔符
  • NF:当前处理的行的字段个数
  • NR:当前处理的行的行号(序数)
  • $0:当前处理的行的整行内容
  • $n:当前处理行的第n个字段(第n列)
  • FILENAME:被处理的文件名
  • RS:行分隔符。awk从文件上读取资料时,将根据RS的定义就把资料切割成许多条记录,而awk一次仅读入一条记录进行处理。预设值是\n

示例:FS

bash 复制代码
[root@localhost opt]# awk -F: {print} file.txt 
1:a
2:b
[root@localhost opt]# awk -F: '{print $1}' file.txt 
1
2
[root@localhost opt]# awk -v FS=: '{print $1}' file.txt 
1
2
[root@localhost opt]# awk -v FS=: '{print $1FS$2}' file.txt  #调用变量名FS不需要$
1:a
2:b
[root@localhost opt]# fs=:
[root@localhost opt]# awk -v FS=$fs '{print $1FS$2}' file.txt 
1:a                                                          #支持定义变量传给FS
2:b

示例OFS:

bash 复制代码
[root@localhost opt]# cat file.txt
1:a
2:b
[root@localhost opt]# awk -v FS=":" -v OFS="==" '{print $1,$2}' file.txt 
1==a
2==b

示例RS:默认是已 /n (换行符)为一条记录的分隔符

bash 复制代码
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo $PATH | awk -v RS=":" '{print $1}'   #以:为换行符
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin

[root@localhost opt]# echo $PATH | sed -rn 's#/(.*)#\1#p' | awk -v RS='/' '{print $1}'| tr -d :
usr
local
sbin
usr
local
bin
usr
sbin
usr
bin
root
bin
#以/为分隔符
echo $PATH | sed -rn 's#/(.*)#\1#p' | awk -v RS='/|:/' '{print $1}'
这样也可以实现

示例NF:代表字段的个数

bash 复制代码
[root@localhost opt]# cat file.txt 
1:a
2:b
[root@localhost opt]# awk -F: '{print NF}' file.txt 
2
2

应用:过滤磁盘占用百分比

bash 复制代码
[root@localhost opt]# df
文件系统                   1K-块    已用     可用 已用% 挂载点
/dev/mapper/centos-root 52403200 3625008 48778192    7% /
devtmpfs                  917604       0   917604    0% /dev
tmpfs                     933524       0   933524    0% /dev/shm
tmpfs                     933524    9216   924308    1% /run
tmpfs                     933524       0   933524    0% /sys/fs/cgroup
/dev/sda1                5232640  182372  5050268    4% /boot
tmpfs                     186708      12   186696    1% /run/user/42
tmpfs                     186708       0   186708    0% /run/user/0
[root@localhost opt]# df | awk  '{print $(NF-1)}' | tr -d % | tail -n +2
7                                        #$NF表示最后一个字段
0
0
1
0
4
1
0

示例NR:行号,类似于wc -l

bash 复制代码
[root@localhost opt]# cat file.txt 
a:aa
b:bb
3:33
[root@localhost opt]# awk -F: '{print NR}' file.txt 
1
2
3

应用:打印出当前系统普通用户同时按倒序排列

bash 复制代码
[root@localhost ~]# awk -F:  '$3>=1000{print $3,$1}' /etc/passwd | sed -nr 's/(.*) (.*)/\2 \1/p'
nfsnobody 65534
fql 1000

[root@localhost ~]# cat /etc/passwd | sort -t: -k3 -n | tail -n1
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
#可以找打uid最大的用户,-t也是指定分隔符,-k3代表第三列

示例FNR:把两个文件分开重新计数

bash 复制代码
[root@localhost opt]# cat file1.txt 
123
456
[root@localhost opt]# cat file2.txt 
abc
[root@localhost opt]# awk '{print NR}' file1.txt file2.txt     #NR会合并计数
1
2
3
[root@localhost opt]# awk '{print FNR}' file1.txt file2.txt    #FNR分开计数
1
2
1

3.6 自定义变量

bash 复制代码
[root@localhost ~]# awk -v test='hello' 'BEGIN{print test}'
hello

printf(了解)

  • %s:显示字符串
  • %d, %i:显示十进制整数
  • %f:显示为浮点数
  • %e, %E:显示科学计数法数值
  • %c:显示字符的ASCII码
  • %g, %G:以科学计数法或浮点形式显示数值
  • %u:无符号整数
  • %%:显示%自身
bash 复制代码
[root@localhost opt]# cat file1.txt 
123:a
456:b
[root@localhost opt]# awk -F: '{printf "%s\n",$1}' file1.txt 
123
456
#printf虽然功能强大,但是不自动换行,需要\n换行

3.7 模式PATTERN

awk '模式{处理动作}',PATTERN:根据pattern条件,过滤匹配的行,再做处理。

3.7.1 模式为空

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

bash 复制代码
[root@localhost opt]# cat file1.txt 
123:a
456:b
[root@localhost opt]# awk -F: '{print $1,$2}' file1.txt 
123 a
456 b
3.7.2 正则匹配

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

bash 复制代码
[root@localhost opt]# cat file1.txt 
123:a 000
456:b 789
[root@localhost opt]# awk  '/^4/{print $1}' file1.txt 
456:b                     
#匹配以4开头的行的第一列
3.7.3 line ranges:行范围

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

算术操作符

  • x+y, x-y, x*y, x/y, x^y, x%y
  • -x:转换为负数
  • +x:将字符串转换为数值

比较操作符:

  • ==, !=, >, >=, <, <=
  • #####逻辑
  • 与:&&,并且关系
  • 或:||,或者关系
  • 非:!,取反
bash 复制代码
[root@localhost opt]# cat file1.txt 
a:1 4
b:2 5
c:3 6
[root@localhost opt]# awk 'NR==1{print}' file1.txt 
a:1 4
[root@localhost opt]# awk 'NR==1,NR==3{print}' file1.txt  #打印出1到3行
a:1 4                           
b:2 5
c:3 6
3.7.4 /pat1/,/pat2/ 案例:找到10:00到11:00之间的日志
bash 复制代码
sed -nr '/10/,/11/p' 文件名    #sed参考2.4.1相关内容介绍
awk '/10/,/11/'      文件名
[root@localhost log]# awk '/Jan 30 21:34:24/,/Jan 30 21:34:26/' messages 
Jan 30 21:34:24 localhost avahi-daemon[685]: Registering new address record for fe80::e743:cb44:9825:6230 on ens33.*.
Jan 30 21:34:26 localhost setroubleshoot: SELinux is preventing /usr/sbin/ip from read access on the file /run/vmware-active-nics. For complete SELinux messages run: sealert -l 3c1805c2-5564-44bb-ab25-c32fb01e593a
#找到an 30 21:34:24到Jan 30 21:34:26日志
3.7.5 关系表达式

在awk中,关系表达式用于比较两个值,并返回一个布尔值(真或假)。这些表达式通常用于控制流程和过滤数据,关系表达式结果为"真"才会被处理。

真:结果为非0值,非空字符串

假:结果为空字符串或0值

bash 复制代码
[root@localhost ~]# seq 5 | awk 'n++'
2
3
4
5
#结果为第一行不打印
第一行:n=0,awk 0为假,则第一行不打印
第二行:n=1,awk 1为真,则打印第二行
第三行:n=2,awk 2为真,则打印第三行
第四行:n=3,awk 3为真,则打印第四行
第五行:n=4,awk 4为真,则打印第五行
[root@localhost ~]# seq 5 | awk '!n++'    #取反,只打印第一行
1 
[root@localhost ~]# seq 5 | awk '!0'      #都打印
1
2
3
4
5
[root@localhost ~]# seq 5 | awk 'i=!i'    #打印奇数行
1 
3
5
第一行:i未赋值,为假,!i取反为真,打印第一行
第二行:i为真,!i取反为假,第二行不打印
第三行:i为假,!i取反为真,打印第三行
第四行:i为真,!i取反为假,第四行不打印
第五行:i为假,!i取反为真,打印第五行
[root@localhost ~]# seq 5 | awk '!(i=!i)' #打印偶数行
2
4
seq 5 |awk -v i=1 'i=!i'也可实现打印偶数行

3.8 条件判断

在awk中,条件判断通常用于控制程序的流程,以便根据特定条件执行不同的操作。条件判断通常与if语句结合使用。

格式:awk 选项 '模式 {actions}',条件判断写在 actions里

if语句:awk的if语句也分为单分支、双分支和多分支

单分支为if(判断条件){执行语句}

双分支为if(判断条件){执行语句}else{执行语句}

多分支为if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句}else if(判断条件){执行语句

示例:

bash 复制代码
[root@localhost opt]# awk  '{if($2>=10){print $1,$2}else{print $1}}' file.txt 
a
b
c 10
d 20
#以空格为分隔符,如果第二列大于等于10,打印第一列和第二列,否则只打印第一列

3.9 循环:for | while

在awk中,可以使用循环语句来重复执行一组操作,直到满足特定条件为止。awk支持for循环和while循环。

格式:condition:条件;statement:语句

for(expr1;expr2;expr3) {statement;...}

for(variable assignment;condition;iteration process) {for-body}

for(var in array) {for-body}

示例:1-100求和

bash 复制代码
[root@localhost ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum+=i};print sum}'
5050

3.10 数组

在awk中,数组是一种非常有用的数据结构,用于存储和操作数据。awk中的数组是关联数组,也就是说它们可以使用字符串作为索引。

awk数组特性:

(1)awk的数组是关联数组(即key/value方式的hash数据结构),索引下标可为数值(甚至是负数、小数等),也可为字符串

① 在内部,awk数组的索引全都是字符串,即使是数值索引在使用时内部也会转换成字符串

② awk的数组元素的顺序和元素插入时的顺序很可能是不相同的

(2) awk数组支持数组的数组

3.10.1 访问、赋值数组元素

可以使用数组的索引来访问和修改数组元素的值

bash 复制代码
[root@localhost ~]# awk 'BEGIN{name["a"]="zhangsan";name["b"]="lisi";name["c"]="wangwu";for(i in name)print name[i]}'
zhangsan
lisi
wangwu
3.10.2 数组长度

awk提供了 length() 函数来获取数组的元素个数,它也可以用于获取字符串的字符数量。还可以获取数值转换成字符串后的字符数量。

示例:

bash 复制代码
[root@localhost ~]# awk 'BEGIN{a[1]="zhangsan";print a[1];print length(a)}'
zhangsan
1
3.10.3 遍历数组

使用for循环来迭代数组中的元素

格式:for(var in array) {for-body}

示例:

bash 复制代码
[root@localhost ~]# ss -natp | awk 'NR!=1{print $1}' | sort |uniq -c
      1 ESTAB
     11 LISTEN
[root@localhost ~]# ss -natp | awk 'NR!=1{a[$1]++}END{for(i in a)print i,a[i]}'
LISTEN 11
ESTAB 1
3.10.4 实际案例

去除重复行:

bash 复制代码
[root@localhost opt]# cat file.txt 
a
a
b
b
c
[root@localhost opt]# awk '!line[$0]++' file.txt 
a
b
c
#line数组,$0,将整行作为参数写入
第一行a,一开始未赋值为假,取反为真,打印a,+1依然为真
第二行a,真取反为假,不打印,+1为真,即下次在遇到a一直不打印
第三行b,一开始未赋值为假,取反为真,打印b,+1依然为真
下面同理

3.11 awk脚本

将awk程序写成脚本,直接调用或执行

示例:

bash 复制代码
#!/bin/awk -f
{if($3>=1000){print $1,$3}}

[root@localhost opt]# awk -F: -f passwd.awk /etc/passwd
nfsnobody 65534
fql 1000
相关推荐
前端李易安16 小时前
正则表达式应用场景与常用正则验证方法汇总
正则表达式
HoneyMoose19 小时前
正则表达式匹配英文字符
正则表达式
人生の三重奏1 天前
正则表达式(补充)
正则表达式
沟沟里的农民2 天前
【正则表达式】粗浅学习
正则表达式
一路向北_.2 天前
CTFshow 命令执行 web29~web36(正则匹配绕过)
web安全·正则表达式·ctfshow
神的孩子都在歌唱4 天前
正则表达式中的贪婪模式和非贪婪模式
数据库·mysql·正则表达式
Amo Xiang5 天前
2024 Python3.10 系统入门+进阶(十六):正则表达式
开发语言·python·正则表达式
激动的兔子5 天前
正则表达式的使用示例--Everything文件检索批量重命名工具
正则表达式·everything
学习使我快乐015 天前
Web APIs 6:正则表达式
前端·javascript·正则表达式
kill bert5 天前
第18周 2-正则表达式
正则表达式