Bootstrap

正则表达式基础知识及grep、sed、awk常用命令


文章目录


前言

正则表达式是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。可以在文本中查找、替换、提取和验证特定的模式以及在日常脚本编写中也是不可缺少的一部分。因此本篇文章主要以正则表达式的基础使用和三剑客命令的使用做出一次分享.


一、正则表达式元字符和特性

1. 字符匹配

普通字符: 
	普通字符按照字面意义进行匹配,例如匹配字母 "a" 将匹配到文本中的 "a" 字符。

元字符: 
	元字符具有特殊的含义,例如 \d 匹配任意数字字符,\w 匹配任意字母数字字符,. 匹配任意字符(除了换行符)等。

2. 量词

字符含哟
*匹配前面的模式零次或多次
+匹配前面的模式一次或多次
?匹配前面的模式零次或一次
{n}匹配前面的模式恰好 n 次
{n,}匹配前面的模式至少 n 次
{m,n}匹配前面的模式至少 m 次且不超过 n 次

3. 字符类

字符含义
[]匹配括号内的任意一个字符。例如,[abc] 匹配字符 “a”、“b” 或 “c”
[^]匹配除了括号内的字符以外的任意一个字符。例如,[^abc] 匹配除了字符 “a”、“b” 或 “c” 以外的任意字符

4. 边界匹配

字符含义
^匹配字符串的开头
$匹配以字符串结尾
\b匹配单词边界,如"\blike"不会匹配alike,但是会匹配liker
\B匹配非单词边界

5. 分词和捕获

字符含义
( )用于分组和捕获子表达式。标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )
(?: )用于分组但不捕获子表达式

6. 特殊字符

字符含义
\转义字符,用于匹配特殊字符本身
. 点匹配任意字符(除了换行符)
管道符用于指定多个模式的选择

7. 位置锚定

