Linux Shell编程--正则表达式、grep、sed、awk

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除!

一、正则表达式

1、正则表达式定义

1.1、简介

正则表达式是一种强大的文本模式匹配工具,用于搜索、替换或分割字符串。在Shell脚本中,正则表达式通常与grep、sed和awk等工具一起使用。

1.2、层次分类

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

1.3、linux三剑客(grep sed awk)支持的正则表达式

支持正则的shell命令 正则类型
grep 默认使用基本正则表达式(BRE)(要使用扩展正则需要加转义字符)
egrep 或 grep -E 使用扩展的正则表达式(ERE)
sed 默认使用基本正则表达式(BRE)
awk 使用扩展正则表达式(ERE)

2、基础正则表达式

基础正则表达式是常用的正则表达式部分

元字符 含义
\ 表示转义字符,去掉特殊符号的特殊含义
. 匹配任意单个字符
^ 匹配字符串开头的位置
$ 匹配字符串末尾的位置
* 匹配前面的字符出现0~+∞
[list] 匹配list列表中的一个字符(列表中只要有一个符合即可)
[^list] 匹配任意非list列表中的一个字符
{n} 匹配前面的子表达式n次
{n,} 匹配前面的子表达式最少n次
{,n} 匹配前面的子表达式最多n次
{n,m} 匹配前面的子表达式n到m次
[ ] 代表单个字符
\? 1次或0次
^$ 空行
.* 1~+∞

3、扩展正则表达式

支持awk和egrep使用,如果grep和sed想要正常使用(grep -E sed -r)

元字符 作用含义
+ 匹配前面子表达式1次及以上
匹配前面子表达式0次或者1次
() 将括号中的字符串作为一个整体
| 以"或"的方式匹配字符串

二、使用 grep

grep命令用于搜索文件或标准输入,以查找匹配特定模式的行。

cs 复制代码
 grep 'pattern' filename
选项 作用
-i 忽略大小写
-v 显示不包含匹配行的文本
-r 递归地搜索目录
-n 显示匹配行的行号
-w 只匹配整个单词
-o 仅显示匹配的部分
-e 使用多个模式
-f 从文件中读取模式
-v 仅显示匹配的行数

egrep 支持正则表达式的拓展元字符

cs 复制代码
  egrep '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' file1.txt

1、基础正则匹配示例

1.1、运用正则,判断需要**[[ ]]**

cpp 复制代码
 [root@centos ~]# num1=1
 # 检查变量$num1是否只包含数字
 [root@centos ~]# [[ $num1 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
 yes
 [root@centos ~]# num3=1b1
 [root@centos ~]# [[ $num3 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
 no
 # 检查变量 $num 是否是一个有效的数字,可以是整数或者小数
 [root@centos ~]# [[ $num =~ ^[0-9]\.[0-9]+$ || $num =~ ^[0-9]+$ ]] && echo "yes" || echo "no"

1.2、* 匹配签到符0个或多个

cpp 复制代码
 [root@centos ~]# useradd abrt
 [root@centos ~]# grep 'abc*' /etc/passwd
 abrt:x:173:173::/etc/abrt:/sbin/nologin

1.3、\< 词首定位符号\ >词尾定位符号

cpp 复制代码
 [root@centos ~]# cat jack.txt 
 Jack JACK JAck jackly
 :% s/\<[Jj]ack\>/123/g

1.4、^以什么开头

cpp 复制代码
 [root@centos ~]# grep '^root' /etc/passwd
 root:x:0:0:root:/root:/bin/bash

1.5、$以什么结尾

cpp 复制代码
 [root@centos ~]# grep 'bash$' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 X:x:1000:1000:Lee:/home/X:/bin/bash
 mysql:x:1001:1001::/home/mysql:/bin/bash

1.6、匹配单个字符

cpp 复制代码
 [root@centos ~]# grep 'r..r' /etc/passwd
 operator:x:11:0:operator:/root:/sbin/nologin
 saslauth:x:995:76:Saslauthd user:/run/saslauthd:/sbin/nologin
 [root@centos ~]# grep 'r.t' /etc/passwd
 operator:x:11:0:operator:/root:/sbin/nologin
 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

1.7、.* 任意多个字符

cpp 复制代码
 [root@centos ~]# 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
 ...

1.8、[] 匹配方括号中的任意一个字符

cpp 复制代码
 [root@centos ~]# grep 'Root' /etc/passwd
 [root@centos ~]# grep '[Rr]oot' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin

1.9、[ - ] 匹配指定范围内的一个字符

cpp 复制代码
 [root@centos ~]#  grep [a-z]oot /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin
 setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin

