正则表达式:grep、sed、awk 中的正则(Regular Expressions in grep, sed, awk)

章节概述

本章深入讲解 Bash 生态中的正则表达式体系,对比 BRE、ERE、PCRE 三种语法标准,详细解析元字符、分组与反向引用,并展示它们在 grepsedawk 中的实际应用差异。

核心理念:正则表达式是文本模式匹配的通用语言,掌握 BRE/ERE/PCRE 的差异是高效文本处理的前提。


第1节:正则表达式基础(Regex Fundamentals)

三种语法标准

标准全称特殊字符工具支持
BREBasic Regular Expression( ) { } \+ \? 需转义grep, sed
EREExtended Regular Expression() {} + ? 直接使用grep -E, sed -E, awk
PCREPerl Compatible Regex(?:...) (?!...) \d \wgrep -P

元字符一览表

元字符含义示例匹配
.任意单字符(非换行)a.cabc, aXc
*前项零次或多次ab*cac, abc, abbc
+前项一次或多次(ERE/PCRE)ab+cabc, abbc
?前项零次或一次colou?rcolor, colour
^行首锚定^Start以 Start 开头的行
$行尾锚定end$以 end 结尾的行
[ ]字符类[aeiou]元音字母
[^ ]否定字符类[^0-9]非数字
{n,m}重复次数a{2,4}aa, aaa, aaaa
|或(交替)cat|dogcat 或 dog
( )分组(ab)+ab, abab
\b单词边界\bword\b独立的 word

第2节:grep 正则实战(grep Regex in Practice)

# BRE(默认):分组和量词需转义
grep 'ab\{2,4\}' file.txt
 
# ERE:更简洁
grep -E 'ab{2,4}' file.txt
grep -E 'cat|dog' file.txt
grep -E '(foo)+bar' file.txt
 
# PCRE:最强大
grep -P '\d{3}-\d{4}' file.txt          # 匹配电话格式
grep -P '(?!TODO)' file.txt             # 负向前瞻
grep -P '\b\w+@\w+\.\w+\b' file.txt    # 邮箱模式
 
# 常用 grep 正则选项
grep -i "pattern" file        # 忽略大小写
grep -v "pattern" file        # 反向匹配
grep -c "pattern" file        # 计数
grep -l "pattern" *.txt       # 仅显示文件名
grep -n "pattern" file        # 显示行号
grep -o "pattern" file        # 仅显示匹配部分
grep -A3 "pattern" file       # 显示匹配后3行
grep -B3 "pattern" file       # 显示匹配前3行
grep -C3 "pattern" file       # 显示匹配前后3行

grep 正则实战示例

# 查找以 # 开头的注释行(非空行)
grep -E '^\s*#' config.conf | grep -v '^$'
 
# 查找 IP 地址
grep -P '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' file.txt
 
# 查找日期格式 YYYY-MM-DD
grep -P '\b\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\b' file.txt
 
# 查找连续重复单词
grep -P '\b(\w+)\s+\1\b' file.txt
 
# 查找包含特殊字符的行
grep -F 'literal.string.with.dots' file.txt  # -F 禁用正则

第3节:sed 正则实战(sed Regex in Practice)

# BRE(默认)
sed 's/old/new/' file.txt
sed 's/\(foo\)\(bar\)/\2\1/' file.txt    # 交换 foo 和 bar
 
# ERE(-E 或 -r)
sed -E 's/(foo)(bar)/\2\1/' file.txt
 
# sed 中的元字符
sed 's/a.b/xxx/g' file.txt        # . 匹配任意字符
sed 's/ab*/xxx/g' file.txt        # * 零次或多次
sed 's/[0-9]//g' file.txt         # 删除数字
sed 's/^[[:space:]]*//' file.txt  # 删除前导空白

sed 正则高级用法

# 地址范围 + 正则
sed '/^BEGIN/,/^END/d' file.txt        # 删除 BEGIN 到 END 之间的内容
sed '/^server {/,/^}/s/charset.*/charset utf-8;/' nginx.conf
 
# 分组与反向引用
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' dates.txt
 
# 条件替换
sed -E '/^[0-9]+$/s/^/ID: /' file.txt   # 仅对数字行添加前缀
 
# 多行模式空间
sed -N 's/\n/ /g' file.txt              # 合并相邻两行

第4节:awk 正则实战(awk Regex in Practice)

# awk 使用 ERE 语法
awk '/pattern/ {print $1}' file.txt
awk '$0 ~ /^[0-9]+$/' file.txt          # 全行为数字
awk '!/^#/' file.txt                     # 过滤注释行
 
