sed的分组替换,捕获组,反向引用 笔记250419

620 阅读14分钟

sed的分组替换,捕获组,反向引用 笔记250419

sed 的分组替换功能通过正则表达式中的 捕获组反向引用 实现,,通过分组(正则表达式捕获)和反向引用实现复杂文本重组:


一、分组替换的核心语法和概念

1. 基本格式
sed 's/\(分组正则\)/替换内容/' 文件
# 或使用扩展正则(-r 或 -E)
sed -r 's/(分组正则)/替换内容/' 文件
  • 分组:用 \(...\)(基础正则)或 (...)(扩展正则)标记要捕获的内容。
  • 反向引用:用 \1, \2, ... 按顺序引用分组。
2. 操作流程
  1. 正则表达式匹配文本。
  2. \(...\) 包裹的内容捕获为分组(按左括号顺序编号)。
  3. 在替换内容中通过 \1, \2 等引用分组值。
1. 捕获组(Capturing Group)
  • 作用:用括号 \(...\) 标记正则表达式的子模式,临时保存匹配内容。
  • 语法
    • 基础正则:必须转义括号 → \(pattern\)
    • 扩展正则sed -rsed -E):直接使用 (pattern)
  • 编号规则:按左括号出现的顺序,从 \1\9
2. 反向引用(Backreference)
  • 作用:在替换内容中引用捕获组的值。
  • 语法\1, \2...\9(按分组顺序引用)。

二、典型场景示例

1. 交换字段位置
# 将 "LastName, FirstName" 改为 "FirstName LastName"
echo "Doe, John" | sed -r 's/([A-Za-z]+),\s*([A-Za-z]+)/\2 \1/'
# 输出:John Doe
  • 分组1:([A-Za-z]+)Doe
  • 分组2:([A-Za-z]+)John

示例:日期格式转换

# 将 "YYYY-MM-DD" 转为 "DD/MM/YYYY"
echo "2023-01-01" | sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 输出:01/01/2023

此处: • ([0-9]{4}) 匹配年份 → \1

([0-9]{2}) 匹配月份 → \2

([0-9]{2}) 匹配日期 → \3

2. 格式化数据
# 将电话号码 "1234567890" 格式化为 "(123) 456-7890"
echo "1234567890" | sed -r 's/([0-9]{3})([0-9]{3})([0-9]{4})/(\1) \2-\3/'
# 输出:(123) 456-7890
3. 提取特定内容
# 从日志中提取 IP 地址(假设日志格式:IP - [时间] "请求")
echo "192.168.1.1 - [2023-10-05] GET /index.html" | sed -r 's/([0-9.]+).*/\1/'
# 输出:192.168.1.1
4. 重复内容合并
# 合并重复单词(如 "hello hello" → "hello")
echo "hello hello world" | sed -r 's/\b(\w+)\b \b\1\b/\1/g'
# 输出:hello world
1. 基本分组替换
# 基础正则(转义括号)
echo "abc123" | sed 's/\([a-z]\+\)\([0-9]\+\)/\2-\1/'
# 输出:123-abc

# 扩展正则(-r 简化语法)
echo "abc123" | sed -r 's/([a-z]+)([0-9]+)/\2-\1/'
2. 交换字段位置
# 将 "姓, 名" 改为 "名 姓"
echo "Doe, John" | sed -r 's/([A-Za-z]+),\s*([A-Za-z]+)/\2 \1/'
# 输出:John Doe
3. 格式化数据
# 将 "20231005" 格式化为 "2023-10-05"
echo "20231005" | sed -r 's/([0-9]{4})([0-9]{2})([0-9]{2})/\1-\2-\3/'
1. 基本分组替换
# 基础正则(转义括号)
echo "abc123" | sed 's/\([a-z]\+\)\([0-9]\+\)/\2-\1/'
# 输出:123-abc

# 扩展正则(-r 简化语法)
echo "abc123" | sed -r 's/([a-z]+)([0-9]+)/\2-\1/'
2. 交换字段位置
# 将 "姓, 名" 改为 "名 姓"
echo "Doe, John" | sed -r 's/([A-Za-z]+),\s*([A-Za-z]+)/\2 \1/'
# 输出:John Doe
3. 格式化数据
# 将 "20231005" 格式化为 "2023-10-05"
echo "20231005" | sed -r 's/([0-9]{4})([0-9]{2})([0-9]{2})/\1-\2-\3/'
1. 日志处理(提取关键信息)
# 从日志中提取 IP 和请求路径
echo "192.168.1.1 - [2023-10-05] GET /index.html HTTP/1.1" | sed -r 's/([0-9.]+).*GET (\/[^ ]+).*/\1 \2/'
# 输出:192.168.1.1 /index.html
2. 配置文件修改(键值重组)
# 将 "key=value" 改为 "value: key"
echo "name=John" | sed -r 's/([^=]+)=([^ ]+)/\2: \1/'
# 输出:John: name
3. 批量重命名文件
# 将 "img_001.jpg" 重命名为 "001_photo.jpg"
find . -name "img_*.jpg" | sed -r 's/(.*\/)img_([0-9]+)\.jpg/mv & \1\2_photo.jpg/' | sh
1. 重构 CSV 文件
# 原始数据:Name,Age,Country → 改为格式:Country | Age | Name
echo "John,30,USA" | sed -r 's/([^,]+),([^,]+),([^,]+)/\3 | \2 | \1/'
# 输出:USA | 30 | John
2. 清理 HTML 标签
# 删除所有 HTML 标签,保留文本
sed -r 's/<[^>]+>//g' index.html
3. 重命名文件
# 批量将 "file_001.txt" 改为 "001_file.txt"
find . -name "file_*.txt" | sed -r 's/(.*)file_([0-9]+)\.txt/mv "&" "\2_file.txt"/' | sh

三、进阶技巧

1. 多级分组与嵌套
# 重组日期时间 "2023-10-05 14:30:00" → "05/10/2023 14-30-00"
echo "2023-10-05 14:30:00" | sed -r 's/(([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/\3\/\2\/\1 \4-\5-\6/'
# 输出:05/10/2023 14-30-00
  • 分组1:完整日期 2023-10-05
  • 分组2:年 2023, 分组3:月 10, 分组4:日 05
  • 分组5:时 14, 分组6:分 30, 分组7:秒 00
2. 动态调整内容
# 在匹配的单词两侧添加标记(如 "apple" → "[APPLE]")
echo "apple banana" | sed -r 's/(\b\w+\b)/[\U\1\E]/g'
# 输出:[APPLE] [BANANA]
# \U 转大写,\E 结束转换
3. 结合 Shell 变量
# 动态替换前缀(如将 "key=value" 改为 "PREFIX_key=value")
prefix="NEW"
echo "key=value" | sed -r "s/(^key=)/${prefix}_\1/"
# 输出:NEW_key=value
1. 多级分组与嵌套
# 重组 "2023-10-05 14:30:00" → "05/10/2023 14-30-00"
echo "2023-10-05 14:30:00" | sed -r 's/(([0-9]{4})-([0-9]{2})-([0-9]{2})) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/\4\/\3\/\1 \6-\7-\8/'
# 分组解析:
# \1: 2023-10-05, \2:2023, \3:10, \4:05
# \5:14:30:00, \6:14, \7:30, \8:00
2. 动态大小写转换
# 将单词首字母大写(如 "apple" → "Apple")
echo "apple banana" | sed -r 's/\b([a-z])(\w+)\b/\u\1\2/g'
# 输出:Apple Banana
  • \u:将下一个字符转为大写(GNU sed 特有)。
3. 非捕获组(间接实现)

⚠️ sed 不支持 (?:) 语法,但可通过分组编号跳过:

# 提取第二个匹配组(忽略第一个)
echo "abc123xyz" | sed -r 's/([a-z]+)([0-9]+)([a-z]+)/\2/'
# 输出:123

四、常见问题与解决

1. 分组未生效
  • 原因:括号未转义(基础正则)或未启用扩展正则。 未正确使用 \(...\) 或扩展正则。
  • 解决
    # 错误示例(基础正则未转义)
    echo "123" | sed 's/([0-9]+)/X\1X/'  # 无效果
    
    # 正确写法
    echo "123" | sed 's/\([0-9]\+\)/X\1X/'      # 基础正则
    echo "123" | sed -r 's/([0-9]+)/X\1X/'     # 扩展正则
    
