一、概念
在 Shell 脚本中,正则表达式是一种强大且常用的文本处理工具,它可以用来匹配、搜索、替换和截取字符串。
正则表达式是由一些字符去描述规则,在正则表达式中有两类字符
(1)元字符(Meta Character):Shell 环境中具有特殊含义的字符,在命令行解释、文件名扩展、变量替换等方面起着关键作用。
(2)普通字符:仅代表自身的字符。\元字符 ==> 普通字符
正则表达式也有不同的流派(如Egrep,java,C#)很多语言都用到了正则表达式,但是这些语言中用来描述字符串规则的"元字符"不是都一样的,因此称正则表达式有不同的流派。
正则表达式中匹配的三种量词:贪婪(Greedy)、勉强(Reluctant)、独占(Possessive)
二、元字符
|---------|------------------------------|-----------------------|----------------------------------------------------------------------------------|
| 元字符 | 含义 | 示例 | 说明 |
| . | 匹配一个除换行符以外的任意字符 | a.b | 匹配以a开头b结尾,中间有一个任意字符的单词 |
| ^ | 匹配行首 | ^ab | 匹配以ab为行首的单词 |
| | 匹配行尾 | ab | 匹配以ab为行尾的单词 |
| \ | 转义符 | \ | 转换 < 原本的含义 |
| < > | 匹配一个指定的单词 | \ | 精准匹配ab这个单词 |
| \| | 逻辑或 | ab\|AB | 匹配ab或者AB |
| \d | 匹配任何数字,等同于[0-9] | grep -P '\d' | 需要和grep等工具一起使用 |
| \D | 匹配一个非数字字符,等价于 | grep -P '\D' | 需要和grep等工具一起使用 |
| \w | 匹配任何字母,等同于[a-zA-Z0-9] | grep -P '\w' | 需要和grep等工具一起使用 |
| \W | 匹配任何非单词字符,等同于[^a-zA-Z0-9] | grep -P '\W' | 需要和grep等工具一起使用 |
| \s | 匹配任何空白字符(空格、制表符、换行符等) | grep -P '\s' | 需要和grep等工具一起使用 |
| \S | 匹配任何非空白字符 | grep -P '\S' | 需要和grep等工具一起使用 |
| \b | 匹配单词边界 | grep -P '\bhello\b' | 单词的开头边界:\bword 匹配 "word" 单词的结尾边界:word\b 匹配 "word" |
| \B | 匹配非单词边界 | grep -P 'hello\B' | 匹配单词内部的连接点:\Bcat\B 会匹配 "category" 中的 "cat" 确保匹配不发生在单词边界:\B-\B 会匹配 "a-b" 中的 - |
Shell程序实例
bash
#/bin/bash
#使用 . 匹配任意字符
echo "small middle big ..." | grep "b.g"
#使用 ^ 匹配行首
echo -e "hello world\nByebye tony\nOK" | grep "^By"
#使用 $ 匹配行尾
echo -e "Today is a good day\nJust so so\n" | grep "so$"
#使用 <> 匹配一个指定单词
echo -e "Pass CET6 and CET4" | grep "\<CET6\>"
#使用 \d 匹配任何数字
echo "abc 123 def" | grep -P "\d"
Shell程序效果截图

三、字符范围匹配
|---------|---------------|-------------|---------------------------|
| 字符 | 含义 | 示例 | 说明 |
| [ ] | 匹配一个指定范围的字符 | a[xyz]b | 匹配以a开头b结尾,中间有一个x或y或z的单词 |
| [^ ] | 匹配一个不在指定范围的字符 | a[^xyz]b | 匹配以a开头b结尾,中间有一个不是x或y或z的单词 |
Shell程序实例 (国内手机号码匹配)
phone.txt
bash
111102198910084421
13611112222
13133334444
15855556666
13177778888
13199990000
+8618611112222
990785199507319527
66666666666
bash
#!/bin/bash
#Shell程序实例 (国内手机号码匹配)
#基本正则表达式
grep -E '1[3-9][0-9]{9}' phone.txt
#严格匹配(推荐)
grep -P '\b1[3-9]\d{9}\b' phone.txt
#带区号匹配
grep -P '(^|\s)(\+86)?1[3-9]\d{9}(\s|$)' phone.txt
Shell程序效果截图

四、字符重复匹配
1、贪婪匹配
|-----------|----------------|--------|----------------|
| 非贪婪量词 | 含义(尽量重复的多) | 示例 | 说明 |
| ? | 使前面的字符重复0次或1次 | a? | 匹配a重复了0次或1次的单词 |
| * | 使前面的字符重复0次或多次 | a* | 匹配a重复了0次或多次的单词 |
| + | 使前面的字符重复1次或多次 | a+ | 匹配a重复了1次或多次的单词 |
| {n} | 使前面的字符重复n次 | a{6} | 匹配a重复了6次的单词 |
| {n,} | 使前面的字符n次或以上 | a{6,} | 匹配a重复了6次或以上的单词 |
| {n,m} | 使前面的字符重复n次到m次 | a{6,9} | 匹配a重复了6次到9次的单词 |
Shell程序实例(IPv4地址匹配)
bash
#/bin/bash
ipstr="asdsdss192.a.65.4ddgdg192.168.258.990sgsfdg 123adsad 123 sa 676761234safd192.168.63.100fed"
#匹配 IPv4地址
#非严格匹配:快速提取 X.X.X.X 格式的字符串,可能数字超IPV4的范围
echo $ipstr | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}'
#严格匹配:不仅匹配X.X.X.X 格式的字符串,还限制数字的范围
echo "$ipstr" | grep -oE '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
Shell程序效果截图

