Shell脚本编程之正则表达式

一、概念

在 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

phone.sh

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
相关推荐
我命由我1234528 分钟前
35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)
java·服务器·开发语言·jvm·后端·架构·java-ee
&zzz28 分钟前
Python生成exe
开发语言·python
Chandler2435 分钟前
Go:方法
开发语言·c++·golang
CopyLower1 小时前
分布式ID生成方案的深度解析与Java实现
java·开发语言·分布式
随便@_@2 小时前
基于MATLAB/simulink的信号调制仿真--AM调制
开发语言·matlab·simulink·移动仿真
爱代码的小黄人2 小时前
深入解析系统频率响应:通过MATLAB模拟积分器对信号的稳态响应
开发语言·算法·matlab
vsropy3 小时前
matlab安装python API 出现Invalid version: ‘R2022a‘,
开发语言·python
old_iron3 小时前
vim定位有问题的脚本/插件的一般方法
linux·编辑器·vim
whoarethenext4 小时前
qt的基本使用
开发语言·c++·后端·qt
atec20004 小时前
使用uv管理python项目环境
开发语言·python·uv