2. 特殊字符处理
  • 场景:替换内容包含 /&
  • 解决:更换分隔符(如 |, #)。
    # 替换 URL 路径(避免转义)
    sed 's|/old/path|/new/path|g' file.txt
    
    # 使用 & 引用整个匹配内容
    echo "test" | sed 's/.*/&_end/'  
    # 输出:test_end
    
3. 跨行匹配
  • 限制sed 默认逐行处理,需用 N, D, P 命令处理多行。
    # 合并两行并用逗号分隔
    sed 'N; s/\n/, /' file.txt
    

总结

  • 核心能力:基础正则通过 \(...\) 扩展正则通过 (...) 捕获内容 → \1, \2 重组文本。










1️⃣ 参考 1

sed 的分组替换功能通过正则表达式中的 捕获组反向引用 实现,可以灵活重组或修改文本内容。以下是详细用法和示例:


语法核心

  1. 捕获组:用 \(...\) 定义(基础正则)或 (...)(扩展正则)。
  2. 反向引用:用 \1, \2 等引用第1、2个捕获组。

启用扩展正则

  • Linux:使用 -r-E 选项(简化语法,无需转义 ())。
  • macOS:仅支持 -E(需注意 BSD 与 GNU 的差异)。

常见场景示例

1. 交换两列数据
# 输入:Last,First → 输出:First Last
echo "Doe,John" | sed -E 's/([^,]+),([^,]+)/\2 \1/'
# 结果:John Doe
2. 重组日期格式
# 输入:YYYY-MM-DD → 输出:DD/MM/YYYY
echo "2023-10-05" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 结果:05/10/2023
3. 格式化电话号码
# 输入:(123)456-7890 → 输出:123-456-7890
echo "(123)456-7890" | sed -E 's/\(([0-9]{3})\)([0-9]{3})-([0-9]{4})/\1-\2-\3/'
# 结果:123-456-7890
4. 提取并重组 URL
# 输入:https://www.example.com/path → 输出:Domain: example.com
echo "https://www.example.com/path" | sed -E 's|https?://(www\.)?([^/]+).*|\2|'
# 结果:example.com
5. 重复单词处理
# 输入:hello hello world → 输出:hello world
echo "hello hello world" | sed -E 's/(\b\w+\b) \1/\1/g'
# 结果:hello world

高级用法

1. 嵌套分组
# 输入:<div>Hello</div> → 输出:<p>Hello</p>
echo "<div>Hello</div>" | sed -E 's/<(\w+)>(.*)<\/\1>/<\1>\2<\/p>/'
# 结果:<div>Hello</p>(若需完全替换标签,需调整正则)
2. 条件重组
# 将 "key=value" 转换为 "value=key"
echo "name=John" | sed -E 's/([^=]+)=([^=]+)/\2=\1/'
# 结果:John=name
3. 多分组联合操作
# 输入:1.2.3 → 输出:Version: 1.2.3
echo "1.2.3" | sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+)/Version: \1.\2.\3/'

注意事项

  1. 转义特殊字符:若使用基础正则(无 -r/-E),需用 \(\) 代替 (...)
  2. 分隔符冲突:若内容包含 /,可改用 #| 作为分隔符:
    sed -E 's#(http://[^/]+)/.*#\1#' file.txt
    
  3. 分组数量:确保反向引用序号(如 \1)不超过捕获组数量。

sed 的分组替换通过正则表达式的 捕获组(Capturing Groups)和 反向引用(Back References)实现,可以灵活提取、重组或修改文本中的特定部分。以下是详细解释和示例:


核心概念

  1. 捕获组
    用括号 \(...\)(基础正则)或 (...)(扩展正则)将一部分正则表达式括起来,形成一个“组”。

    • 基础正则(BRE):需转义括号(\(\))。
    • 扩展正则(ERE):使用 -r(Linux)或 -E(macOS)选项,无需转义括号。
  2. 反向引用
    在替换部分用 \1, \2, \3 等引用第1、2、3个捕获组的内容。


基础语法

# 基础正则(BRE)
sed 's/\(pattern1\)\(pattern2\)/\2\1/' file.txt

# 扩展正则(ERE,更简洁)
sed -r 's/(pattern1)(pattern2)/\2\1/' file.txt  # Linux
sed -E 's/(pattern1)(pattern2)/\2\1/' file.txt  # macOS

常见场景示例

1. 交换两列数据
# 输入:Last,First → 输出:First Last
echo "Doe,John" | sed -E 's/([^,]+),([^,]+)/\2 \1/'
# 结果:John Doe
  • ([^,]+):匹配非逗号字符(直到遇到逗号),作为第一组(\1)。
  • ([^,]+):匹配剩余非逗号字符,作为第二组(\2)。

2. 重组日期格式
# 输入:YYYY-MM-DD → 输出:DD/MM/YYYY
echo "2023-10-05" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 结果:05/10/2023
  • ([0-9]{4}):捕获年(\1)。
  • ([0-9]{2}):捕获月(\2)和日(\3)。

3. 提取并重组 URL
# 输入:https://www.example.com/path → 输出:example.com
echo "https://www.example.com/path" | sed -E 's|https?://(www\.)?([^/]+).*|\2|'
  • ([^/]+):匹配域名部分(\2)。
  • www\.? 匹配可选的 "www."(\1,但未在替换中使用)。

4. 删除重复单词
# 输入:hello hello world → 输出:hello world
echo "hello hello world" | sed -E 's/(\b\w+\b) \1/\1/g'
  • (\b\w+\b):捕获一个完整单词(\1)。
  • \1:匹配重复的单词并替换为单个。

5. 格式化电话号码
# 输入:(123)456-7890 → 输出:123-456-7890
echo "(123)456-7890" | sed -E 's/\(([0-9]{3})\)([0-9]{3})-([0-9]{4})/\1-\2-\3/'
  • ([0-9]{3}):分别捕获区号(\1)、前缀(\2)和号码(\3)。

高级用法

1. 嵌套分组
# 输入:<div>Hello</div> → 输出:<p>Hello</p>
echo "<div>Hello</div>" | sed -E 's/<(\w+)>(.*)<\/\1>/<p>\2<\/p>/'
  • <(\w+)>:捕获标签名(\1)。
  • <\/\1>:匹配闭合标签(如 </div>)。

2. 多分组联合操作
# 输入:key=value → 输出:value=key
echo "name=John" | sed -E 's/([^=]+)=([^=]+)/\2=\1/'
  • ([^=]+):捕获键(\1)和值(\2)。

3. 动态替换文件名
# 输入:file_001.txt → 输出:file_002.txt
echo "file_001.txt" | sed -E 's/([0-9]+)/printf "%03d" $((\1+1))/e'
  • 使用 e 标志执行 Shell 命令(需 GNU sed)。

注意事项

  1. 启用扩展正则

    • Linux:sed -r
    • macOS:sed -E
  2. 转义特殊字符

    • 若使用基础正则(无 -r/-E),需转义括号:\(\)
  3. 分隔符冲突

    • 若内容包含 /,可改用 #| 作为分隔符:
      sed -E 's#(http://[^/]+)/.*#\1#' file.txt
      
  4. 分组顺序

    • 分组按左括号出现的顺序编号(从左到右)。

通过灵活使用捕获组和反向引用,你可以高效处理复杂的文本重组和替换任务!










2️⃣ 参考 2

sed 分组替换详解:用正则表达式精准操作文本

sed 的分组替换功能通过正则表达式捕获组(())和反向引用(\1, \2 等)实现,适合处理复杂文本模式。以下是深度解析和示例:


一、基本分组替换

语法

sed 's/\(pattern1\)\(pattern2\)/替换内容/g' file.txt
  • ():定义捕获组,pattern1 为第一个组,pattern2 为第二个组。
  • \1, \2:在替换内容中引用捕获组。

示例:交换单词顺序

echo "apple banana" | sed 's/\(apple\) \(banana\)/\2 \1/'
# 输出:banana apple

二、多个捕获组

场景:提取 URL 协议并格式化

echo "https://example.com" | sed -r 's/(http|https)://(\S+)/URL: \2 (Protocol: \1)/g'
# 输出:URL: example.com (Protocol: https)
  • -r:启用扩展正则表达式(支持 |+ 等)。
  • (\S+):匹配非空格字符(URL 主体)。

三、修改捕获组内容

场景:给日志时间戳加前缀