1.10、[^] 匹配不在指定组内的字符,非的意思

^在[]内表示取反,^在[]外表示以什么开头

cpp 复制代码
 [root@centos ~]# grep '[^0-9]oot' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin
 setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin
 [root@centos ~]# grep '[^0-9A-Z]oot' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin
 setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin
 [root@centos ~]#  grep '[^0-9A-Za-z]oot' /etc/passwd
 [root@centos ~]# useradd Root
 useradd: user 'Root' already exists
 [root@centos ~]# grep '[a-Z]oot' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin
 setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin
 Root:x:1002:1002::/home/Root:/bin/bash
 [root@centos ~]# grep '^[rc]oot' /etc/passwd
 root:x:0:0:root:/root:/bin/bash

1.11、\(\)匹配后的标签

2、扩展正则匹配示例

2.1、+ 匹配一个或多个前导字符

cpp 复制代码
 [root@centos ~]# egrep 'ro+t' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin

2.1、? 匹配零个或一个前导字符

cpp 复制代码
 [root@centos ~]# egrep 'ro?t' /etc/passwd
 abrt:x:173:173::/etc/abrt:/sbin/nologin

2.2、a|b 匹配a或b

cpp 复制代码
 [root@centos ~]# netstat -anlp|egrep ':80|:22'
 [root@centos ~]# egrep 'root|Root' /etc/passwd
 root:x:0:0:root:/root:/bin/bash
 operator:x:11:0:operator:/root:/sbin/nologin
 Root:x:1002:1002::/home/Root:/bin/bash

2.3、x{m} 字符x重复m次

cpp 复制代码
 [root@centos ~]# egrep 'o{2}' a.txt
 loove
 looooove
 [root@centos ~]# egrep 'o{2,}' a.txt
 loove
 looooove
 [root@centos ~]# egrep 'o{6,7}' a.txt

三、sed

sed流编辑器)用于对文本进行过滤和替换。

bash 复制代码
sed 's/pattern/replacement/' filename

1、常用选项与命令

1.1、常用选项

选项 作用
-n 使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN的资料一般都会被列出到屏幕上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e 直接在指令列模式上进行 sed 的动作编辑;
-f 直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的sed 动作
-r sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i 直接修改读取的档案内容,而不是由屏幕输出

1.2、常用命令

命令 作用
a 新增, a 的后面可以接字串,而这些字串会在新的一行出现
c 取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d 删除
i 插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
p 列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n 一起
s 取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g

2、基本用法

默认会把文件内容全部输出到屏幕中 ,下面笔记进行对此进行了省略;可加-n参数进行安静模式,并且sed操作默认不会修改原文件内容

cpp 复制代码
# 示例文件:file1.txt
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

2.1、用Massachusetts替换MA:

cpp 复制代码
[root@centos ~]# sed 's/MA/Massachusetts/' file1.txt

2.2、使用多重指令:

cpp 复制代码
[root@centos ~]# sed 's/MA/Massachusetts/ ; s/PA/Pennsylvania/' file1.txt

2.3、使用脚本文件

cpp 复制代码
[root@centos ~]# cat namestate 
s/MA/Massachusetts/
s/PA/Pennsylvania/
s/CA/California/
s/VA/Virginia/
s/OK/Oklahoma/
[root@centos ~]# sed -f namestate file1.txt 

2.4、保存输出

cpp 复制代码
[root@centos ~]# sed -f namestate file1.txt > newfile.txt

2.5、阻止输入行自动显示

cpp 复制代码
[root@centos ~]# sed -n 's/MA/Massachusetts/p' file1.txt

3、sed流编辑器用法及解析

sed : stream editor(流编辑器)的缩写. 它们最常见的用法是进行文本的替换.;

3.1、删除某行

cpp 复制代码
[root@centos ~]# sed '1d' filename             	# 删除第一行 
[root@centos ~]# sed '$d' filename             	# 删除最后一行
[root@centos ~]# sed '1,2d' filename        	# 删除第一行到第二行
[root@centos ~]# sed '2,$d' filename         	# 删除第二行到最后一行
[root@centos ~]# sed '1~2d' passwd			    # 删除奇数行
[root@centos ~]# sed '0~2d' passwd 				# 删除偶数行
[root@centos ~]# sed '/^$/d' filename			# 删除文件中的空行

3.2、显示某行

cpp 复制代码
[root@centos ~]# sed -n '1p' filename           #显示第一行 
[root@centos ~]# sed -n '$p' filename           #显示最后一行
[root@centos ~]# sed -n '1,2p' filename        #显示第一行到第二行
[root@centos ~]# sed -n '2,$p' filename        #显示第二行到最后一行

