当从另一个命令管道时,将原始模式空间限制为匹配的字符串

a_g*_*est 3 linux sed string

我有一个文本文件,我想更换内部的所有空间[[,并]]用连字符(括号中从来没有嵌套,总是匹配)。下面是一个例子:

$ cat test.txt 
abc [[foo]] xyz
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz [[something else]]
Run Code Online (Sandbox Code Playgroud)

所以所需的输出是:

abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
Run Code Online (Sandbox Code Playgroud)

我想我可以sed用来匹配括号内的字符串,然后使用该e标志sed再次运行结果以进行替换。然而,问题是不仅匹配的字符串被作为命令执行,而且整个模式空间(似乎是整行):

$ sed -E 's@(\[\[)(.+)(\]\])@sed -e "s/ /-/g" <<< "\1\2\3"@gpe' test.txt 
abc sed -e "s/ /-/g" <<< "[[foo]]" xyz
sh: 1: Syntax error: redirection unexpected

abc sed -e "s/ /-/g" <<< "[[foo bar]]" xyz
sh: 1: Syntax error: redirection unexpected

abc sed -e "s/ /-/g" <<< "[[foo bar baz]]" xyz
sh: 1: Syntax error: redirection unexpected
Run Code Online (Sandbox Code Playgroud)

有没有办法将通过e标志执行的内容限制为匹配的字符串?如果没有,我将如何解决这个问题sed

ste*_*ver 7

我认为没有办法限制e修饰符传递给 shell 的内容;但是你可以做这样的事情:

$ sed -E ':a;s@(.*\[\[)([^][]* [^][]*)(\]\].*)@printf "%s%s%s" "\1" "$(printf "\2" | sed "s/ /-/g")" "\3"@e;ta' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
Run Code Online (Sandbox Code Playgroud)

请注意,多个替换的处理是通过循环完成的 - 由于匹配的贪婪,它实际上以相反的顺序进行替换。

还要注意e的用途/bin/sh,这将有可能不支持<<<输入重定向(因此使用的管道当量printf "\2" | sed "s/ /-/g")。


如果 perl 是一个选项,您可以做一些更接近您的原始意图的事情,例如:

$ perl -pe 's/(?<=\[\[)(.*?)(?=\]\])/$1 =~ s: :-:rg/ge' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
Run Code Online (Sandbox Code Playgroud)

由于 perl 提供了一个非贪婪的修饰符?,这可以更传统地使用g外部替换上的标志来处理每行的多个替换。

  • @a_guest `=~` 是匹配运算符。这里,`$1` 是正则表达式捕获的内容(相当于 `sed` 中的 `\1`)。然后将存储在 `$1` 中的值与替换运算符进行匹配(`=~`)(`s:old:new:`,这在 `sed` 中是相同的,它只是使用 `:` 而不是 `/` ) 用 `-` 替换所有空格。把整个事情想象成“找到双括号内的字符串,并在将匹配的字符串通过将替换空格的替换运算符传递后用自己替换它们”。 (2认同)