Sed 中的重复捕获组

Sed 中的重复捕获组

我有这样的模式:

#ABC: (0),(0-11,22),(A7E2BB0F38DF),(42),(1A0290800D7),(7042),(81A0290800D7),(7442)

我想使用 sed 得到以下结果:

0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442

bash 中的以下工作:

result="#ABC: (0),(0-11,22),(A7E2BB0F38DF),(42),(1A0290800D7),(7042),(81A0290800D7),(7442)"
sed -n 's/.*(\(.*\)),(\(.*\)),(\(.*\)),(\(.*\)),(\(.*\)),(\(.*\)),(\(.*\)),(\(.*\))/\1 \2 \3 \4 \5 \6 \7 \8/p' <<< "$result"
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442

现在,我如何优化这个 sed 表达式?如何删除多余的捕获组?

答案1

这样可以吗?

% sed -Ee 's/[^(]*\(([^)]*)\)/\1 /g' < input.txt
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442 

也就是说,匹配第一个左括号 ( [^(]*\() 之前的任何内容,然后捕获不是右括号 ( ([^)]*)) 的任何内容,然后匹配右括号,用捕获的部分 ( ) 替换批次,\1并为字符串的其余部分 ( /g)。

或者在 Perl 中:

% perl -ne 'print "$1 " while(/\((.*?)\)/g); print "\n"' < input.txt
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442 

答案2

我假设问题中显示的原始输入不是某种结构化文档格式(如 YAML 或 JSON)的较大文档的片段。如果是的话,可能有比此处或其他答案中介绍的更好的方法来实现这一目标。


这会以与您指定的相反的方式实现您想要执行的操作。这不是尝试匹配并保留括号内的内容,而是删除第一个(及其之前的所有内容、最后一个)及其之后的所有内容,然后),(用空格替换子字符串的每个实例:

$ sed -e 's/[^(]*(//' -e 's/)[^)]*$//' -e 's/),(/ /g' file
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442

或者,首先用空格替换),(,这使我们能够重写其他替换,而不需要太多的方括号和括号(假设输入中的外括号都不包含带括号的子字符串):

$ sed -e 's/),(/ /g' -e 's/.*(//' -e 's/).*//' file
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442

剩下的是不在括号之外的子字符串(以空格分隔)。


一种完全不同的方法是通过剥离第一个 前面的文本(,然后将每个(和翻译)为双引号,将输入转换为正确引用的 CSV。然后我们可以使用csvformatfromcsvkit将默认字段分隔符更改为空格:

$ sed -e 's/[^(]*(/(/' -e 'y/()/""/' file | csvformat -D ' '
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442

相关内容