# awk 正则 vs 字符串匹配
awk '$3 == "error"' file.txt             # 精确匹配
awk '$3 ~ /error/' file.txt              # 正则匹配
awk '$3 !~ /debug/' file.txt             # 正则否定匹配

awk 正则高级用法

# 使用正则分割字段
echo "key=value" | awk -F'[=:]' '{print $1, $2}'
 
# 多条件正则
awk '/error/ && /timeout/' logfile.txt
awk '/error/ || /critical/' logfile.txt
 
# 正则捕获组(需用 match 函数)
echo "2024-01-15" | awk '{
    match($0, /([0-9]{4})-([0-9]{2})-([0-9]{2})/, arr)
    print "Year:", arr[1], "Month:", arr[2], "Day:", arr[3]
}'
 
# 字段级正则
awk '{gsub(/old/, "new"); print}' file.txt
awk 'BEGIN{FS="[ \t]+"} {print $NF}' file.txt  # 最后一个字段

第5节:分组与反向引用(Groups & Back-references)

分组语法对比

特性BREEREPCRE
普通分组\(ab\)(ab)(ab)
非捕获分组(?:ab)
命名分组(?<name>ab)
反向引用\1 \2\1 \2\1 \k<name>
前瞻(?=ab) (?!ab)
后顾(?<=ab) (?<!ab)

反向引用实战

# grep:查找重复单词
grep -P '\b(\w+)\s+\1\b' file.txt
 
# sed:交换字段
echo "hello world" | sed -E 's/(\w+) (\w+)/\2 \1/'
# 输出:world hello
 
# sed:给第一个单词加引号
echo "hello world" | sed -E 's/^(\w+)/"\1"/'
# 输出:"hello" world
 
# awk:提取分组(使用 match + 数组)
echo "2024-01-15" | awk '{
    match($0, /([0-9]{4})-([0-9]{2})-([0-9]{2})/, a)
    print a[1]"/"a[2]"/"a[3]
}'

第6节:PCRE 高级特性(PCRE Advanced Features)

# 负向前瞻:匹配后面不跟特定模式的文本
grep -P 'error(?!_ignored)' file.txt
 
# 正向前瞻:匹配后面跟特定模式的文本
grep -P '\d+(?= dollars)' file.txt
 
# 负向后顾:匹配前面不跟特定模式的文本
grep -P '(?<!\$)\d+\.\d+' file.txt    # 不匹配货币格式
 
# 占有量词(避免回溯)
grep -P 'a++b' file.txt
 
# 原子组
grep -P '(?>ab|a)b' file.txt
 
# Unicode 支持
grep -P '\p{Han}' chinese.txt          # 匹配中文字符
grep -P '\p{L}+' text.txt              # 匹配任何字母

PCRE 修饰符

# 忽略大小写(内联)
grep -P '(?i)hello' file.txt
 
# 多行模式(^ $ 匹配每行)
grep -P '(?m)^start' file.txt
 
# 点号匹配换行
grep -P '(?s)start.*end' file.txt
 
# 扩展模式(允许空白和注释)
grep -P -x '(?x)
    \d{4}      # year
    -          # separator
    \d{2}      # month
    -          # separator
    \d{2}      # day
' dates.txt

第7节:BRE vs ERE vs PCRE 完整对比(Complete Comparison)

场景BREEREPCRE推荐
简单匹配grep 'pattern'grep -E 'pattern'grep -P 'pattern'BRE
量词ab\{2,4\}ab{2,4}ab{2,4}ERE
分组\(ab\)(ab)(ab)ERE
a|ba|ba|bERE
前瞻后顾(?=...)PCRE
Unicode\p{Han}PCRE
可读性最好PCRE

跨工具一致性建议

# 统一使用 ERE(最通用)
grep -E 'pattern' file
sed -E 's/pattern/repl/' file
awk '/pattern/ {action}' file
 
# 需要高级功能时使用 PCRE
grep -P '\d{3}-\d{4}' file
perl -ne 'print if /pattern/' file

本章要点总结

  • BRE 需要转义 (){},ERE/PCRE 直接使用
  • PCRE 支持前瞻、后顾、命名分组、Unicode
  • grep -E 获得 ERE,grep -P 获得 PCRE
  • sed 默认 BRE,-E 切换到 ERE
  • awk 使用 ERE 语法
  • 优先使用 ERE 保持跨工具一致性

上一章管道与重定向
下一章sed 流编辑器