Shell脚本正则表达式总结


正则表达式(Regular Expression, 简称 regex)是处理文本时非常强大的工具,它通过定义文本模式来匹配、搜索、替换特定的字符串。在 Shell 脚本中,正则表达式广泛应用于字符串操作、数据处理等场景,尤其是在 grepsedawk 等常用工具中,使用正则表达式可以极大地提高脚本的效率和灵活性。

基本概念

正则表达式由一系列字符及元字符组成,用来匹配特定的文本。常见的正则表达式字符包括:

  • 普通字符:如 ab1 等,直接表示它们自身。
  • 元字符:具有特殊含义的字符,比如 ., *, ?, ^, $ 等。
  • 字符类:一组字符的集合,如 [abc] 表示可以匹配 abc 中的任意一个。
  • 预定义字符类:如 \d 表示数字,\w 表示字母、数字或下划线。

常用正则表达式元字符及其含义

  • . :匹配任意单个字符(除了换行符)。

    • 示例:a.b 可以匹配 acb, a1b, a#b 等。
  • * :匹配前面的字符 0 次或多次。

    • 示例:ab* 可以匹配 a, ab, abb, abbb 等。

+ :匹配前面的字符 1 次或多次。

  • 示例:ab+ 可以匹配 ab, abb, abbb,但不匹配 a

? :匹配前面的字符 0 次或 1 次。

  • 示例:ab? 可以匹配 a, ab,但不匹配 abb

^ :表示匹配行的开始。

  • 示例:^abc 匹配以 abc 开头的行。

$ :表示匹配行的结束。

  • 示例:xyz$ 匹配以 xyz 结尾的行。

[] :字符集合,匹配其中任意一个字符。

  • 示例:[abc] 可以匹配 a, b, c 中的任意一个字符。

| :逻辑或,表示匹配左边或右边的正则表达式。

  • 示例:a|b 可以匹配 ab

在 Shell 中使用正则表达式的常见工具

grep

grep 是 Linux 中最常见的用于搜索文本的命令。它通过正则表达式匹配行,并输出匹配到的行。

示例:

echo -e "apple\nbanana\ncherry" | grep 'a.p'

输出:

apple

解释:这里的正则表达式 a.p 表示匹配以 a 开头,任意字符接着,然后是 p 的字符串。

sed

sed 是一个流编辑器,常用于对文本进行替换操作,它支持使用正则表达式。

示例:

echo "hello world" | sed 's/world/universe/'

输出:

hello universe

解释:sed 's/old/new/' 的基本格式表示将 old 替换为 new,其中的 old 可以是正则表达式。

awk

awk 是一个强大的文本处理工具,能够基于正则表达式进行复杂的文本分析与处理。

示例:

echo -e "1 apple\n2 banana\n3 cherry" | awk '/apple/ {print $2}'

输出:

apple

解释:awk 中的 /apple/ 是一个正则表达式,它表示匹配包含 apple 的行,并打印该行的第二个字段。

进阶正则表达式

字符范围

使用 [] 定义字符范围,例如 [a-z] 表示小写字母,[0-9] 表示数字。

示例:

echo -e "cat\ndog\n123" | grep '[a-z]'

输出:

cat
dog

反义字符

使用 [^] 来定义一个排除的字符范围。例如,[^0-9] 表示非数字的任意字符。

示例:

echo -e "apple\nbanana\n123" | grep '[^0-9]'

输出:

apple
banana

分组与捕获

使用 () 进行分组,可以捕获匹配到的子字符串,常用于 sed 中。

示例:

echo "2024-09-11" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\2\/\3\/\1/'

输出:

09/11/2024

解释:这里使用了 \([0-9]\{4\}\) 来捕获年份,\([0-9]\{2\}\) 来捕获月份和日期,通过 \2/\3/\1 调整了它们的顺序。

重复次数

{n,m} 用来指定匹配的最小和最大次数。

示例:

echo "aaaabbbcc" | grep -o 'a\{2,3\}'

输出:

aaa

解释:a\{2,3\} 匹配连续的 2 到 3 个 a

Shell 脚本中的正则表达式应用示例

检查邮箱格式

脚本:

email="test@example.com"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid email"
else
    echo "Invalid email"
fi

解释:该脚本使用正则表达式检查邮箱的格式是否正确。

批量替换文件中的字符串

脚本:

for file in *.txt; do
    sed -i 's/old_text/new_text/g' "$file"
done

解释:该脚本使用 sed 命令在所有 .txt 文件中批量替换 old_textnew_text

提取 IP 地址

脚本:

ifconfig | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}'

解释:该命令通过正则表达式提取网络接口中的 IP 地址。

正则表达式的高级应用

非贪婪匹配

默认情况下,正则表达式是“贪婪的”,这意味着它会尽可能多地匹配字符。我们可以通过添加 ? 实现“非贪婪匹配”,即匹配尽可能少的字符。

示例:

echo "This is <b>bold</b> text" | sed -E 's/<.*>/[TAG]/'

输出:

This is [TAG] text

在上面的例子中,<.*> 是贪婪匹配,它会匹配从第一个 < 到最后一个 > 之间的所有字符。为了实现非贪婪匹配,可以使用 .*?

非贪婪匹配的示例:

echo "This is <b>bold</b> text" | sed -E 's/<.*?>/[TAG]/'

输出:

This is [TAG]bold[/TAG] text

回溯引用

回溯引用(Backreferences)允许我们在正则表达式中引用前面已经匹配的子模式。这在匹配重复的模式时非常有用。

示例:

echo "abab abcdcd abxyxy" | grep -E '([a-z]{2})\1'

输出:

