分组和反向引用是正则表达式中的基本概念,sed为两者提供支持。
分组允许您将多个字符视为一个单元,而反向引用允许您引用先前匹配的组。
在本教程中,我们将介绍与分组和反向引用相关的各种主题sed
,例如捕获组、组内交替等等。
捕获组和反向引用
假设你有一个名为names.txt
包含以下内容:
John Doe
Jane Doe
并且您想将姓名顺序更改为姓氏、名字。您可以使用sed
命令与捕获组和反向引用来完成此操作。
Command:
sed 's/\(.*\) \(.*\)/\2, \1/' names.txt
Output:
Doe, John
Doe, Jane
在此命令中,搜索模式\(.*\) \(.*\)
用于匹配整行并捕获名字和姓氏。首先\(.*\)
捕获第一个名称和第二个名称\(.*\)
捕获姓氏。
替换模式\2, \1
用于替换匹配的文本。
The \1
是对第一个捕获组的反向引用,并且\2
是对第二个捕获组的反向引用。
因此,替换模式会交换名字和姓氏,并在中间添加逗号。
在 sed 组中使用大括号 {} 进行重复
大括号{}
in sed
用于指定一个字符或一组字符的重复。
您可以使用它们来匹配某个字符或一组的特定重复次数。
例如,假设您有一个名为numbers.txt
包含以下内容:
111
222
333
并且你想改变111
to 1
, 222
to 2
, and 333
to 3
。您可以使用sed
带大括号的命令{}
来指定重复。
Command:
sed 's/\(1\)\1\{2\}/\1/' numbers.txt
Output:
1
222
333
在此命令中,搜索模式\(1\)\1\{2\}
用于匹配111
在输入中。这\(1\)
捕获第一个1
,以及\1\{2\}
匹配接下来的两个1
s.
替换模式\1
用于将匹配的文本替换为第一个捕获的组,即1
.
因此,111
被替换为1
在输出中。
您可以再次运行该命令2
and 3
改变222
to 2
and 333
to 3
.
在组内使用交替 (|)
交替进行sed
用于匹配 this OR that。您可以使用管道符号|
团体内部()
来指定交替。
请注意,您需要使用-E
启用扩展正则表达式的选项sed
为了这个工作。
例如,假设您有一个名为days.txt
包含以下内容:
Monday
Tuesday
Wednesday
而你想更换Monday
or Wednesday
with Holiday
。您可以使用sed
通过在小组内轮流指挥来实现这一目标。
Command:
sed -E 's/(Monday|Wednesday)/Holiday/' days.txt
Output:
Holiday
Tuesday
Holiday
搜索模式(Monday|Wednesday)
用于匹配Monday
or Wednesday
在输入中。
替换模式Holiday
用于替换匹配的文本。
成组转义括号和元字符
In sed
, 括号()
用于分组,但如果您想匹配输入中的文字括号或其他元字符,则需要使用反斜杠对其进行转义\
.
例如,假设您有一个名为data.txt
包含以下内容:
(abc) def (ghi)
并且您想删除括号。您可以使用sed
带有转义括号的命令来完成此操作。
Command:
sed 's/(/[/g; s/)/]/g' data.txt
Output:
[abc] def [ghi]
在此示例中,sed
命令替换所有出现的(
with [
以及所有出现的)
with ]
.
但是,如果您正在使用sed
与-E
选项(启用扩展正则表达式),括号(
and )
默认情况下被视为特殊字符,您必须对它们进行转义才能将它们视为文字字符:
sed -E 's/\(/[/g; s/\)/]/g' data.txt
该命令将产生与前面的示例相同的输出。
反向引用的限制和边缘情况
反向引用位于sed
功能强大,但您应该注意一些限制和边缘情况:
-
反向引用数量有限: In
sed
,您最多只能使用 9 个反向引用(\1
to \9
)在替换模式中。如果搜索模式中的捕获组超过 9 个,则只能在替换模式中引用前 9 个。
-
不支持 Lookahead 和 Lookbehind 断言: 如前面提到的,
sed
不支持lookahead和lookbehind断言,这在某些情况下会限制反向引用的使用。
-
不支持嵌套捕获组:
sed
不支持嵌套捕获组,这可能会导致在某些情况下难以使用反向引用。
使用组和反向引用时的常见错误
在中使用组和反向引用sed
可能很棘手,人们经常犯一些常见的错误:
-
不转义特殊字符:在基本正则表达式(BRE)中,这是默认模式
sed
, 特殊字符如(
, )
, {
, and }
必须用反斜杠转义\
被视为组分隔符或重复运算符。例如,\(
and \)
用于分组,并且\{m,n\}
用于重复。
-
使用错误的反向引用:反向引用
\1
to \9
按照捕获的组在搜索模式中出现的顺序引用它们。在替换模式中使用错误的反向引用是一个常见的错误。
-
混淆分组和捕获:所有捕获组
( ... )
也是分组构造,但并非所有分组构造都是捕获组。例如,(?: ... )
是一个非捕获组,它将所包含的标记进行分组,但不捕获匹配的文本。
-
不考虑换行: 默认情况下,
sed
逐行处理输入,因此^
and $
锚点匹配行的开头和结尾,而不是整个输入的开头和结尾。如果您需要处理多行,则需要使用N
命令将下一行输入附加到模式空间。
-
Using
*
代替.*
: *
匹配零个或多个前面的标记,而.*
匹配零个或多个任何字符(换行符除外)。这是一个常见的错误使用*
代替.*
在搜索模式中。
使用 sed 进行高效日志处理
我从事的一项特别的自由职业是为一位美国客户提供服务,该客户有大量日志文件需要处理和重新格式化。
该日志文件包含大约 1000 万行文本,每行都有一组由分隔符分隔的值。
客户端需要重新排列文本的某些部分、交换一些值并删除冗余条目。
我尝试使用Python来处理该文件,但由于文件太大,处理时间太长。
然后我意识到 Linux 中的 sed 命令非常适合这项任务,因为它是为文本处理而设计的,可以直接处理文件,而无需将整个文件加载到内存中。
分组允许您将正则表达式的一部分分组在一起,而反向引用允许您重新引用分组的文本。
这些行是这样的:
timestamp|IP|URL|status
客户希望将其重新安排为:
IP|timestamp|status|URL
我可以使用带有分组和反向引用的 sed 命令来轻松地重新排列文本:
sed 's/\(.*\)|\(.*\)|\(.*\)|\(.*\)/\2|\1|\4|\3/' input.log > output.log
在此命令中, s 表示sed
执行替换,() 用于对文本的各个部分进行分组。 ‘\1’、‘\2’等是反向引用,指的是文本的分组部分。
Using sed
通过分组和反向引用,我能够在 30 分钟内处理 1000 万行文件,而我最初编写的 Python 脚本只能在相同的时间内处理文件的 10% 左右。