echo "1625432100 Error: File not found" | sed -r 's/([0-9]{10}) (Error)/\1 [TIMESTAMP] \2/'
# 输出:1625432100 [TIMESTAMP] Error: File not found
  • [0-9]{10}:匹配时间戳。
  • \1 [TIMESTAMP] \2:在捕获组 1 后插入前缀。

四、结合其他命令

场景:匹配特定行并替换后打印

sed -n '/DEBUG/ {s/\(old\)/\1_new/; p}' file.txt
  • -n:安静模式(不自动打印)。
  • /DEBUG/:匹配包含 DEBUG 的行。
  • p:打印修改后的行。

五、非捕获组

语法

sed 's/pattern\(?:non-captured\)replace/g' file.txt
  • ?::定义非捕获组(不存储匹配内容)。

示例:忽略部分内容

echo "a1b2c3" | sed 's/[a-z]\(:\?[0-9]\)/X/g'
# 输出:XbXcX(非捕获组 `?:` 不参与引用)

六、扩展正则表达式

选项-r-E(启用扩展正则)。

示例:匹配多种模式

echo "apple orange grape" | sed -r 's/(apple|orange)/\U\1/g'
# 输出:APPLE orange grape(`\U` 将捕获组转为大写)

七、实际应用场景

  1. 日志分析:提取时间、错误类型等关键信息。
    sed -E 's/(.*Error): (.*)/[\1] \2/g' log.txt
    
  2. 配置文件修改:批量替换参数值。
    sed -i -r 's/(^server=).*/\1new_server/g' config.conf
    
  3. 数据清洗:格式化输出。
    echo "Name: Alice; Age: 30" | sed -r 's/(Name: )(.*); (Age: )(.*)/\2 (\4 years old)/'
    # 输出:Alice (30 years old)
    


sed 分组替换、捕获组与反向引用详解

sed 的分组替换功能通过正则表达式捕获组(())和反向引用(\1, \2 等)实现,适合处理复杂文本模式。以下是深度解析和示例:


一、分组替换基础

语法

sed 's/\(pattern1\)\(pattern2\)/替换内容/g' file.txt
  • ():定义捕获组,pattern1 为第一个组,pattern2 为第二个组。
  • \1, \2:在替换内容中引用捕获组。

示例:交换单词顺序

echo "apple banana" | sed 's/\(apple\) \(banana\)/\2 \1/'
# 输出:banana apple

二、捕获组与反向引用

1. 多个捕获组

场景:提取 URL 协议并格式化

echo "https://example.com" | sed -r 's/(http|https)://(\S+)/URL: \2 (Protocol: \1)/g'
# 输出:URL: example.com (Protocol: https)
  • -r:启用扩展正则表达式(支持 |+ 等)。
  • (\S+):匹配非空格字符(URL 主体)。
2. 修改捕获组内容

场景:给日志时间戳加前缀

echo "1625432100 Error: File not found" | sed -r 's/([0-9]{10}) (Error)/\1 [TIMESTAMP] \2/'
# 输出:1625432100 [TIMESTAMP] Error: File not found
  • [0-9]{10}:匹配时间戳。
  • \1 [TIMESTAMP] \2:在捕获组 1 后插入前缀。
3. 结合其他命令

场景:匹配特定行并替换后打印

sed -n '/DEBUG/ {s/\(old\)/\1_new/; p}' file.txt
  • -n:安静模式(不自动打印)。
  • /DEBUG/:匹配包含 DEBUG 的行。
  • p:打印修改后的行。

三、非捕获组

语法

sed 's/pattern\(?:non-captured\)replace/g' file.txt
  • ?::定义非捕获组(不存储匹配内容)。

示例:忽略部分内容

echo "a1b2c3" | sed 's/[a-z]\(:\?[0-9]\)/X/g'
# 输出:XbXcX(非捕获组 `?:` 不参与引用)

四、扩展正则表达式

选项-r-E(启用扩展正则)。

示例:匹配多种模式

echo "apple orange grape" | sed -r 's/(apple|orange)/\U\1/g'
# 输出:APPLE orange grape(`\U` 将捕获组转为大写)

五、实际应用场景

  1. 日志分析:提取时间、错误类型等关键信息。
    sed -E 's/(.*Error): (.*)/[\1] \2/g' log.txt
    
  2. 配置文件修改:批量替换参数值。
    sed -i -r 's/(^server=).*/\1new_server/g' config.conf
    
  3. 数据清洗:格式化输出。
    echo "Name: Alice; Age: 30" | sed -r 's/(Name: )(.*); (Age: )(.*)/\2 (\4 years old)/'
    # 输出:Alice (30 years old)
    

六、注意事项

  1. 特殊字符转义:如 /& 需用 \ 转义。
  2. 跨平台兼容性:macOS 的 sed 需用 -E 代替 -r
  3. 备份文件:使用 -i 直接修改文件前,建议备份。

通过分组替换、捕获组和反向引用,sed 能高效处理复杂文本模式,是日志分析、配置管理等场景的利器。










3️⃣ 参考 3

以下是关于 sed分组替换、捕获组和反向引用的详细解析,结合实际示例和常见问题说明:


1. 基础概念

(1) 捕获组(Capture Groups)
  • 定义:通过正则表达式中的 \(...\)(或 (...) 在扩展正则表达式中)定义的分组,捕获匹配的文本。
  • 作用:将匹配的子字符串保存到内存中,以便后续引用。
(2) 反向引用(Backreferences)
  • 定义:在 sed 的替换命令中,通过 \1\2 等引用前面捕获的分组内容。
  • 适用范围:只能引用当前正则表达式中定义的捕获组。

2. 分组替换语法

基本语法
sed 's/正则表达式\(分组1\).*(分组N)/replacement\1\2.../flags' 文件
  • 分组定义:用 \(...\) 捕获内容(BRE模式)或 (...)(ERE模式)。
  • 反向引用:在替换部分使用 \1\2 等引用分组内容。
ERE模式(扩展正则表达式)

需通过 -E-r 参数启用:

sed -E 's/(pattern1)(pattern2)/replacement\1\2/' 文件

3. 示例详解

示例1:提取字段并重组

需求:将 CSV 文件中的 name,age 格式改为 age:name
输入

Alice,30
Bob,25

命令

sed 's/\([^,]*\),\(.*\)/\2:\1/' input.txt

解释

  • \([^,]*\):捕获第一个字段(name,分组1)。
  • ,:匹配分隔符。
  • \([^,]*\):捕获第二个字段(age,分组2)。
  • 替换为 \2:\1,即 age:name

输出

30:Alice
25:Bob

示例2:替换特殊字符

需求:将 URL 中的路径部分替换为其他内容。
输入

https://example.com/path/to/resource

命令

# 使用 "%" 作为分隔符,避免斜杠冲突
sed 's%\(\w+://[^/]*\)/\(.*\)%\1/new/path%g' input.txt