2、非贪婪匹配
非贪婪匹配就是在贪婪量词后面加个问号?
|-----------|---------------------|---------|--------------------|
| 非贪婪量词 | 含义(尽量重复的少) | 示例 | 说明 |
| ?? | 匹配 0 次或 1 次,但尽可能少 | a?? | 尽量少地匹配a重复了0次或1次的单词 |
| *? | 匹配 0 次或多次,但尽可能少 | a*? | 尽量少地匹配a重复0次或多次的单词 |
| +? | 匹配 1 次或多次,但尽可能少 | a+? | 尽量少地匹配a重复了1次或多次的单词 |
| {n}? | 使前面的字符重复n次 | a{6}? | 尽量少地匹配a重复了6次的单词 |
| {n,}? | 使前面的字符n次或以上,但尽可能少 | a{6,}? | 尽量少地匹配a重复了6次或以上的单词 |
| {n,m}? | 使前面的字符重复n次到m次,但尽可能少 | a{6,9}? | 尽量少地匹配a重复6次到9次的单词 |
Shell程序实例 (匹配 HTML 标签内容)
Web 服务器日志 (access.log)
bash
[2023-05-01 10:00:00] "GET /home HTTP/1.1" 200 "Mozilla/5.0"
[2023-05-01 10:00:01] "POST /login HTTP/1.1" 302 "Chrome/114.0"
[2023-05-01 10:00:02] "GET /profile?id=123&name=Alice HTTP/1.1" 200 "Safari/16.0"
parse_log.sh
bash
#!/bin/bash
echo "===== 日志分析结果 ====="
echo "1. 时间戳:"
grep -Po '\[\K.*?(?=\])' access.log
echo -e "\n2. HTTP 方法和路径:"
grep -Po '"\K[A-Z]+ .*?(?= HTTP)' access.log
echo -e "\n3. 查询参数:"
grep -Po 'GET /.*?\K\?.*?(?= HTTP)' access.log
echo -e "\n4. User-Agent:"
grep -Po '"\K[^"]*?(?="$)' access.log | tail -n +2 | sed -n 'p;n'
Shell程序效果截图