3.3、使用模式进行查询

cpp 复制代码
[root@centos ~]# sed -n '/ruby/p' filename    #查询包括关键字ruby所在所有行
[root@centos ~]# sed -n '/\$/p' filename    #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含

3.4、增加一行或多行字符串

cpp 复制代码
[root@centos ~]# cat file.txt 
Hello!
Knowledge is power!
Every cloud has a silver lining
# 第一行后增加字符串"welcomto Hunan!"
[root@centos ~]# sed '1a welcom to Hunan!' file.txt
Hello!
welcom to Hunan!
Knowledge is power!
Every cloud has a silver lining
# 第一行到第三行后增加字符串"welcome to Hunan!"
[root@centos ~]# sed '1,3a welcome to Hunan!' file.txt
Hello!
welcome to Hunan
Knowledge is power!
welcome to Hunan
Every cloud has a silver lining
welcome to Hunan
# [root@centos ~]# sed '1a welcome to Hunan\nPassion!' file.txt
Hello!
welcome to Hunan
Passion!
Knowledge is power!
Every cloud has a silver lining第一行后增加多行,使用换行符\n

3.5、代替一行或多行

cpp 复制代码
# 第一行代替为Hi
[root@centos ~]# sed '1c Hi' file.txt
Hi
Knowledge is power!
Every cloud has a silver lining
# 第一行到第二行代替为Hi
[root@centos ~]# sed '1,2c Hi' file.txt
Hi
Every cloud has a silver lining

3.6、 替换一行中的某部分

格式: sed 's/要替换的字符串/新的字符串/g' (要替换的字符串可以用正则表达式)

cpp 复制代码
[root@centos ~]# sed -n '/is/p' file.txt | sed 's/is/are/g'	# 替换is为are
Knowledge are power!
[root@centos ~]# sed -n '/is/p' file.txt | sed 's/is//g'	# 删除is
Knowledge  power!
# 忽略大小写替换
sed 's/pattern/replace_string/gi' filename

# g标记可以使sed匹配第N次以后的字符被替换
echo "thisthisthisthis" | sed 's/this/THIS/2g'

# sed中的分隔符可以替换成别的字符, 因为s标识会认为后面的字符为分隔符
sed 's:text:replace_text:'
sed 's|text|replace_text|'

3.7、插入

cpp 复制代码
# 在文件file.txt中最后一行插入"bye"
[root@centos ~]# sed -i '$a bye' file.txt	
[root@centos ~]# cat file.txt 
Hello!
Knowledge is power!
Every cloud has a silver lining
bye

3.8、注意点

由于在使用 -i 参数时比较危险, 所以我们在使用i参数时在后面加上.bak就会产生一个备份的文件, 以防后悔:

cpp 复制代码
sed -i.bak 's/pattern/replace_string/' filename

sed如果在脚本中使用的话, 不可避免的要调用变量, 所以以下这种方式可以用来调用变量即' '换成了""

cpp 复制代码
# text=hello
# echo "hello world" | sed "s/$text/HELLO/"

四、 awk

awk是一个强大的文本处理工具,它使用模式匹配和动作来处理数据。

awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,

通常用来格式化文本信息

awk处理过程: 依次对每一行进行处理,然后输出 默认分隔符是空格或者tab键

cpp 复制代码
awk '/pattern/ { actions }' filename

1、awk的工作原理

cpp 复制代码
awk -F":" '{print $1,$3}' /etc/passwd