解释

  • \(\w+://[^/]*\):捕获协议和域名(分组1)。
  • /:匹配路径分隔符。
  • \(.*):捕获路径(分组2)。
  • 替换为 \1/new/path

输出

https://example.com/new/path

示例3:反向引用失效问题

问题:当正则表达式中使用 |(或条件)时,未参与匹配的分组的反向引用会失效。
输入

aau
ba

命令

# 意图:匹配 "aau" 或 "ba"
sed -E '/(a)\1u|b\1/ p' input.txt

结果

  • aau 被匹配(\1 引用第一个 a)。
  • ba 未匹配(第二个条件中的 \1 未被定义)。

修正方法

# 分开处理两个条件
sed -E '/(a)\1u/ p; /ba/ p' input.txt

示例4:替换密码字段

需求:将 /etc/shadow 文件的最后一行密码替换为指定值。
输入的最后一行

userX:$6$hS4yqJu7WQfGlk0M$Xj/SCS5z4BWSZKN0raNncu6VMuWdUVbDScMYxOgB7mXUj./dXJN0zADAXQUMg0CuWVRyZUu6npPLWoyv8eXPA.::0:99999:7:::

命令

old_pass=$(tail -n 1 /etc/shadow | cut -d':' -f2)
new_pass='$1$123456$wOSEtcyiP2N/IfIl15W6Z0'
sed -i '$ s%'"$old_pass"'%'"$new_pass"'%g' /etc/shadow

解释

  • % 作为分隔符,避免 /$ 的转义问题。
  • $ 表示最后一行,替换最后一行的密码字段。

4. 常见问题与解决方案

(1) 特殊字符处理
  • 问题:替换内容包含 /$ 等特殊字符。
  • 解决方案:使用其他分隔符(如 #|%):
    # 替换路径中的 "/"
    sed 's#old/path/new#new/path/updated#g' file.txt
    
(2) 反向引用编号限制
  • 限制sed 支持最多 9 个分组\1\9)。
  • 嵌套分组:按左括号顺序编号,例如:
    sed 's/\(\(\w+\)\.\(\w+\)\)/\3.\2/'  # 分组1: 整个匹配,分组2: 第一个词,分组3: 第二个词
    
(3) BRF 与 ERE 的区别
  • BRE(默认)
    sed 's/\(pattern\)/replacement\1/' file.txt
    
  • ERE(通过 -E-r
    sed -E 's/(pattern)/replacement\1/' file.txt
    

5. 高级技巧

(1) 多行处理

通过 N 命令将多行合并到模式空间,再进行分组操作:

# 将连续两行合并为一行,并用逗号分隔
sed -e '{N;s/\n/,/}' file.txt
(2) 非捕获组

使用 ?: 忽略某些分组的捕获(避免占用编号):

sed -E 's/(a)(?:b)(c)/\1\2/'  # 只捕获 "a" 和 "c",忽略 "b"
(3) 动态替换

结合变量和命令替换:

# 将文件中的 "old" 替换为当前时间
sed "s/old/$(date +%Y-%m-%d)/g" file.txt

6. 总结

  • 分组替换:通过 \(...\) 定义捕获组,用 \1\2 引用。
  • 特殊字符:使用其他分隔符(如 %)避免冲突。
  • 反向引用失效:确保分组在匹配路径中被使用。
  • ERE模式:简化语法,提升可读性。

通过灵活运用这些技巧,可以高效处理复杂的文本替换任务,例如字段重组、敏感信息脱敏、配置文件修改等。










4️⃣ 参考 4

sed 是一个强大的流编辑器,分组替换、捕获组和反向引用是其处理文本时非常实用的特性,下面为你详细介绍这些概念并给出示例。

1. 捕获组(Capturing Groups)

捕获组就是把正则表达式中一部分用括号括起来,被括起来的部分匹配到的内容会被保存起来,后续可以通过反向引用使用这些内容。

  • 基本正则表达式(BRE):在基本正则表达式里,使用 \(\) 来定义捕获组。
  • 扩展正则表达式(ERE):使用 -r 选项开启扩展正则表达式时,直接用 () 来定义捕获组。

2. 反向引用(Backreferences)

反向引用允许你在替换部分引用之前捕获组所匹配的内容。通过 \nn 是数字)来引用对应的捕获组,\1 代表第一个捕获组,\2 代表第二个捕获组,依此类推。

3. 分组替换示例

示例 1:交换姓名顺序(基本正则表达式)

假设你有一个文件 names.txt,内容如下:

Alice Brown
Bob Green
Charlie Black

你想把名字和姓氏的顺序交换,以下是使用基本正则表达式实现的 sed 命令:

sed 's/\(.*\) \(.*\)/\2, \1/' names.txt
  • \(.*\) \(.*\)
    • 第一个 \(.*\) 捕获第一个单词(名字)。
    • 第二个 \(.*\) 捕获第二个单词(姓氏)。
    • 中间的空格用于匹配名字和姓氏之间的空格。
  • \2, \1:在替换部分,\2 引用第二个捕获组(姓氏),\1 引用第一个捕获组(名字),并在它们之间添加了逗号和空格。

执行该命令后,输出结果如下:

Brown, Alice
Green, Bob
Black, Charlie
示例 2:日期格式转换(扩展正则表达式)

假设你有一个文件 dates.txt,内容如下:

2025-04-19
2025-05-20

你想把日期格式从 YYYY-MM-DD 转换为 MM/DD/YYYY,可以使用以下 sed 命令:

sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\2\/\3\/\1/' dates.txt
  • ([0-9]{4})-([0-9]{2})-([0-9]{2})
    • 第一个 ([0-9]{4}) 捕获年份。
    • 第二个 ([0-9]{2}) 捕获月份。
    • 第三个 ([0-9]{2}) 捕获日期。
  • \2\/\3\/\1:在替换部分,\2 引用月份,\3 引用日期,\1 引用年份,并使用 / 作为分隔符。

执行该命令后,输出结果如下:

04/19/2025
05/20/2025
示例 3:提取和重组电子邮件地址(扩展正则表达式)

假设你有一个文件 emails.txt,内容如下:

john.doe@example.com
jane.smith@example.org

你想把电子邮件地址中的用户名和域名提取出来,然后重新组合成 域名:用户名 的格式,以下是对应的 sed 命令:

sed -r 's/([^@]+)@([^.]+)\.(.+)/\2.\3:\1/' emails.txt
  • ([^@]+)@([^.]+)\.(.+)
    • 第一个 ([^@]+) 捕获 @ 之前的用户名。
    • 第二个 ([^.]+) 捕获域名的第一部分。
    • 第三个 (.+) 捕获域名的其余部分。
  • \2.\3:\1:在替换部分,\2\3 组合成完整的域名,\1 引用用户名,中间用 : 分隔。

执行该命令后,输出结果如下:

example.com:john.doe
example.org:jane.smith










5️⃣ 参考 5

sed 中,分组替换、捕获组和反向引用是密切相关且强大的功能组合,它们允许你灵活地处理和转换文本。它们通常结合正则表达式使用,以实现复杂的文本操作。

分组替换、捕获组和反向引用的概念

  • 捕获组 :在 sed 的正则表达式中,通过使用圆括号 \(pattern\) 来定义捕获组,可以提取出匹配的部分文本用于后续的处理。
  • 反向引用 :在替换部分中,可以使用 \1\2 等来引用之前捕获的组内容。\1 代表第一个捕获组的内容,\2 代表第二个捕获组的内容,依此类推。

分组替换、捕获组和反向引用的操作

示例 1:简单的分组替换
echo "John Doe" | sed 's/\(John\) \(Doe\)/\2, \1/'

输出:Doe, John

解释:\(\)pattern\) 捕获了两个组,分别是 JohnDoe,然后在替换部分通过 \1\2 引用这些捕获的组。

示例 2:提取和重新排列日期格式
echo "2023-10-05" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3-\2-\1/'

输出:05-10-2023