字符含义
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\t匹配一个制表符
\n匹配一个换行符
\r匹配一个回车符
\f匹配一个换页符
\d匹配一个数字字符。等价于 [0-9]
\D匹配一个非数字字符。等价于 [^0-9]
\w匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’
\W匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’
[标记一个中括号表达式的开始。要匹配 [,请使用 [
{标记限定符表达式的开始。要匹配 {,请使用 {

二、grep常用参数

1. -n额外输出行号

代码如下(示例):

[root@localhost ~]# grep -n "青" test.txt
1:1     province    省份  青海省
3:3     subject_no  主体备案号   青ICP备11000289号
4:4     addr    注册地址    青海省西宁市城中区南关街138号
7:7     site_no 网站备案/许可证号   青ICP备11000289号-2

2. -v 排除匹配的行

代码如下(示例):

[root@localhost ~]# grep -v "青" test.txt
2     domain  域名或者ip  tianfengyinlou.cn
5     check_time  备案时间, 时间对象  2011-06-23 16:38:00
6     update_time 更新时间, 毫秒级时间戳    1607414120745
8     site_url    站点/网站首页网址   www.tianfengyinlou.cn
9     comp_name   主办单位名称(公司名称)    西宁天丰银楼金银珠宝有限公司

3. -E 支持扩展正则匹配

代码如下(示例):
egrep等价于grep -E

[root@localhost ~]# grep -E '青海省|青ICP' text.txt
[root@localhost ~]# egrep '青海省|青ICP' text.txt                                                                            
1     province    省份  青海省
3     subject_no  主体备案号   青ICP备11000289号
4     addr    注册地址    青海省西宁市城中区南关街138号
7     site_no 网站备案/许可证号   青ICP备11000289号-2

4. -e进行多规则匹配搜索

代码如下(示例):

[root@localhost ~]# grep -e 2025 -e log o 
2025-01-16 08:32:45 - User accessed http://example.com/page1
2025-01-16 08:35:12 - Another request was made to https://secure-site.com/login
2025-01-16 08:36:25 - Error at http://example.com/error
2025-01-16 08:37:55 - API call to https://api.example.com/data?query=test&limit=10
2025-01-16 08:39:11 - User logged in at http://example.com/dashboard
2025-01-16 08:42:00 - Redirect to https://www.example.org/path/to/resource#anchor
2025-01-16 08:45:10 - Some other link without protocol: www.example.com
2025-01-16 08:47:21 - Contact page at http://example.com/contact
2025-01-16 08:50:50 - API failed at https://api.example.com/error?code=404
2025-01-16 08:53:00 - Please visit https://example.net/homepage for more info

5. -R 递归匹配目录中的文件内容

代码如下(示例):有时候,在一个目录中我们并不知道哪个文件内容包含我们想要的结果,此时,可以查找整个目录,输出匹配的文件名以及行记录

[root@localhost ~]# grep -R 青海 /export/wxd/
/export/test.txt:1     province    省份  青海省
/export/test.txt:4     addr    注册地址   青海省西宁市城中区南关街138号

6. -r递归地搜索目录及其子目录中的文件

代码如下(示例):有时候,在一个目录中我们并不知道哪个文件内容包含我们想要的结果,此时,可以查找整个目录,输出匹配的文件名以及行记录

[root@localhost ~]# grep -r --include="*.txt" "hello" /path/to/directory/
#使用 --include 选项来限制搜索的文件类型

7. -l只输出包含了匹配项的文件名(可以结合-R使用)

代码如下(示例):

[root@localhost ~]# grep -Rl 青 /export/sss
/export/sss/test.txt

8. -A 指定输出匹配行 后的额外行数

代码如下(示例):

[root@localhost ~]# grep -A1(额外行数) "ICP" test.txt 额外输出包含"ICP"行的后一行

3     subject_no  主体备案号   青ICP备11000289号
4     addr    注册地址   青海省西宁市城中区南关街138号
--
7     site_no 网站备案/许可证号   青ICP备11000289号-2
8     site_url    站点/网站首页网址   www.tianfengyinlou.cn

9. -B 指定输出匹配行 前的额外行数

代码如下(示例): 额外输出包含"ICP"行的前一行

[root@localhost ~]# grep -B1 "ICP" test.txt

2     domain  域名或者ip  tianfengyinlou.cn
3     subject_no  主体备案号   青ICP备11000289号
--
6     update_time 更新时间, 毫秒级时间戳    1607414120745
7     site_no 网站备案/许可证号   青ICP备11000289号-2

10. -C 指定输出匹配行 前后的额外行数

代码如下(示例):额外输出包含"ICP"行的前后各一行

[root@localhost ~]# grep -C1 "ICP" test.txt

2     domain  域名或者ip  tianfengyinlou.cn
3     subject_no  主体备案号   青ICP备11000289号
4     addr    注册地址   青海省西宁市城中区南关街138号
--
6     update_time 更新时间, 毫秒级时间戳    1607414120745
7     site_no 网站备案/许可证号   青ICP备11000289号-2
8     site_url    站点/网站首页网址   www.tianfengyinlou.cn

11. -i 忽略大小写

代码如下(示例):

[root@localhost ~]# grep -i user o.log
2025-01-16 08:32:45 - User accessed http://example.com/page1
2025-01-16 08:39:11 - User logged in at http://example.com/dashboard

12. -o 用于在匹配文本中仅输出匹配的部分,而不是整个行或文件

代码如下(示例):

[root@localhost ~]# grep -Eo 'https?://[^\"]+' o.log
http://example.com/page1
https://secure-site.com/login
http://example.com/error
https://api.example.com/data?query=test&limit=10
http://example.com/dashboard
https://www.example.org/path/to/resource#anchor
http://example.com/contact
https://api.example.com/error?code=404
https://example.net/homepage for more info

13. -q 用于if逻辑判断 安静模式,不打印任何标准输出

代码如下(示例):-q 用于if逻辑判断 安静模式,不打印任何标准输出。如果有匹配的内容则立即返回状态值0
如果存在返回值为0,不存在返回值为1

[root@localhost ~]# grep -q 'auth' /etc/pam.d/system-auth
[root@localhost ~]# echo $?
0
[root@localhost ~]# grep -q 'auth required pam_tally2.so' /etc/pam.d/system-auth
[root@localhost ~]# echo $?
1

14. -c只输出匹配行的计数

代码如下(示例):

[root@localhost ~]# grep -c "User" o
2

15. -s 不显示不存在或无匹配文本的错误信息

代码如下(示例):

[root@localhost ~]# grep -s "root" /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:996:992:Docker User:/var/lib/docker:/sbin/nologin

16. -? 同时显示匹配行的上下?行

代码如下(示例):配合-n参数使用

[root@localhost ~]# grep -n -2 "root" /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
2-bin:x:1:1:bin:/bin:/sbin/nologin
3-daemon:x:2:2:daemon:/sbin:/sbin/nologin
--
8-halt:x:7:0:halt:/sbin:/sbin/halt
9-mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10:operator:x:11:0:operator:/root:/sbin/nologin
11-games:x:12:100:games:/usr/games:/sbin/nologin
12-ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

17. -w精确匹配

代码如下(示例):必须和要查找的一致,才会返回true,否则返回false

[root@localhost ~]# grep -w User o 
2025-01-16 08:32:45 - User accessed http://example.com/page1
2025-01-16 08:39:11 - User logged in at http://example.com/dashboard

18. -P 启用 Perl 兼容正则表达式

代码如下(示例):

启用 Perl 兼容正则表达式,使 grep 能够支持更多功能,如零宽断言、非捕获分组、增强的模式匹配等。
示例:
	使用 -P 可以在正则表达式中使用一些标准的 Perl 特性,例如:
		零宽断言((?<=...)(?=...) 等)
		支持 \K(忽略之前的匹配,只有 \K 后的内容会作为匹配结果)
		使用非捕获分组 (?:...)
需求:
	使用 grep -P 来提取一个字符串中的数字部分
		echo "abc 123 def 456" | grep -oP '\d+'
	输出  123  456

三、grep练习

1. 从日志中匹配URL

代码如下(示例):

[root@localhost ~]# grep -oP "https?://[\S]+" o 
http://example.com/page1
https://secure-site.com/login
http://example.com/error
https://api.example.com/data?query=test&limit=10
http://example.com/dashboard
https://www.example.org/path/to/resource#anchor
http://example.com/contact
https://api.example.com/error?code=404
https://example.net/homepage
[root@localhost ~]# grep -Eo 'https?://[^\"]+' o
http://example.com/page1
https://secure-site.com/login
http://example.com/error
https://api.example.com/data?query=test&limit=10
http://example.com/dashboard
https://www.example.org/path/to/resource#anchor
http://example.com/contact
https://api.example.com/error?code=404
https://example.net/homepage for more info

2. 取出文件中的行(不包含空行和注释行)

代码如下(示例):

[root@localhost ~]# cat /etc/ssh/sshd_config |egrep -v -n "^$|^#"
[root@localhost ~]# cat /etc/ssh/sshd_config |grep -Ev -n "^$|^#"

3. 匹配以xx开头的行

代码如下(示例):

[root@localhost ~]# grep '^hello' file.txt

4. 匹配以xx结尾的行

代码如下(示例):

[root@localhost ~]# grep 'world$' file.txt

5. 匹配包含A或B的行

代码如下(示例):

[root@localhost ~]# grep 'hello\|world' file.txt

6. 查看指定时间范围的日志

代码如下(示例):查询2023-05-05日凌晨01:10-15 – 09:10-15的日志

[root@localhost ~]# grep "05/May/2023:0[1-9]:1[0-5]" access.log

7. 取出ip访问量及ip

代码如下(示例):

[root@localhost ~]# grep '16/Jan/2025' ./access.log | awk '{ ip[$1]++ } END{for (i in ip){print ip[i],i}}'
5 192.168.10.39
3 192.168.10.49
2 192.168.10.67
2 192.168.10.40
1 192.168.10.41
3 192.168.10.51
7 192.168.10.42
1 192.168.10.52
3 192.168.10.43
2 192.168.10.38

8. 过滤某个时间段的日志

代码如下(示例):时间格式: 年-月-日 时-分-秒

[root@localhost ~]# grep -E '2022-Mar-(0[1-9]|1[0-9]|2[0-9]|3[0-1]) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])' sunshine-meetings-test1-20220322191825.log 

9. 获取服务器端口

代码如下(示例):

[root@localhost ~]# netstat -lntp |grep -oP ":\K\d+"|sort -n |uniq
22
80
801
1080
1234
6443
8080
8081
8082
8083
9000
9001
9005
9273

四、sed

1. 参数说明

参数含义
a新增的字串会在新的一行
d删除行
i通常在当前行的上一行插入
p通常与参数-n 一起运行,打印行内容
s替换指定内容

2. 示例

2.1. 全局替换

[root@localhost ~]# sed -i 's/xx/xx/g' 文件

2.2. 匹配字段并对包含该字段的行进行注释

[root@localhost ~]# sed -i '/9273/s/^/#/' filename

2.3. 删除包含xx的行

[root@localhost ~]# sed  -i '/xx/d' 文件

2.4. 删除空行

[root@localhost ~]# sed  -i '/^$/d' 文件

2.5. 替换指定行

[root@localhost ~]# sed  -i '2s/old-text/new-text/' 文件

2.6. 在第一行前插入一个新行

[root@localhost ~]# sed -i '1i\new-line' 文件

2.7. 在最后一行追加一个新行

[root@localhost ~]# sed -i '$a\new-line' 文件

2.8. 打印指定的行

[root@localhost ~]# sed '2p' 文件 

2.9. 查看指定时间区间的日志

[root@localhost ~]# sed -n '/2020-02-19 14:10:00/,/2020-02-19 14:15:00/p elasticjob.log

2.10. 根据行首或行尾进行匹配

[root@localhost ~]# sed '/^pattern/d' input.txt
[root@localhost ~]# sed '/pattern$/d' input.txt

2.11. 打印行范围

[root@localhost ~]# sed  '2,4p' 文件  打印第2行和第四行
[root@localhost ~]# sed  '2,+3p' 文件 打印第2行和下面3行

五、awk

1. 取出文件中的多行

[root@localhost ~]# cat xx.txt | awk -F '' 'NR>=2 && NR<=5' 

2. 取出文件中的最后一行

[root@localhost ~]# cat xx.txt | awk -F '' 'END {print}'

3. 取出文件中的最后一列

[root@localhost ~]# cat xx.txt | awk -F '' '{print $NF}'

4. 取出文件中的倒数第n列

[root@localhost ~]# cat xx.txt | awk -F '' '{print $(NF-1)}'

5. 取出某个时间段的日志

[root@localhost ~]# awk '$2>"14:10:10" && $2<"14:12:59"' elastic-job.log  #遵循日志格式和列进行替换即可

六、sort排序、uniq去重

1. 了解知识

uv(独立访客): 
	访问网站的一个客户端为一个访客(IP)

pv(访问量): 
	页面浏览量或点击量,用户每次刷新即被计算一次

sort -k1 -rn  按照第一个字段进行倒序排序
	-k1 表示根据第一个字段(通常是数字)进行排序
	-r 表示按降序排列
	-n 表示按数字的大小进行排序

uniq:
	用于删除相邻的重复行。默认情况下,uniq 只会删除相邻的重复行,因此通常与 sort 命令配合使用,以确保所有重复行都被处理。 
	-c: 这个选项表示 "count",即统计每个唯一行的出现次数。输出的结果会在每一行前显示该行的出现次数

2. 查看日志,根据访问IP统计出某个时间段uv

[root@localhost ~]# awk '$4>"[26/Apr/2023:21:00:00 +0800]" && $4<"[26/Apr/2023:22:00:00 +0800]"' /servers/nginx/logs/access.log | awk '{print $1}' |sort |uniq -c | wc -l

10

3. 查看日志,根据日志中的访问IP进行UV分组,查看每天的总UV

[root@localhost ~]# awk '{print substr($4,2,11) " " $1}' /servers/nginx/logs/access.log | sort -r |uniq | awk '{uv[$1]++;next}END{for (ip in uv) print ip, uv[ip]}'

16/Jan/2025 10

4. 查看日志,根据访问url统计出某个时间段pv

[root@localhost ~]# awk '$4>"[16/Jan/2025:11:00:00 +0800]" && $4<"[16/Jan/2025:12:00:00 +0800]"' ./access.log |wc -l
29

5. 查看日志,根据日志中的访问时间进行pv分组,查看每天的总PV

#使用substr函数($4代表时间的列,2,11代表时间列的第二个字符到第11个字符,即按年月日取)
[root@localhost ~]# awk '{print substr($4,2,11)}' /servers/nginx/logs/access.log | sort  | uniq -c
      1 16/Jan/2025 /eureka/apps/CITYEYES-AMP/192.168.30.41:8892?status=UP&lastDirtyTimestamp=1732713444538
      1 16/Jan/2025 /eureka/apps/CITYEYES-AMP/192.168.30.43:8892?status=UP&lastDirtyTimestamp=1732713438994
      1 16/Jan/2025 /eureka/apps/CITYEYES-VAP/192.168.30.40:8891?status=UP&lastDirtyTimestamp=1733213570083
      1 16/Jan/2025 /eureka/apps/CITYEYES-VAP/192.168.30.51:8891?status=UP&lastDirtyTimestamp=1732713446662
      1 16/Jan/2025 /eureka/apps/CITYEYES-VFP/192.168.30.38:8890?status=UP&lastDirtyTimestamp=1732713439010
      1 16/Jan/2025 /eureka/apps/CITYOS-DATA-MANAGEMENT/cityos-xueliang-std10.jdicity.local:cityos-data-management:8787?status=UP&lastDirtyTimestamp=1732713420852
      1 16/Jan/2025 /eureka/apps/CITYOS-DATA-MANAGEMENT/cityos-xueliang-std16.jdicity.local:cityos-data-management:8787?status=UP&lastDirtyTimestamp=1732713439007
      1 16/Jan/2025 /eureka/apps/CITYOS-GATEHUB-ADMIN/cityos-xueliang-std16.jdicity.local:cityos-gatehub-admin:9900?status=UP&lastDirtyTimestamp=1732713439007
      1 16/Jan/2025 /eureka/apps/CITYOS-GATEHUB-GATEWAY/cityos-xueliang-std10.jdicity.local:cityos-gatehub-gateway:9901?status=UP&lastDirtyTimestamp=1732713439027
      1 16/Jan/2025 /eureka/apps/CITYOS-GATEHUB-GATEWAY/cityos-xueliang-std16.jdicity.local:cityos-gatehub-gateway:9901?status=UP&lastDirtyTimestamp=1732713439006
     15 16/Jan/2025 /eureka/apps/delta
      1 16/Jan/2025 /eureka/apps/MESSAGE-MODULE-CENTER/192.168.30.67:9088?status=UP&lastDirtyTimestamp=1732713438971
      2 16/Jan/2025 /metrics
      1 16/Jan/2025 /signin

6. 统计每天访问URL的TOP3

#使用substr函数($4代表时间的列,2,11代表时间列的第二个字符到第11个字符,即按年月日取)
[root@localhost ~]# awk '{print substr($4,2,11)" " $7}' /servers/nginx/logs/access.log |sort |uniq -c | sort -rn | head -n 3
15 16/Jan/2025 /eureka/apps/delta
2 16/Jan/2025 /metrics
1 16/Jan/2025 /signin

总结

有不正确的地方请指正,后续随时补充更新!!!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;