![Sed 中的重复捕获组](https://linux22.com/image/214783/Sed%20%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E6%8D%95%E8%8E%B7%E7%BB%84.png)
我有这样的模式:
#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。然后我们可以使用csvformat
fromcsvkit将默认字段分隔符更改为空格:
$ sed -e 's/[^(]*(/(/' -e 'y/()/""/' file | csvformat -D ' '
0 0-11,22 A7E2BB0F38DF 42 1A0290800D7 7042 81A0290800D7 7442