前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除!
一、正则表达式
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
致谢
在此,我要对所有为知识共享做出贡献的个人和机构表示最深切的感谢。同时也感谢每一位花时间阅读这篇文章的读者,如果文章中有任何错误,欢迎留言指正。
学习永无止境,让我们共同进步!!