abab abcdcd abxyxy

解释:这里的 ([a-z]{2})\1 表示首先匹配两个小写字母,并且接下来紧跟相同的两个字母。\1 就是对前面匹配内容的引用。

零宽断言(Lookahead 和 Lookbehind)

零宽断言是一种高级正则表达式技术,允许我们定义一个条件,但不包含在最终匹配结果中。

  • 正向前瞻(Lookahead):断言某个模式后面跟着特定的模式,但不包含该后续模式。

    示例:

    echo "apple1 banana12 cherry123" | grep -oE '[a-z]+(?=[0-9])'
    

    输出:

    apple
    banana
    cherry
    

    解释:(?=[0-9]) 是一个正向前瞻,它匹配那些后面紧跟数字的单词,但只返回单词部分,不包括数字。

  • 反向前瞻(Lookbehind):断言某个模式前面有特定的模式,但不包含该前置模式。

    示例:

    echo "id12345 id67890" | grep -oE '(?<=id)[0-9]+'
    

    输出:

    12345
    67890
    

    解释:(?<=id) 是反向前瞻,它匹配那些前面紧跟 id 的数字,但只返回数字部分。

实用场景中的正则表达式

从日志中提取 IP 地址

系统日志文件中经常包含大量的网络信息,提取 IP 地址可以帮助我们快速定位问题。

示例:

cat /var/log/syslog | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}'

解释:([0-9]{1,3}\.){3}[0-9]{1,3} 匹配标准的 IPv4 地址格式,由四组 1 到 3 位的数字构成,每组之间用点号隔开。

验证电话号码

通过正则表达式可以快速验证用户输入的电话号码格式。

示例:

phone="(123) 456-7890"
if [[ $phone =~ ^\([0-9]{3}\)\ [0-9]{3}-[0-9]{4}$ ]]; then
    echo "Valid phone number"
else
    echo "Invalid phone number"
fi

解释:这里的正则表达式 ^\([0-9]{3}\)\ [0-9]{3}-[0-9]{4}$ 匹配一个典型的美国电话号码格式,前面有三个数字的区号,并用括号括起来,后面跟着三个数字和四个数字,中间用 - 分隔。

匹配带分隔符的 CSV 数据

当处理 CSV 文件时,常常需要提取其中的某一列,利用正则表达式可以轻松实现。

示例:

echo "John,Doe,25,Engineer" | grep -oE '^[^,]+'

输出:

John

解释:^[^,]+ 匹配从行首开始的所有非逗号字符,这意味着它将提取 CSV 数据中的第一列。

提取 HTML 标签中的内容

在处理网页数据时,常常需要从 HTML 中提取特定标签的内容。利用正则表达式可以方便地处理这类需求。

示例:

echo '<div class="content">Hello World</div>' | grep -oP '(?<=<div class="content">).*?(?=</div>)'

输出:

Hello World

解释:这里使用了正向前瞻 (?<=<div class="content">) 和正向后顾 (?=</div>) 来提取位于 <div> 标签之间的文本内容。

Shell 脚本中的多种用法

多模式匹配

在处理复杂文本时,我们有时需要匹配多个模式,这可以通过正则表达式中的逻辑运算符 | 实现。

示例:

echo "apple banana cherry" | grep -E 'apple|cherry'

输出:

apple
cherry

解释:apple|cherry 表示匹配 applecherry,这是“或”操作符在正则表达式中的应用。

结合 findgrep 实现文件内容搜索

在大型项目中,我们常常需要查找包含特定内容的文件,这时可以结合 findgrep 一起使用。

示例:

find . -type f -name "*.log" | xargs grep -E 'ERROR|FATAL'

解释:find . -type f -name "*.log" 查找当前目录下所有 .log 文件,然后通过 xargs 把这些文件传递给 grep,搜索其中包含 ERRORFATAL 字样的日志。

从命令输出中提取数据

有时我们需要从命令的输出中提取某些关键信息,正则表达式可以帮助我们快速筛选和过滤。

示例:

df -h | grep -oE '[0-9]+%'

输出:

30%
45%

解释:这条命令从 df -h 的输出中提取所有的磁盘使用百分比。

性能优化与陷阱

避免过度使用贪婪匹配

贪婪匹配容易导致匹配范围过大,尤其在处理嵌套结构(如 HTML 标签)时。尽量使用非贪婪匹配来避免这个问题。

示例:

# 贪婪匹配:
echo '<div><p>text</p></div>' | sed 's/<.*>/[TAG]/'
# 输出:[TAG]

# 非贪婪匹配:
echo '<div><p>text</p></div>' | sed 's/<.*?>/[TAG]/'
# 输出:[TAG]<p>text</p>[TAG]

复杂正则的可读性

正则表达式的复杂性增加时,往往会变得难以阅读和维护。使用注释和分段处理可以提高可读性。某些语言(如 Perl 或 Python)支持带注释的正则表达式。

示例:

# Perl 中带注释的正则表达式:
$string =~ m{
    ^               # 开头
    ( [0-9]{3} )    # 捕获区号


    -               # 横线
    ( [0-9]{3} )    # 捕获前缀
    -               # 横线
    ( [0-9]{4} )    # 捕获后缀
    $               # 结尾
}x;

通过在正则表达式中添加注释,我们可以更清楚地解释每个部分的功能,增强代码的可维护性。


总结

正则表达式在 Shell 脚本中是不可或缺的工具,它能够极大地简化文本处理任务。在 grepsedawk 等常用命令中,正则表达式的应用几乎无处不在。熟练掌握正则表达式,不仅可以提升工作效率,还可以轻松处理复杂的文本操作任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XMYX-0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值