解释:捕获组 \([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\) 分别捕获了年份、月份和日期,然后在替换部分按新格式重新排列。

示例 3:提取特定信息
echo "[ERROR] 2023-10-05 12:30:45 - Invalid user input" | sed 's/\[\([A-Z]*\)\] \([0-9-]* [0-9:]*\).*$/\1: \2/'

输出:ERROR: 2023-10-05 12:30:45

解释:捕获组 \([A-Z]*\) 捕获了日志级别,\([0-9-]* [0-9:]*\) 捕获了日期和时间,\1\2 引用这些捕获的组组合成新的格式。

示例 4:复杂文本的重新排列
echo "file.txt:10:This is an error message" | sed 's/\([a-zA-Z0-9.]*\):\([0-9]*\):\([^:]*\)/Line \2 in \1: \3/'

输出:Line 10 in file.txt: This is an error message

解释:捕获组 \([a-zA-Z0-9.]*\) 捕获了文件名,\([0-9]*\) 捕获了行号,\([^:]*\) 捕获了消息内容,然后在替换部分按新格式重新排列。

总结

通过 sed 的分组替换、捕获组和反向引用功能,你可以从文本中提取特定部分并重新排列或转换它们。这在文本处理中非常有用,可以灵活地实现各种文本格式转换和数据提取需求。










6️⃣ 参考 6

sed中的分组替换涉及到捕获组和反向引用的概念。以下是对这两个概念的中文解释及示例:

捕获组

捕获组是通过在正则表达式中使用圆括号()来定义的。当正则表达式匹配成功时,括号内的内容会被捕获并保存起来,以便后续使用。

示例

假设有一个文件example.txt,内容如下:

张三 25
李四 30
王五 28

如果我们想把每行的姓名和年龄用逗号分隔,可以使用捕获组:

sed 's/\(张三\|李四\|王五\)\s\([0-9]\+\)/\1,\2/' example.txt

这里,\(\)之间的内容就是一个捕获组。第一个捕获组捕获姓名(张三、李四或王五),第二个捕获组捕获年龄。

反向引用

反向引用是指在正则表达式或替换字符串中引用之前捕获的分组内容。在sed中,反向引用使用\1\2等来表示第一个、第二个捕获的分组,以此类推。

示例

继续使用上面的example.txt文件,如果我们想在每行的姓名后面加上“的年龄是”,可以使用反向引用:

sed 's/\(张三\|李四\|王五\)\s\([0-9]\+\)/\1的年龄是\2/' example.txt

这里,\1引用了第一个捕获组(姓名),\2引用了第二个捕获组(年龄)。替换后的结果如下:

张三的年龄是25
李四的年龄是30
王五的年龄是28

总结

  • 捕获组:使用圆括号()定义,用于捕获正则表达式中匹配的内容。
  • 反向引用:使用\1\2等引用之前捕获的分组内容,在替换字符串中使用。

通过结合捕获组和反向引用,sed可以实现更复杂的文本处理任务。










7️⃣ 参考 7

sed分组替换与反向引用完整教程


一、核心概念与语法
  1. 捕获组

    • 使用( )包裹正则表达式片段,形成捕获组
    • 语法差异
      • 基础正则模式(默认):需转义为\(...\)
      • 扩展正则模式(-r选项):直接使用()
  2. 反向引用

    • 通过\1\2等引用对应捕获组内容,编号由左括号出现顺序决定
    • 特殊符号
      • &表示完整匹配内容
      • \n(n=1-9)表示第n个捕获组
    • 示例:
      echo "abc123" | sed -r 's/([a-z]+)([0-9]+)/数值:\2 字母:\1/'
      # 输出:数值:123 字母:abc
      

二、关键操作场景
场景1:字段顺序重组

任务:将姓名-电话-城市转为城市/电话/姓名

echo "张三-13800138000-北京" | sed -r 's/([^-]*)-([^-]*)-([^-]*)/\3\/\2\/\1/'
# 输出:北京/13800138000/张三

解析

  • ([^-]*)匹配非-字符,形成3个捕获组
  • 替换时通过\3/\2/\1调整顺序,\/转义斜杠
场景2:动态内容提取

任务:从ifconfig输出中提取IPv4地址

ifconfig eth0 | sed -rn 's/.*inet ([0-9.]+) .*/\1/p'
# 输出:192.168.1.10

关键点

  • ([0-9.]+)捕获IP地址段
  • -n抑制默认输出,p标志仅打印匹配行
场景3:多层嵌套处理

任务:处理含括号的文本Error(code:404)[状态码:404]

echo "Error(code:404)" | sed -r 's/([^(]+)\(code: ([0-9]+)\)/[\2]/'
# 输出:[404]

技巧

  • 外层([^(]+)匹配Error但不捕获
  • 内层([0-9]+)捕获状态码,通过\2引用

三、高级技巧
  1. 非捕获组优化
    使用(?:)避免分配组号,减少资源占用:

    echo "ID:ABC-2024" | sed -r 's/(?:[A-Z]{3})-([0-9]{4})/编号:\1/'
    # 输出:ID:编号:2024
    
  2. 逻辑或匹配
    统一处理不同分隔符的日期格式:

    echo "2024/07/25" | sed -r 's#([0-9]{4})([/-])([0-9]{2})\2([0-9]{2})#\1年\3月\4日#'
    # 输出:2024年07月25日
    

    说明

    • [/-]匹配/-分隔符
    • \2确保分隔符一致性
  3. 复杂替换保护原内容
    在电话号码中间添加掩码但保留区号:

    echo "Tel:010-87654321" | sed -r 's/([0-9]{3,4})-([0-9]{4})/\1-****/'
    # 输出:Tel:010-****
    

四、注意事项
  1. 转义优先级

    • 基础正则中必须转义()\(\)
    • 替换内容中的/需转义为\/,或用其他分隔符如#
      sed 's#/path/to#/new/path#g'  # 避免转义/
      
  2. 贪婪匹配特性

    • 默认匹配最长内容,如.*会吞掉整行
    • 非贪婪模式需使用Perl语法(sed -r 's/.*?(pattern)/.../'
  3. 行处理限制

    • sed默认逐行处理,跨行匹配需用N命令(示例):
      sed 'N;s/line1\nline2/merged/'  # 合并两行
      

综合示例

任务:转换Markdown表格标题为HTML格式

echo "| Header1 | Header2 |" | sed -r 's/\|([^|]+)\|([^|]+)\|/<th>\1<\/th><th>\2<\/th>/'
# 输出:<th>Header1</th><th>Header2</th>

解析

  • ([^|]+)匹配非|字符作为列内容
  • 替换为HTML标签并保留原始内容

使用sed进行分组替换的详细指南

分组替换是sed正则表达式的高级应用,通过()定义捕获组,使用\1\2等反向引用实现结构化替换。以下是具体用法和场景示例:


一、基础分组语法

核心格式
sedrs/(1正则)(2正则)/替换格式\1\2/文件sed -r 's/(组1正则)(组2正则)/替换格式\1\2/' 文件
说明:

  • ()定义捕获组,需配合-r选项(扩展正则)避免转义\(\)
  • \1表示第1组内容,\2表示第2组,最多支持9个组
  • 默认仅替换每行第一个匹配项,需全局替换时加g标志

二、典型应用场景示例

1. 日期格式转换
YYYY-MM-DD转为DD/MM/YYYY

echo "2024-07-25" | sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 输出:25/07/2024

解析:

  • ([0-9]{4})捕获年 → \1
  • ([0-9]{2})捕获月 → \2
  • ([0-9]{2})捕获日 → \3
  • 替换时调整顺序并添加斜杠(需转义\/

2. 数据字段重组
重组姓名:年龄:城市城市-姓名(年龄)

echo "Alice:28:Shanghai" | sed -r 's/([^:]+):([0-9]+):([^:]+)/\3-\1(\2)/'
# 输出:Shanghai-Alice(28)

解析:

  • ([^:]+)匹配非冒号字符(姓名、城市)
  • ([0-9]+)匹配年龄数字
  • 通过重组顺序实现格式转换

3. 多字段动态处理
在电话号码中间添加掩码:

echo "Phone:010-87654321" | sed -r 's/([0-9]{3,4})-([0-9]{4})/\1-****/g'
# 输出:Phone:010-****

说明:

  • 保留区号(\1),隐藏后四位为****

三、高级分组技巧

1. 嵌套分组
处理带括号的内容:

echo "Error (code: 404)" | sed -r 's/\(code: ([0-9]+)\)/(状态码:\1)/'
# 输出:Error (状态码:404)

关键点:

  • 使用\(\)匹配原括号
  • 内层([0-9]+)捕获状态码

2. 非捕获分组优化
使用(?:)避免无效捕获(减少\n编号):

echo "ID: A1B-2024" | sed -r 's/(?:[A-Z0-9]{3})-([0-9]{4})/序列号:\1/'
# 输出:ID: 序列号:2024

解析:

  • (?:[A-Z0-9]{3})匹配前缀但不分配组号
  • ([0-9]{4})作为\1使用

3. 逻辑或操作
匹配多种日期格式:

sed -r 's/([0-9]{4})(\/|-)([0-9]{2})\2([0-9]{2})/\1年\3月\4日/g' dates.txt

说明:

  • \/|-匹配分隔符/-
  • \2确保分隔符一致性(如2024/07/252024-07-25

四、注意事项
  1. 转义问题

    • 在基础正则模式(无-r)中需转义括号:\(...\)
    • 替换内容中的&表示完整匹配,需用\&转义
  2. 贪婪匹配

    • 默认匹配最长内容,可用.*?实现非贪婪匹配(需-r支持Perl正则)
  3. 多行处理

    • sed默认逐行处理,跨行匹配需使用N命令(较复杂,建议换用awkPerl

示例代码测试

场景:将Markdown链接转换为HTML格式

echo "[Google](https://google.com)" | sed -r 's/\[([^]]+)\]\(([^)]+)\)/<a href="\2">\1<\/a>/'
# 输出:<a href="https://google.com">Google</a>










8️⃣ 参考 8

sed 的分组替换、捕获组和反向引用是其正则表达式处理能力的核心功能,能够实现复杂的文本重组和动态替换。以下结合具体场景和示例详解这些功能:


一、基础概念与语法

  1. 捕获组(Capture Group) • 定义:通过正则表达式中的圆括号 () 标记子模式,将匹配的文本片段保存到临时缓冲区,供后续引用。

    • 语法:

    \(pattern\)    # 基础正则(需转义括号)
    (pattern)      # 扩展正则(需加 `-r` 或 `-E` 选项)
    

    • 示例:提取日志中的日期和时间

    echo "2023-01-01 12:00:00 [INFO] Log entry" | sed -nE 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}) ([0-9:]+).*/\1 \2/p'
    # 输出:2023-01-01 12:00:00 
    
  2. 反向引用(Back Reference) • 作用:在替换文本中通过 \1\2 等引用捕获组内容(编号从1开始,按左括号顺序)。

    • 典型场景:

    ◦ 字段重排:交换姓名顺序

    ```bash
    echo "John Doe" | sed -r 's/(\w+) (\w+)/\2, \1/'
    # 输出:Doe, John 
    ```
    

    ◦ 动态修饰:为值添加引号

    ```bash
    echo "key=value" | sed -r 's/(key=)(.*)/\1"\2"/'
    # 输出:key="value" 
    ```
    

二、实战应用案例

  1. 复杂格式转换 • 日期格式从 YYYY-MM-DD 转为 DD/MM/YYYY

    echo "2023-01-01" | sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
    # 输出:01/01/2023 
    
  2. 多字段提取与重组 • 日志中提取 IP 和访问次数

    sed -nE 's/.*IP: ([0-9.]+).* Count: ([0-9]+).*/\1 \2/p' access.log
    # 输出:192.168.1.1 1000 
    
  3. 批量修改配置文件 • 调整键值对顺序

    sed -r 's/(max_connections)=([0-9]+)/\2 \1/' mysql.conf
    # 将 "max_connections=100" 转为 "100 max_connections" 
    

三、高级技巧与注意事项

  1. 扩展正则表达式(-r/-E 选项) • 作用:简化语法,避免转义符号。

    sed -r 's/(love)able/\1rs/' file.txt  # loveable → lovers 
    
  2. 处理特殊字符 • 转义需求:若替换内容包含 /,可改用 #| 作为分隔符。

    sed 's#/old/path#/new/path#' file.txt 
    
  3. 多分组协同操作 • 同时操作多个分组:

    echo "apple,banana,cherry" | sed -r 's/(\w+),(\w+),(\w+)/\3:\2:\1/'
    # 输出:cherry:banana:apple 
    

四、常见问题与解决

  1. 分组未生效
    • 检查是否启用扩展正则(-r)或正确转义括号(\(\()。

  2. 反向引用错误
    • 确保分组编号顺序正确,如 \1 对应第一个 ()

  3. 性能优化
    • 避免过度使用贪婪匹配(如 .*),改用非贪婪模式 .*?(需扩展正则支持)。


五、综合示例(多场景整合)

# 任务:将日志中的 "IP:192.168.1.1 Time:2023-01-01" 转换为 "2023-01-01 | 192.168.1.1"
sed -r 's/.*IP:([0-9.]+).*Time:([0-9-]+).*/\2 | \1/' log.txt 

sed 的分组替换是其正则表达式功能的核心应用之一,通过捕获组(Capture Group)和反向引用(Back Reference)实现灵活文本处理。以下是结合实际案例的详细解析:


一、基础语法与原理

  1. 捕获组定义
    在正则表达式中,使用小括号 () 包裹需要捕获的内容,形成分组。例如 \(pattern\)(基础正则)或 (pattern)(扩展正则,需加 -r 参数)。

  2. 反向引用
    在替换时,通过 \1\2 等引用捕获组内容(\1 对应第一个分组,\2 对应第二个,依此类推)。

示例:日期格式转换

# 将 "YYYY-MM-DD" 转为 "DD/MM/YYYY"
echo "2023-01-01" | sed -r 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 输出:01/01/2023

此处: • ([0-9]{4}) 匹配年份 → \1

([0-9]{2}) 匹配月份 → \2

([0-9]{2}) 匹配日期 → \3


二、常见应用场景

  1. 字段重排 对逗号分隔的文本重新排列字段顺序:
# 输入:101,Zhangya,CEO → 输出:CEO,Zhangya,101
sed -r 's/(.*),(.*),(.*)/\3,\2,\1/' person.txt

通过 (.*) 捕获三个字段,并用 \3,\2,\1 调整顺序。

  1. 部分内容保留与替换 替换字符串中的特定部分,同时保留其他内容:
# 将 "PASS_MAX_DAYS 99999" 改为 "PASS_MAX_DAYS 90"
sed -r -i 's/(PASS_MAX_DAYS)\s([0-9]+)/\1 90/' /etc/login.defs

(PASS_MAX_DAYS) 捕获键名 → \1

([0-9]+) 捕获原值 → \2(但替换时丢弃)。

  1. 动态添加符号 在匹配内容周围添加符号(如引号):
# 将 "name=value" 改为 "name="value""
sed -r 's/(name=)(.*)/\1"\2"/' config.txt

(name=) 捕获键名 → \1

(.*) 捕获值 → \2,并在其两侧添加引号。


三、高级技巧与注意事项

  1. 扩展正则表达式(-r 选项) 使用 -r 启用扩展正则,避免转义括号:
# 基础正则需转义:sed 's/\(love\)able/\1rs/'
# 扩展正则更简洁:
sed -r 's/(love)able/\1rs/' file.txt  # loveable → lovers

(来自 )

  1. 特殊字符转义 • 需转义的字符:(){}+?(需用 \(\)\+ 等)。

• 无需转义的字符:[]*(直接使用)。

  1. 多分组替换与复杂结构
# 将第一个逗号替换为 "-",第二个替换为 "_"
echo "101,Zhangya,CEO" | sed -r 's/(.*),(.*),(.*)/\1-\2_\3/'
# 输出:101-Zhangya_CEO

通过多个分组实现复杂替换逻辑。


四、常见问题与解决

  1. 分组不生效
    • 检查是否遗漏 -r 参数(扩展正则)或未转义括号(基础正则)。

    • 确保分组编号正确(从左到右依次为 \1\2)。

  2. 反向引用错误
    • 若替换结果包含 \1 原文字符,可能是正则语法错误或未启用扩展正则。

  3. 性能优化
    • 避免过度使用贪婪匹配(如 .*),改用非贪婪 .*?(需结合 -r\? 转义)。


五、实际案例参考

  1. 日志处理
    提取日志中的 IP 和时间戳:

    sed -nE 's/.*IP: ([0-9.]+) .* Timestamp: ([0-9-]+) .*/\1 \2/p' access.log
    
  2. 配置文件修改
    批量修改键值对:

    sed -r -i 's/(max_connections)=[0-9]+/\1=1000/' mysql.conf
    

通过灵活运用分组替换,sed 可高效处理复杂文本场景。建议先通过 -n p 测试命令(如 sed -n 's/pattern/replacement/p' file),确认无误后再使用 -i 直接修改文件。










9️⃣ 参考 9

sed 中,分组替换捕获组反向引用 是非常强大的功能,用于复杂的文本替换和重组操作。以下是这些概念的详细解释及使用示例:

1. 捕获组(Capture Groups)

捕获组用于匹配并记住特定模式的子串,以便在替换部分引用。捕获组通过圆括号 () 定义。

基本语法
\(模式\)

sed 中,捕获组通常使用反斜杠 \ 进行转义。

2. 反向引用(Backreferences)

反向引用用于在替换字符串中引用之前捕获的组。反向引用使用 \1, \2, \3 等表示,其中 \1 引用第一个捕获组,\2 引用第二个,依此类推。

基本语法
sed 's/模式/\1/' 文件

3. 分组替换(Grouped Replacement)

分组替换结合了捕获组和反向引用,用于在替换过程中重组文本。

示例

以下是一些常见的 sed 分组替换、捕获组和反向引用的示例:

示例 1:交换两个单词的位置

目标:将每行的两个单词交换顺序。

输入文件 example.txt

apple banana
cat dog

命令

sed 's/\(.*\) \(.*\)/\2 \1/' example.txt

解释

  • \(.*\):第一个捕获组,匹配任意字符(非贪婪)。
  • :一个空格。
  • \(.*\):第二个捕获组,匹配任意字符(非贪婪)。
  • 替换为 \2 \1:第二个捕获组的内容在前,第一个在后,用空格分隔。

输出

banana apple
dog cat
示例 2:在数字前添加货币符号

目标:在每个数字前添加美元符号 $

输入文件 prices.txt

The price is 100 dollars.
The price is 200 dollars.

命令

sed 's/\([0-9]\+\)/$\1/' prices.txt

解释

  • \([0-9]\+\):第一个捕获组,匹配一个或多个数字。
  • 替换为 $\1:在捕获的数字前添加 $

输出

The price is $100 dollars.
The price is $200 dollars.
示例 3:提取特定部分并重组文本

目标:将 "name: John Doe" 转换为 "Doe, John"。

输入文件 names.txt

name: John Doe
name: Jane Smith

命令

sed 's/name: \(.*\) \(.*\)/\2, \1/' names.txt

解释

  • name: :匹配固定字符串。
  • \(.*\):第一个捕获组,匹配名字。
  • :一个空格。
  • \(.*\):第二个捕获组,匹配姓氏。
  • 替换为 \2, \1:姓氏在前,名字在后,中间加逗号和空格。

输出

Doe, John
Smith, Jane
示例 4:使用多个捕获组

目标:将 "firstname lastname" 转换为 "lastname (firstname)"。

输入文件 names.txt

John Doe
Jane Smith

命令

sed 's/\(.*\) \(.*\)/\2 (\1)/' names.txt

解释

  • \(.*\):第一个捕获组,匹配名字。
  • :一个空格。
  • \(.*\):第二个捕获组,匹配姓氏。
  • 替换为 \2 (\1):姓氏在前,名字在后,用括号括起来。

输出

Doe (John)
Smith (Jane)

注意事项

  1. 转义字符:在 sed 中,圆括号 () 和其他特殊字符需要进行转义,使用反斜杠 \
  2. 捕获组编号:捕获组从左到右编号,从 \1 开始。
  3. 非捕获组:如果不需要捕获某个组,可以使用 (?:模式) 的形式。例如,(?:foo) 不会捕获 "foo"。
  4. 全局替换:使用 g 标志可以在每行中进行全局替换,而不仅仅是第一个匹配项。

总结

sed 的分组替换、捕获组和反向引用功能使得复杂的文本处理任务成为可能。通过合理使用这些功能,可以实现各种复杂的文本重组和替换操作。建议在实际使用中结合具体需求进行练习,以更好地掌握这些高级功能。

sed 中,分组替换(也称为捕获组子表达式替换)是通过使用圆括号 () 来捕获匹配的模式,并在替换部分引用这些捕获的内容。分组替换在需要对匹配到的部分进行重组或引用时非常有用。

基本语法

sed 's/模式/替换内容/' 文件

在模式部分使用圆括号 () 来定义捕获组,在替换内容中使用 \1, \2, ... 来引用这些组。

引用捕获组

  • \1:引用第一个捕获组
  • \2:引用第二个捕获组
  • 以此类推...

示例

  1. 交换两个单词的位置

    假设有一个文件 example.txt,内容如下:

    apple banana
    cat dog
    

    目标:将每行的两个单词交换位置。

    sed 's/\(.*\) \(.*\)/\2 \1/' example.txt
    

    解释

    • \(.*\):第一个捕获组,匹配任意字符(非贪婪)。
    • :一个空格。
    • \(.*\):第二个捕获组,匹配任意字符(非贪婪)。
    • 替换为 \2 \1:第二个捕获组的内容在前,第一个在后,用空格分隔。

    结果

    banana apple
    dog cat
    
  2. 在特定模式周围添加括号

    假设有一个文件 example.txt,内容如下:

    The quick brown fox jumps over the lazy dog.
    

    目标:将 "quick brown" 用括号括起来。

    sed 's/\(quick brown\)/\(\1\)/' example.txt
    

    解释

    • \(quick brown\):捕获 "quick brown"。
    • 替换为 (\1):将捕获的内容用括号括起来。

    结果

    The (quick brown) fox jumps over the lazy dog.
    
  3. 在数字前添加货币符号

    假设有一个文件 prices.txt,内容如下:

    The price is 100 dollars.
    The price is 200 dollars.
    

    目标:在每个数字前添加美元符号 $

    sed 's/\([0-9]\+\)/$\1/' prices.txt
    

    解释

    • \([0-9]\+\):捕获一个或多个数字。
    • 替换为 $\1:在捕获的数字前添加 $

    结果

    The price is $100 dollars.
    The price is $200 dollars.
    
  4. 交换单词顺序并添加逗号

    假设有一个文件 names.txt,内容如下:

    John Doe
    Jane Smith
    

    目标:将姓名顺序改为 "Doe, John" 和 "Smith, Jane"。

    sed 's/\([^ ]*\) \(.*\)/\2, \1/' names.txt
    

    解释

    • \([^ ]*\):第一个捕获组,匹配除空格外的任意字符(即名字)。
    • :一个空格。
    • \(.*\):第二个捕获组,匹配剩余的任意字符(即姓氏)。
    • 替换为 \2, \1:姓氏在前,名字在后,中间加逗号和空格。

    结果

    Doe, John
    Smith, Jane
    

注意事项

  • 转义字符:在使用圆括号 () 和其他特殊字符时,需要使用反斜杠 \ 进行转义。
  • 捕获组编号:从左到右,第一个捕获组为 \1,第二个为 \2,以此类推。
  • 非捕获组:如果不需要捕获某个组,可以使用 (?:模式) 的形式。例如,(?:foo) 不会捕获 "foo"。

总结

sed 的分组替换功能通过捕获和引用匹配的模式,使得复杂的文本替换操作成为可能。通过合理使用捕获组和替换引用,可以实现各种复杂的文本处理需求。建议在实际使用中结合具体需求进行练习,以加深理解。










🔟 参考 10

sed 的分组替换(Capture Groups)

sed 支持通过正则表达式的 分组捕获 实现灵活的替换操作。以下是核心用法和示例:


一、分组语法
  1. 定义分组
    使用括号 () 将正则表达式的一部分定义为分组,例如 (pattern)
  2. 引用分组
    在替换内容中通过 \1, \2, \3 等引用第1、2、3个分组的内容。

二、基础示例

场景:将 a_a 替换为 A_A(保留下划线后的字母并大写)

sed 's/\([a-z]\)_\1/\U\1_\1/g' file.txt
  • ([a-z]):匹配一个小写字母并捕获为第1组。
  • \1:引用第1组的内容(小写字母)。
  • \U\1:将第1组的内容转换为大写。

三、复杂场景与技巧
  1. 交换位置
    场景:将 foo-bar 替换为 bar-foo

    sed 's/\(foo\)-\(bar\)/\2-\1/g' file.txt
    
    • (foo)(bar) 分别捕获为第1、2组。
    • \2-\1:交换两组的顺序。
  2. 保留部分内容并修改
    场景:将 Chapter 1 替换为 Section 1

    sed 's/\(Chapter \)[0-9]+\1/Section \1/' file.txt
    
    • (Chapter ):捕获前缀。
    • [0-9]+:匹配数字但不需要捕获。
    • \1:保留前缀并替换为 Section
  3. 处理多分组
    场景:将 2023-04-19 转换为 19/04/2023(日期格式转换)

    sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3/\2/\1/g' file.txt
    
    • ( [0-9]{4} ):年 → 第1组。
    • ( [0-9]{2} ):月 → 第2组。
    • ( [0-9]{2} ):日 → 第3组。
    • \3/\2/\1:按日/月/年格式重组。

四、高级技巧
  1. 忽略大小写替换
    场景:将 Hello 替换为 hi,不区分大小写

    sed -E 's/(hello)/hi/\1/gI' file.txt
    
    • I:忽略大小写匹配。
    • \1:保留原分组内容的小写形式。
  2. 使用替代符避免冲突
    如果替换内容包含 /,可更换定界符(如 #):

    sed 's#/old/path/#/new/path/#g' file.txt
    
  3. 直接修改文件
    使用 -i 选项直接修改文件:

    sed -i 's/\([A-Z]\)/\L\1/g' file.txt  # 将大写字母转为小写
    

五、注意事项
  1. 分组数量
    最多支持 \9(9个分组),更高版本可能支持更多。
  2. 转义字符
    如果分组内容包含 \,需额外转义(如 \\1)。
  3. 兼容性
    • GNU sed 默认支持扩展正则表达式(如 + 量词)。
    • BSD sed 需添加 -E 选项启用扩展语法。

六、实际应用
  1. 批量修改文件名
    file_v1.txt 改为 file_v2.txt

    sed -i 's/\(file_v\)[0-9]/\12/' file_v1.txt
    
  2. 日志处理
    ERROR: code=404 替换为 WARNING: code=404

    sed 's/\(ERROR:\) code=\([0-9]+\)/\1\2/g' log.txt
    

以下是关于 sed 分组替换、捕获组与反向引用的详细解析,结合语法、实战案例及注意事项整理而成:


一、核心概念

1. 捕获组(Capture Group)
  • 定义:通过正则表达式的 圆括号 () 标记文本片段,匹配的内容会被临时存储,供后续引用。
  • 语法
    • 基本正则:需转义括号 \(pattern\)
    • 扩展正则(加 -r-E 选项):直接使用 (...)
  • 示例:提取 IP 地址:
    sed -nE 's/.*(192\.168\.[0-9]+\.[0-9]+).*/\1/p' log.txt  # 基本正则需转义 `.`
    sed -n 's/.*(192\.168\.[0-9]+\.[0-9]+).*/\1/p' log.txt  # 扩展正则无需转义
    
2. 反向引用(Backreference)
  • 定义:在替换部分通过 \数字 引用捕获组内容(如 \1 引用第一个组)。
  • 作用:实现动态内容复用,如字段调换、重复替换等。

二、分组替换语法

1. 基本格式
sed 's/\(group1\)\(group2\)/\2\1/g' file.txt
  • 说明
    • \(group1\)\(group2\) 为两个捕获组。
    • 替换时用 \2\1 交换顺序。
2. 扩展正则简化写法
sed -E 's/(group1)(group2)/\2\1/g' file.txt  # 无需转义括号

三、实战应用场景

1. 字段位置调换

需求:将 John Doe 转为 Doe, John

sed -E 's/(John) (Doe)/\2, \1/g' names.txt
  • 输出Doe, John
2. 日志时间戳格式转换

原始日志2023-10-05 14:30:00 目标格式05/Oct/2023 14:30:00

sed 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/Oct\/\1/g' log.txt
  • 解析
    • ([0-9]{4}) 捕获年份,([0-9]{2}) 捕获月、日。
    • \3 引用日,\2 引用月,\1 引用年。
3. 多条件分组替换

需求:将 applebanana 替换为 fruit

sed -E 's/(apple|banana)/fruit/g' fruits.txt
  • 输出:所有 applebanana 变为 fruit
4. 复杂字段重组

需求:将 101,Zhangya,CEO 转为 CEO,Zhangya,101

sed -E 's/(.*),(.*)\,(.*)/\3,\2,\1/g' person.txt
  • 解析
    • 三个捕获组分别匹配 ID、姓名、职位。
    • 替换时按 职位,姓名,ID 顺序重组。

四、高级技巧

1. 保留部分内容并插入新文本

需求:将 PASS_MAX_DAYS 99999 改为 PASS_MAX_DAYS 90

sed -r 's/(PASS_MAX_DAYS)\s([0-9]+)/\1 90/g' /etc/login.defs
  • 解析
    • (PASS_MAX_DAYS) 保留原词,([0-9]+) 捕获数字并替换为 90
2. 匿名化日志中的 IP
sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/XXX.XXX.XXX.XXX/g' access.log
  • 场景:隐藏真实 IP,保留日志结构。
3. 处理带转义字符的文本

需求:替换 path=/usr/local/binpath=/opt/bin

sed 's#/usr/local/bin#/opt/bin#g' config.conf
  • 说明:用 # 替代 / 作为分隔符,避免转义。

五、注意事项

  1. 正则表达式引擎
    • 基本正则:分组需转义为 \(...\)
    • 扩展正则:使用 -r-E 选项,分组无需转义。
  2. 特殊字符转义
    • / 需转义为 \/. 需转义为 \.
  3. 性能优化
    • 处理大文件时,使用 --unbuffered-u)减少内存占用。
  4. 备份原文件
    • 直接修改文件时,建议备份:sed -i.bak 's/.../.../g' file

六、综合案例

案例:日志清洗与重构

原始日志

2023-10-05 14:30:00 [ERROR] User login failed for 192.168.1.1

目标格式

[14:30:00] ERROR: User login failed for IP 192.168.1.1

命令

sed -E 's/([0-9]{4}-[0-9]{2}-[0-9]{2}) ([0-9]{2}:[0-9]{2}:[0-9]{2}) \[([A-Z]+)\] (.*) for ([0-9.]+)/[\2] \3: \4 for IP \5/g' log.txt
  • 解析
    • 捕获组:日期、时间、错误级别、消息、IP。
    • 反向引用:\2(时间)、\3(错误级别)、\5(IP)。

七、总结

sed 的分组替换通过 捕获组反向引用 实现灵活的文本操作,适用于字段调换、日志处理、数据清洗等场景。掌握正则表达式的分组规则和反向引用语法,可显著提升文本处理效率。对于复杂需求,建议结合 grepawk 等工具进行流水线处理。

以下是关于 sed 分组替换的详细解析,涵盖语法、实战案例及注意事项:


一、分组替换基础语法

sed 中,分组通过 正则表达式括号 () 实现,用于捕获匹配的文本片段,并在替换时通过 反向引用 \1\2 等复用。
语法示例

sed 's/\(group1\)\(group2\)/\2\1/g' file.txt
  • 说明
    • \(group1\)\(group2\) 为两个捕获组。
    • 替换时,\2\1 分别引用第二个和第一个捕获的内容,实现顺序调换。

二、核心应用场景

1. 字符串顺序调换

需求:将 year-month-day 格式日期改为 day/month/year
命令

sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/g' dates.txt
  • 解析
    • \([0-9]\{4\}\) 捕获年份(4位数字)。
    • \3\/\2\/\1 将捕获的日、月、年按新顺序组合,/ 需转义为 \/
  • 输出示例
    2023-01-01 → 01/01/2023
    
2. 字段位置反转

需求:将 John Doe 转为 Doe, John
命令

sed 's/\(John\) \(Doe\)/\2, \1/g' names.txt
  • 解析
    • 两个捕获组分别匹配名字和姓氏。
    • 替换时交换顺序并添加逗号。
3. 结构化文本处理

需求:处理日志中的 IP 和访问次数,提取后重新排列。
示例日志

192.168.1.1 - - [10/Oct/2023] "GET /index.html" 200 1024

命令

sed -nE 's/.* ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*/\1/p' access.log
  • 解析
    • ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) 捕获 IP 地址。
    • 替换时直接输出 \1(即 IP)。

三、高级技巧

1. 多条件分组替换

需求:同时替换多个模式(如 applebananafruit)。
命令

sed -E 's/(apple|banana)/fruit/g' fruits.txt
  • 解析
    • (apple|banana) 为分组,匹配任一模式并替换为 fruit
2. 保留部分内容并插入新文本

需求:将 PASS_MAX_DAYS 99999 改为 PASS_MAX_DAYS 90
命令

sed -r 's/(PASS_MAX_DAYS)\s([0-9]+)/\1 90/g' /etc/login.defs
  • 解析
    • (PASS_MAX_DAYS) 保留原词,([0-9]+) 捕获数字并替换为 90
3. 复杂字段替换

需求:将 101,Zhangya,CEO 转为 CEO,Zhangya,101
命令

sed -r 's/(.*),(.*)\,(.*)/\3,\2,\1/g' person.txt
  • 解析
    • 三个捕获组分别匹配 ID、姓名、职位。
    • 替换时按 职位,姓名,ID 顺序重组。

四、注意事项

  1. 正则表达式引擎
    • 基本正则:分组需转义为 \(...\)
    • 扩展正则:使用 -r 选项,分组无需转义(如 (...))。
  2. 特殊字符转义
    • / 需转义为 \/. 需转义为 \.
  3. 性能优化
    • 处理大文件时,使用 --unbuffered-u)减少内存占用。
  4. 备份原文件
    • 直接修改文件时,建议备份:sed -i.bak 's/.../.../g' file

五、实战案例

案例1:日志时间戳格式转换

原始日志

2023-10-05 14:30:00

目标格式05/Oct/2023 14:30:00
命令

sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/Oct\/\1/g' log.txt
案例2:批量修改配置文件

需求:将 timeout=30 改为 timeout=60,但仅修改 httpd 配置。
命令

sed -i '/httpd/s/timeout=[0-9]\+/timeout=60/g' /etc/httpd.conf

六、总结

sed 的分组替换功能通过捕获组和反向引用实现,适用于字段调换、格式转换、日志处理等场景。掌握正则表达式的分组规则和反向引用语法,可高效完成复杂文本操作。