(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束

(2)然后行被:分解成字段,每个字段存储在已编号的变量中,从$1开始

(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab

(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为​3间有一个,逗号。逗号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格

(5)awk打印字段时,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。

2、BEGIN与END

BEGIN {} {}:在处理任何输入之前执行的代码块

END{}:在处理完所有输入之后执行的代码块

cpp 复制代码
[root@centos ~]# awk 'BEGIN{print 1/2} {print "ok"} END{print "-----"}' /etc/hosts
0.5
ok
ok
-----
# 首先打印 1/2 的结果,然后打印 "ok",接着对 /etc/hosts 文件的每一行执行 {print "ok"},最后在处理完所有行之后打印 "----"

3、常用选项与示例

选项 作用
FS 指定每行文本的字段分隔符,默认为空格或制表位
OFS 输出字段分隔符
NF 当前处理的行的字段个数
NR 当前处理的行的行号(序数)
$0 当前处理的行的整行内容
$n 当前处理行的第 n 个字段(第 n 列)
FILENAME 被处理的文件名
RS "行"分割符,awk从文件上读取资料时,将根据RS的定义把资料切割成许多条记录; wak一次仅读入一条记录,以进行处理,预设值是" \n "
ORS 输出记录分隔符

3.1、FS(输入字段分隔符)

cpp 复制代码
# 输出 /etc/passwd 文件中每一行的第一个字段,也就是系统中每个用户的登录名
[root@centos ~]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
root
bin
daemon
adm
...

3.2、OFS(输出字段分隔符)

cpp 复制代码
# 输出 /etc/passwd 文件中每一行的前两个字段,格式为 用户名..加密密码
[root@centos ~]# awk 'BEGIN{FS=":";OFS=".."}{print $1,$2}' /etc/passwd
root..x
bin..x
daemon..x
adm..x
...

3.3、NR (记录编号)

当awk将行为记录时, 该变量相当于当前行号

cpp 复制代码
[root@centos ~]# awk -F: '{print NR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
6 isuo
7 IPADDR=192.168.6.5
8 hjahj123
9 GATEWAY=192.168.1.1
10 NETMASK=255.255.255.0
11 DNS=114.114.114.114

3.4、FNR(记录编号)

当awk将行为记录时, 该变量相当于当前行号**(不同文件分开)**

cpp 复制代码
[root@centos ~]# awk -F: '{print FNR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
1 isuo
2 IPADDR=192.168.6.5
3 hjahj123
4 GATEWAY=192.168.1.1
5 NETMASK=255.255.255.0
6 DNS=114.114.114.114

3.5、RS(输入记录分隔符)

cpp 复制代码
[root@centos ~]# cat 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@centos ~]# awk -F: 'BEGIN{RS="bash"} {print $0}' passwd
root:x:0:0:root:/root:/bin/

bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

3.6、ORS(输出记录分隔符)

cpp 复制代码
[root@centos ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' 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

4、练习

(1)将文件合并为一行

cpp 复制代码
awk 'BEGIN{ORS="" } {print $0}' /etc/passwd

(2)把一行内容分为多行

cpp 复制代码
[root@centos ~]# cat d.txt
root:x:0:0:root:/root:/bin/bash
[root@centos ~]# awk 'BEGIN{RS=":"} {print $0}' d.txt
root
x
0
0
root
/root
/bin/bash

5、理解案例

1、打印一个文件中的第2列和第3列

cpp 复制代码
awk '{print $2,$3}' filename

2、打印指定行指定列的某个字符

cpp 复制代码
awk -F":" 'NR==3{ print $7 }' /etc/passwd

3、统计一个文件的行数:

cpp 复制代码
awk '{ print NR}' filename

4、在脚本中, 传递变量到awk中:

cpp 复制代码
# var=1000
# echo | awk -v VARIABLE=$var '{ print VARIABLE }'

5、指定字段分隔符-F或在BEGIN{ FS=":" } :

cpp 复制代码
# awk -F: '{ print $2, $3 }' filename
# awk 'BEGIN{ FS=":" }{ print $2, $3 }' filename

6、在awk中使用for循环 :

cpp 复制代码
# 每行打印两次
[root@centos ~]# awk -F: '{for(i=1;i<=2;i++) {print $0}}' passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 分别打印每行每列
[root@centos ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i}}' passwd
...

7、 在awk中使用if条件判断:

cpp 复制代码
# 显示管理员用户名
[root@centos ~]# awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
# 统计系统用户
[root@centos ~]# awk -F":" '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd

致谢

在此,我要对所有为知识共享做出贡献的个人和机构表示最深切的感谢。同时也感谢每一位花时间阅读这篇文章的读者,如果文章中有任何错误,欢迎留言指正。

学习永无止境,让我们共同进步!!

相关推荐
Jtti22 分钟前
Windows系统服务器怎么设置远程连接?详细步骤
运维·服务器·windows
小奥超人1 小时前
PPT文件设置了修改权限,如何取消权?
windows·经验分享·microsoft·ppt·办公技巧
hairenjing112310 小时前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
九鼎科技-Leo12 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
九鼎科技-Leo14 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
黎明晓月18 小时前
Java之字符串分割转换List
java·windows·list
九鼎科技-Leo18 小时前
在 C# 中,ICollection 和 IList 接口有什么区别?
windows·c#·.net
顾辰呀19 小时前
实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新
前端·windows
Bunny Chen1 天前
如何缩小PPT演示文稿的大小?
windows·microsoft·powerpoint
如光照1 天前
Linux与Windows中的流量抓取工具:wireshark与tcpdump
linux·windows·测试工具·网络安全