在 Linux 自动化运维与 Shell 编程中,grep、sed、awk 被合称为「文本处理三剑客」,配合正则表达式可以实现极高效的文本过滤、替换、提取与统计工作,是运维工程师的必备核心技能。本文系统梳理正则表达式基础与三剑客的核心用法,配合大量实战示例帮助快速上手。
正则表达式基础
正则表达式(Regular Expression, RE)是一套字符匹配模式,通过特殊符号定义匹配规则,在文本中查找符合规则的内容。它是三剑客的核心能力基础,广泛应用于 grep、sed、awk,以及 MySQL、Python、Nginx 等众多技术场景。
核心概念
元字符:具备特殊含义、不同于字面本身的字符,是正则表达式的核心组成
行内匹配:正则默认按行匹配,只要行中包含符合规则的内容就会匹配整行
分类:分为基本正则(BRE)与扩展正则(ERE),扩展正则新增了更多元字符,语法更简洁,需要工具开启对应支持
基本正则表达式元字符(BRE)
| 元字符 | 含义 | 示例与说明 |
|---|---|---|
^ | 行首定位符,匹配行开头 | grep "^root" /etc/passwd 匹配以 root 开头的行 |
$ | 行尾定位符,匹配行结尾 | grep "bash$" /etc/passwd 匹配以 bash 结尾的行 |
. | 匹配任意单个字符(除换行符) | grep "a.c" test.txt 可匹配 abc、adc、aec 等 |
* | 匹配前一个字符 0 次或多次 | grep "ab*c" test.txt 可匹配 ac、abc、abbc 等 |
.* | 匹配任意长度的任意字符 | grep ".*" test.txt 匹配文件中所有行 |
[] | 匹配指定范围内的单个字符 | [lL]ove 匹配 love、Love;[a-z0-9] 匹配小写字母或数字 |
[^] | 匹配不在指定范围内的单个字符(取反) | [^0-9]ove 匹配非数字开头的 ove 字符串 |
\ | 转义符,取消元字符的特殊含义 | grep "l\." 1.txt 匹配字面量 l. |
\< | 词首定位符,匹配单词开头 | grep "\<love" test.txt 匹配以 love 开头的单词 |
\> | 词尾定位符,匹配单词结尾 | grep "love\>" test.txt 匹配以 love 结尾的单词 |
() | 匹配稍后使用的字符的标签 | :3,9 s/(.*)/#\1/ 加注释 |
x\{m\} | 字符 x 连续出现 m 次 | grep "x\{m\}" test.txt 匹配连续 3 个 o |
x\{m,\} | 字符 x 连续出现至少 m 次 | o\{5,\} 匹配至少 5 个连续 o |
x\{m,n\} | 字符 x 连续出现 m~n 次 | o\{3,5\} 匹配 3 到 5 个连续 o |
补充说明:
^是「行首」,\<是「词首」。^love只匹配行第一个单词是 love 的行;<love可以匹配行中任意位置以 love 开头的单词。
扩展正则表达式元字符(ERE)
扩展正则在基本正则基础上新增了更灵活的语法,使用时需要通过 grep -E(即 egrep)、sed -r 开启支持。
| 元字符 | 含义 | 示例与说明 |
|---|---|---|
+ | 匹配前一个字符 1 次或多次 | egrep "lo+ve" test.txt 匹配 love、loove,不匹配 lve |
? | 匹配前一个字符 0 次或 1 次 | egrep "lo?ve" test.txt 匹配 lve、love,不匹配 loove |
a|b | 匹配a或b | 逻辑或,匹配左右任意一个表达式 |
() | 分组,将多个字符作为整体处理 |
高频正则示例
# 找love
grep love 1.txt
# 以love开头
/^love/
# 以love结尾
/love$/
# l开始,一个任意字符,ve结尾
/l.ve/
# l开始,零个或多个o,ve结尾
/lo*ve/
# 大L 或者小L 开头的 ove
/[Ll]ove/
# love最后一个小写字母
/love[a-z]/
# love最后一个(不是字母或者数字),而是符号
/love[^a-zA-Z0-9]/
[root@localhost ~]# egrep "love[^a-zA-Z0-9]" 1.txt
love,
love?
# 所有行
/.*/
[root@localhost ~]# egrep ".*" 1.txt | wc
# 空行
/^$/
# 开头一个大写,最后2个任意字符
/^[A-Z]..$/
# 一个大写开头,0到多个小写或空格,3,最后是0-5的一个数字
/^[A-Z][a-z ]*3[0-5]/
# 0到多个小写字母,最后一个点
/[a-z]*\./
# 0到多个空格开头,一个大写,一个小写,再一个小写结尾
/^ *[A-Z][a-z][a-z]$/
# 0到多个字母开头,非逗号,0到多个英文结尾
/^[A-Za-z]*[^,][A-Za-z]*$/
# 找个单词
/\<fourth\>/
# 找单词
/\<f.*th\>/
# 5两次2三次和一个点
/5{2}2{3}\./
# 0到多个 ,空格或tab的行
/^[ \t]*$/
# 井号开头的行
/^#/
# 有0到多个,空格或者tab开头的行,的注释行
/^[ \t]*#/
# 多个r
:1,$ s/\([Oo]ccur\)ence/\1rence/
# 换个位置
:1,$ s/\(square\) and \(fair\)/\2 and \1/
grep 文本过滤工具
grep 是三剑客中的「查找器」,核心能力是根据正则规则从文本中过滤出符合条件的行,是 Linux 下最常用的文本检索工具。
grep 家族与返回值
grep 有三个常用变体,适配不同场景:
grep:默认支持基本正则表达式egrep(等价于grep -E):支持扩展正则表达式fgrep(等价于grep -F):不解析正则,纯字面量匹配,处理速度快
命令返回值含义:
0:成功找到匹配内容1:未找到任何匹配内容2:文件不存在、权限不足等执行错误
grep "root" /etc/passwd
echo $? # 输出 0,匹配成功
grep "not_exist_user" /etc/passwd
echo $? # 输出 1,无匹配结果
grep "root" /not_exist_file
echo $? # 输出 2,文件不存在
核心参数详解
| 参数 | 功能 | 示例 |
|---|---|---|
-q | 静默模式,不输出结果,只通过返回值判断 | grep -q "root" /etc/passwd 常用于 Shell 脚本条件判断 |
-v | 反向匹配,输出不包含规则的行 | grep -v "^#" config.conf 过滤掉注释行 |
-R / -r | 递归检索目录下所有文件 | grep -R "nginx" /etc/ 在 /etc 目录下递归查找 nginx |
-o | 只输出匹配到的关键字,而非整行 | grep -o "root" /etc/passwd 每行输出一个匹配到的 root |
-n | 输出结果时显示行号 | grep -n "root" /etc/passwd |
-B N | 显示匹配行的前 N 行(Before) | grep -B2 "error" app.log |
-A N | 显示匹配行的后 N 行(After) | grep -A5 "404" access.log |
-C N | 显示匹配行的前后各 N 行(Context) | grep -C3 "Exception" error.log |
-l | 只输出包含匹配内容的文件名 | grep -rl "root" /etc/ |
此外扩展正则还支持两个快捷符号:
\w:等价于[a-zA-Z0-9],匹配字母数字字符\W:等价于[^a-zA-Z0-9],匹配非字母数字字符\b:等价于<>,匹配单词边界
常用过滤示例
# 查找以 n 开头的行
egrep "^n" datafile.txt
# 查找以 4 结尾的行
egrep "4$" datafile.txt
# 查找包含 5. 开头后跟任意字符的行
egrep "5\.." datafile.txt
# 查找连续出现 9 个小写字母的行
egrep "[a-z]{9}" datafile.txt
# 匹配完整独立单词 north
egrep "\bnorth\b" datafile.txt
# 匹配包含 NW 或 EA 的行
egrep "NW|EA" datafile.txt
# 匹配包含 1 个及以上 3 的行
egrep "3+" datafile.txt
# 匹配包含一个或多个 no 的行
egrep "(no)+" datafile.txt
sed 流编辑器
sed(Stream EDitor)是三剑客中的「编辑器」,是一款非交互式的流编辑器,逐行处理文本并输出结果,默认不修改原文件,非常适合批量自动化修改配置文件、文本格式转换等场景。
工作原理与基础格式
工作流程:
读取一行文本存入「模式空间」(临时缓冲区)
根据 sed 命令处理模式空间中的内容
处理完成后将模式空间内容输出到屏幕
读取下一行重复上述流程,直到文件末尾
两种使用格式:
# 1. 命令行模式:直接写处理规则
sed [选项] '处理命令' 文件名
# 2. 脚本模式:从文件读取处理规则
sed [选项] -f 脚本文件 文件名
返回值说明:
正常执行时无论是否匹配到内容,返回值都是 0
仅当命令存在语法错误时,返回非 0 值
正则支持:
默认支持基本正则表达式
加
-r参数可支持扩展正则表达式
核心命令详解
删除命令 d
删除匹配到的行。
# 删除包含 root 的行
sed -r '/root/d' passwd
# 删除第 3 行
sed -r '3d' passwd
# 删除第 3 行到最后一行
sed -r '3,$d' passwd
# 删除最后一行
sed -r '$d' passwd
# 删除所有空行
sed -r '/^$/d' test.txt
# 删除所有 # 开头的注释行
sed -r '/^#/d' config.conf
替换命令 s
最常用的核心命令,格式为 s/原内容/替换内容/标记。
# 将每行第一个 root 替换为 aofa
sed -r 's/root/aofa/' passwd
# 全局替换:将所有 root 替换为 aofa
sed -r 's/root/aofa/g' passwd
# 只替换行首的 root
sed -r 's/^root/aofa/' passwd
# & 代表匹配到的内容:在结尾两位数字后追加 .5
sed -r 's/[0-9][0-9]$/&.5/' passwd
# 反向引用:给所有 mail 前面加上 E
sed -r 's/(mail)/E\1/g' passwd
# 自定义分隔符:当内容包含 / 时,可用 # 或 @ 作为分隔符
sed -r 's#/bin/bash#/bin/zsh#g' passwd
读写文件命令 r /w
r:读取外部文件内容,追加到匹配行之后w:将匹配到的行写入到外部文件(另存为)
# 在文件最后一行之后追加 1.txt 的全部内容
sed -r '$r 1.txt' passwd
# 在包含 root 的行之后追加 1.txt 的内容
sed -r '/root/r 1.txt' passwd
# 将 passwd 前 5 行另存为 123.txt
sed -r '1,5w 123.txt' passwd
# 将包含 root 的行另存为 root_line.txt
sed -r '/root/w root_line.txt' passwd
追加 / 插入 / 替换整行
a:在匹配行之后追加新行i:在匹配行之前插入新行c:将匹配行整体替换为新内容
# 在第 2 行之后追加一行 123
sed -r '2a123' passwd
# 在第 2 行之前插入一行 aaaaaaaa
sed -r '2iaaaaaaaa' passwd
# 将第 2 行整行替换为 new_content
sed -r '2cnew_content' passwd
# 插入多行内容,用 \ 转义换行
sed -r '2a line1\nline2\nline3' passwd
其他常用命令
n:读取下一行到模式空间(next),配合其他命令使用!:反向选择,对不匹配的行执行命令-e:多重编辑,同时执行多条 sed 命令
# 找到 root 行后,删除它的下一行
sed -r '/root/{n;d}' passwd
# 删除第 2 行以外的所有行(只保留第 2 行)
sed -r '2!d' passwd
# 多重编辑:删除1-3行,再把第4行的 adm 换成 admin
sed -r -e '1,3d' -e '4s/adm/admin/g' passwd
# 等价写法,用分号分隔多条命令
sed -r '1,3d;4s/adm/admin/g' passwd
运维实战案例
# 1. 删除 vsftpd 配置中的所有注释行
sed -ri '/^#/d' /etc/vsftpd/vsftpd.conf
# 2. 在配置文件末尾追加一行配置
sed -ri '$a\chroot_local_user=YES' /etc/vsftpd/vsftpd.conf
# 3. 修改 SELINUX 配置项的值
sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
# 4. 给 2-6 行添加 # 注释
sed -r '2,6s/^/#/' a.txt
# 两种等价写法
sed -r '2,6s/(.*)/#\1/' a.txt
sed -r '2,6s/.*/#&/' a.txt
# 5. sed 中调用外部 Shell 变量
var1="hello world"
sed -r "1a$var1" /etc/hosts
# 注意:调用变量必须用双引号,单引号内变量不会被解析
生产环境提示:
-i参数会直接修改原文件,建议先不加-i验证输出结果,确认无误后再添加该参数执行修改。
awk 文本处理语言
awk 是三剑客中功能最强大的「分析器」,它本身就是一门完整的编程语言,擅长对结构化文本进行按列提取、统计、运算与格式化输出,是日志分析、数据统计的核心利器。
工作原理与基础语法
工作流程:
逐行读取文本,将整行内容存入内置变量
$0根据字段分隔符将行拆分为多个字段,依次存入
$1, $2, $3...执行用户定义的处理动作
读取下一行重复处理,直到文件结束
标准语法结构:
awk [选项] 'BEGIN{预处理} {行处理} END{后处理}' 文件名
BEGIN{}:处理所有行之前执行一次,常用于初始化变量、设置分隔符{}:行处理块,每读取一行就执行一次END{}:处理所有行之后执行一次,常用于输出最终统计结果
基础示例:
# 以冒号分隔,打印用户名和对应 UID
awk -F: '{print $1, $3}' /etc/passwd
# 三段式完整结构示例
awk 'BEGIN{print "start"} {print $0} END{print "end"}' /etc/hosts
核心内部变量
| 变量 | 含义 | 示例 |
|---|---|---|
FS | 输入字段分隔符,默认空格 | awk 'BEGIN{FS=":"} {print $1}' /etc/passwd |
OFS | 输出字段分隔符,默认空格 | awk 'BEGIN{OFS="---"} {print $1,$2}' |
RS | 输入记录(行)分隔符,默认换行 | 可自定义行分隔符实现特殊格式解析 |
NR | 当前记录号(行号),多文件连续计数 | awk '{print NR, $0}' file1 file2 |
FNR | 当前文件的记录号,多文件独立计数 | awk '{print FNR, $0}' file1 file2 |
NF | 当前行的字段总数 | awk -F: '{print NF, $NF}' /etc/passwd |
$NF | 最后一个字段的内容 | 常用于直接取每行最后一列 |
模式匹配与条件判断
awk 支持丰富的匹配模式,只有符合模式的行才会执行对应动作。
正则匹配
使用 ~ 表示匹配,!~ 表示不匹配。
# 匹配以 root 开头的行
awk '/^root/' /etc/passwd
awk '$0 ~ /^root/' /etc/passwd
# 第一列匹配以 root 开头
awk -F: '$1 ~ /^root/' /etc/passwd
# 第一列不包含 root 字符串
awk -F: '$1 !~ /root/' /etc/passwd
数值与字符串比较
支持 ==、!=、<、>、<=、>= 全套比较运算符。
# UID 等于 0 的用户行
awk -F: '$3 == 0' /etc/passwd
# UID 小于 10 的用户行
awk -F: '$3 < 10' /etc/passwd
# 默认 shell 是 /bin/bash 的用户
awk -F: '$7 == "/bin/bash"' /etc/passwd
逻辑运算与范围模式
# 逻辑与:用户名包含root 且 UID<=15
awk -F: '$1~/root/ && $3<=15' /etc/passwd
# 逻辑或:用户名包含root 或 UID<=15
awk -F: '$1~/root/ || $3<=15' /etc/passwd
# 范围模式:从包含 adm 的行到包含 lpd 的行全部输出
awk -F: '/adm/,/lpd/' /etc/passwd
awk 编程基础
变量使用
awk 支持自定义变量,无需声明可直接使用。
# -v 参数传入自定义变量
awk -v user=root -F: '$1 == user' /etc/passwd
# 调用 Shell 外部变量
name="root"
awk -F: '$1 == "'"$name"'"' /etc/passwd
条件分支
# 单分支:UID 为 0 则标记为管理员
awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
# 双分支:统计管理员和系统用户数量
awk -F: '{if($3==0){count++} else{i++}} END{print "管理员数:"count; print "系统用户数:"i}' /etc/passwd
# 多分支:按 UID 分类用户类型
awk -F: '{
if($3==0){print $1,"is admin"}
else if($3>=1000){print $1,"is normal user"}
else{print $1,"is system user"}
}' /etc/passwd
循环结构
# while 循环:将每行内容打印 10 次
awk '{i=1; while(i<=10){print $0; i++}}' passwd
# for 循环:逐列打印每行的所有字段
awk -F: '{for(i=1;i<=NF;i++) {print $i}}' passwd
# 数字循环:输出 1 到 5
awk 'BEGIN{for(i=1;i<=5;i++){print i}}'
数组
awk 数组支持关联数组,非常适合用于统计、去重场景。
# 将用户名存入数组
awk -F: '{username[++i]=$1} END{print username[1]}' /etc/passwd
# 遍历数组打印所有键值
awk -F: '{user[$1]=$3} END{for(i in user){print i, user[i]}}' /etc/passwd
经典实战案例
统计系统中各类 shell 的数量
awk -F: '{shells[$NF]++} END{for(i in shells){print i, shells[i]}}' /etc/passwd
统计三类用户的数量
awk -F: '{
if($3==0){admin++}
else if($3>=1000){normal++}
else{system++}
} END{
print "管理员数量:", admin
print "系统用户数量:", system
print "普通用户数量:", normal
}' /etc/passwd
三目运算符简化判断
# 第7列大于4显示high,否则显示low
awk '{print ($7 > 4 ? "high "$7 : "low "$7)}' datafile
写在最后
Shell 三剑客 + 正则表达式是 Linux 文本处理的黄金组合:
- grep 擅长快速过滤查找,精准定位目标行
- sed 擅长批量编辑替换,自动化完成配置修改
- awk 擅长结构化数据分析,实现统计、计算、格式化输出
SHELL脚本自动化部署LAMP环境
在线部署网校系统
#!/bin/bash
# 定义数据库密码变量
Mysql_Pass=123
# 系统更新(已注释禁用)
#yum update
# 安装 EPEL 扩展软件源
yum -y install epel-release
# 安装基础环境工具
yum -y install bash-completion vim wget
systemctl restart firewalld
systemctl enable firewalld
setenforce 0
# 防火墙规则配置:开放 HTTP、HTTPS 服务及 8080 端口
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
# 安装 LAMP 运行环境(Apache + PHP + MariaDB)
yum -y install httpd
yum -y install \
php php-cli \
php-curl \
php-fpm \
php-intl \
php-mcrypt \
php-mysql \
php-gd \
php-mbstring \
php-xml \
php-dom
yum -y install mariadb-server mariadb
yum -y install http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/m/mod_xsendfile-0.12-10.el7.x86_64.rpm
# Apache 服务配置
rm -rf /etc/httpd/conf.d/welcome.conf
# 修改 Apache 监听端口为 8080(当前已注释禁用)
#sed -ri 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf
systemctl enable httpd
# MariaDB 数据库初始化
systemctl start mariadb
systemctl enable mariadb
mysqladmin -uroot password "$Mysql_Pass"
mysql -uroot -p"$Mysql_Pass" -e "create database edusoho"
# PHP 运行参数配置
sed -ri 's/post_max_size = 8M/post_max_size = 1024M/' /etc/php.ini
sed -ri 's/memory_limit = 128M/memory_limit = 1024M/' /etc/php.ini
sed -ri 's/upload_max_filesize = 2M/upload_max_filesize = 1024M/' /etc/php.ini
sed -ri 's#;date.timezone =#date.timezone = Asia/ShangHai#' /etc/php.ini
systemctl start php-fpm
systemctl enable php-fpm
# 下载并部署 EduSoho 程序
wget http://download.edusoho.com/edusoho-7.5.12.tar.gz
tar xf edusoho-7.5.12.tar.gz
cp -rf edusoho /var/www/
chown -R apache.apache /var/www/edusoho/
rm -rf /var/www/html/index.html
sed -ri 's#DocumentRoot "/var/www/html"#DocumentRoot "/var/www/edusoho/web"#' /etc/httpd/conf/httpd.conf
cat >>/etc/httpd/conf/httpd.conf <<EOF
<Directory "/var/www/edusoho/web">
AllowOverride All
Require all granted
</Directory>
EOF
systemctl restart httpd
systemctl restart php-fpm
三者经常通过管道联合使用,配合 Shell 脚本可以实现复杂的自动化运维任务,大幅提升批量处理文本的效率。熟练掌握三剑客,是 Linux 运维进阶的必经之路。