五、常用文本处理命令
1、grep
grep是Global search Regular Expression and Print out the line的简称,即全面搜索正则表达式并把行打印出来。
grep(egrep):用来在文本文件里查一个特定的字符串。
egrep用的是扩展的正则表达式。
bash
grep [选项] [模式] [文件...]
grep options "正则表达式" filenames
options:
-n 显示行号
-E egrep用的是扩展的正则表达式
-i ignore 在字符串比较时忽略大小写
-c count 打印每个文件里匹配行的个数
--color=always 高亮显示
--color=never 不高亮显示
--color=auto 自动
-H 显示文件名
-h 不显示文件名
**注意:**默认情况下,grep命令打印出包含模式的所有行,一旦加上-c选项,就只显示包含模式行的数量
bash
grep -n -E --color=always -H "([0-9]{1,3}.){3}[0-9]{1,3}" 1.txt
2、awk
AWK 是一种强大的文本处理语言,特别适合处理结构化文本数据(如日志文件、CSV 文件等)。它以行为单位处理文本,并支持字段分割、模式匹配、计算和格式化输出等功能。
awk:逐行读取文件,默认以 连续的空白字符(空格/制表符) 为分隔符将每行数据拆分为多个字段(1, 2...),然后可以对字段、整行($0)、行号(NR)等数据进行处理,支持计算、匹配、统计等操作。
bash
awk options 'pattern {action}' file
pattern:正则表达式或条件,用于匹配行
action:匹配后执行的操作(如 print、计算等)
file:输入文件(可省略,默认从 stdin 读取)
options:
-F 指定分隔符
-v 定义变量
-f 从脚本读取命令
-E 启用扩展正则
--posix 启用 POSIX 正则
基本匹配
bash
awk '/正则表达式/' file
#示例
awk '/error/' log.txt # 打印包含 "error" 的行
匹配特定字段
bash
awk '$2 ~ /正则表达式/' file
#示例
awk '$3 ~ /^[0-9]+$/' data.txt # 第 3 列是数字的行
反向匹配(不匹配)
bash
awk '!/正则表达式/' file
#示例
awk '!/warning/' log.txt # 打印不包含 "warning" 的行
3、sed
sed全称为Stream Editor,是一个强大的流式文本编辑器,用于对文本进行过滤和转换。它特别适合用于批量编辑文件或处理文本流。
bash
sed [选项] '命令' 文件
选项:控制 sed 的行为(如 -i 直接修改文件)
命令:指定对文本的操作(如替换 s/old/new/)
文件:输入文件(可省略,默认从 stdin 读取)
选项:
-n 静默模式(仅打印处理的行)
-i 直接修改文件(慎用!)
-E/-r 启用扩展正则
-e 执行多条命令
--debug 调试模式(GNU sed 支持)
基本匹配
bash
sed -n '/正则表达式/p' file
#示例
sed -n '/error/p' log.txt # 打印包含 "error" 的行
替换(s///)
bash
sed 's/正则表达式/替换内容/修饰符' file
#示例
sed 's/foo/bar/g' file # 全局替换 "foo" 为 "bar"
sed 's/[0-9]\+/NUM/g' file # 替换所有数字为 "NUM"(\+ 需转义)
sed -E 's/[0-9]+/NUM/g' file # 同上,但用扩展正则(\+ 无需转义)
删除(d)
bash
sed '/正则表达式/d' file
#示例
sed '/^#/d' file # 删除所有注释行(以 `#` 开头)
sed '/^$/d' file # 删除所有空行
反向引用(\1, \2...)
bash
sed 's/\(正则表达式\)/替换\1/' file
#示例
sed 's/\(foo\) bar/\1 baz/' file # "foo bar" → "foo baz"
sed -E 's/(foo) bar/\1 baz/' file # 扩展正则写法(括号无需转义)
4、cut
cut 是一个简单但实用的文本处理工具,用于从文件或标准输入中提取文本的特定部分(如列或字符位置)。它特别适合处理结构化数据(如 CSV、TSV 或固定宽度的文本)。它 不支持正则表达式 ,但可以快速截取固定格式的字段。
把cut放进到本文中一起讲解,主要是给使用shell编程设计的大伙们,提供一个参考比对借鉴的工具。
bash
cut [选项] [文件]
选项 :指定如何截取文本(如按字符、字段等)。
文件 :输入文件(可省略,默认从 stdin 读取)
选项 :
-d 指定 分隔符(默认 TAB)
-f 选择 字段(列)
-c 选择字符位置
-s 仅显示包含分隔符的行
--complement 反选(排除指定列)
按字段(列)提取
bash
cut -d':' -f1,3 /etc/passwd
#示例输出
root:0
daemon:1
bin:2
...
按字符位置提取
bash
echo "abcdefgh" | cut -c2-5
#示例输出 (提取第 2~5 个字符)
bcde
排除某些列
bash
#(提取 data.csv 中 除了第 2 列 的所有列)
cut -d',' -f2 --complement data.csv
处理 CSV 文件
bash
#(先过滤含 "ERROR" 的行,再提取第 3 列)
cut -d',' -f1,3 employees.csv
提取固定宽度的数据
bash
#(提取第 1~10 和第 20~30 个字符)
cut -c1-10,20-30 data.txt
结合 grep 过滤后提取
bash
#(先过滤含 "ERROR" 的行,再提取第 3 列)
grep "ERROR" log.txt | cut -d' ' -f3
5、wc
wc(Word Count)是一个用于 统计文件或文本的行数、单词数、字符数 的简单工具,常用于日志分析、代码统计等场景。
bash
wc [选项] [文件...]
选项:控制统计的内容(如仅显示行数)。
文件:输入文件(可省略,默认从 stdin 读取)。
选项:
-l 仅统计 行数(Line)
-w 仅统计 单词数(Word)
-c 仅统计 字节数(Byte)
-m 仅统计 字符数(适用于 Unicode)
-L 显示 最长行的长度
无选项 显示 行数、单词数、字节数
基础使用案例
bash
#基本统计
wc file.txt
#输出结果
12 45 230 file.txt #12:行数;45:单词数(以空格分隔);230:字节数
#仅统计行数
wc -l file.txt
#输出结果
12 file.txt
#统计多个文件
wc *.txt
#输出结果
12 45 230 file1.txt
20 80 400 file2.txt
32 125 630 total
#结合 grep 统计匹配行数
grep "ERROR" log.txt | wc -l #(统计 log.txt 中包含 "ERROR" 的行数)
#统计代码行数
find . -name "*.py" -exec wc -l {} \; #(统计当前目录下所有 .py 文件的行数)
6、sort
sort 是 Linux 中用于 对文本行进行排序 的强大工具,支持按字母、数字、月份等多种规则排序,并能去重、合并文件等。
bash
sort [选项] [文件]
选项:控制排序规则(如降序、按数字排序等)
文件:输入文件(可省略,默认从 stdin 读取)
选项:
-r 降序排序(默认升序)
-n 按数字排序(默认按字符串)
-k 指定排序的列
-u 去重(仅保留唯一行)
-f 忽略大小写
-t 指定列分隔符
-o 结果输出到文件
-m 合并已排序的文件
-c 检查文件是否已排序
...
基础使用案例
bash
#1、基本排序(按字母升序)
sort fruit.txt
#输出结果
banana
apple
orange
#2、按数字排序
sort -n numbers.txt
#numbers.txt文件输入:
10
2
45
#输出
2
10
45
#3、按指定列排序
sort -t',' -k2 data.csv
#输入(data.csv):
Alice,25
Bob,30
Eve,20
#输出:
Eve,20
Alice,25
Bob,30
#4、降序排序 + 去重
sort -r -u names.txt
#输入:
Alice
Bob
Alice
#输出:
Bob
Alice
#5、检查文件是否已排序
#若已排序,无输出;未排序则报错。
sort -c file.txt
#6、合并多个已排序文件
sort -m sorted1.txt sorted2.txt