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

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

我有一个文本文件,我想用连字符替换内部的所有空格[[]]括号从不嵌套并且始终匹配)。下面是一个例子:

$ cat test.txt 
abc [[foo]] xyz
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz [[something else]]

所以期望的输出是:

abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

我想我可以用来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

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

答案1

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

$ 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]]

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

另请注意,e使用/bin/shwhich 可能不支持<<<输入重定向(因此使用管道等效项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]]

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

答案2

使用标准sed

$ sed -e ':again' -e 's/\(\[\[[^]]*\) \([^]]*\]\]\)/\1-\2/g' -e 't again' file
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

[[这会将在和之间找到的每个空格字符替换]]为破折号。这是通过匹配[[和 来完成的]],并且它们之间匹配一个空格字符(可选地两侧有其他字符串)。匹配的子字符串将按原样替换,其中空格替换为破折号。

如果替换完成,该t命令会使脚本分支回标签again以进行另一次替换。这可以解决由于重叠比赛而第一次错过的空间。

因为据说每个[[总是与]](大概在同一条线上),我们可以稍微缩短命令:

sed -e ':again' -e 's/\(\[\[[^]]*\) /\1-/g' -e 't again' file

这不寻找结束语]]

答案3

cat - <<\! > file
Abc [[ \ ]] def and a cup of 
Ghi [[]] jkl 
Mno [[ ]] pqr 
abc [[" \' \\\"]] xyz
abc [[foo$$]] xyz [[a b c]] deal
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz
abc [[foo $bar baz]] xyz [[FOO BAR VAZ]] $#
!
  1. GNU sed无需求助于/e修饰符
sed -Ee '
  :loop
    s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
  t loop
' file

虽然以 Posixly 方式编写它很简单,但为了最大限度地减少我们在启用扩展正则表达式模式的情况下使用的反斜杠,-E.该循环逐渐转换在“[[...]]”对之间找到的空格字符/迭代。当循环在任何对中找不到任何此类空格时,循环就会停止。然后打印模式空间并将下一行读入模式空间...冲洗...重复直到我们看到 eof。


  1. 使用awk实用程序分割字符串上的每一行[[| ]]。我们可以这样做,因为对称性 ([[ n ]]) 是成对出现的并且按这个顺序。没有悬空的 [[ 或 ]]。那么每个偶数字段都将在 [[ n ]] 内并且需要被处理。
awk -F '[[]{2}|]]' '
  {
    for (i=2; i<=NF; i+=2) {
      gsub(/ /, "-", $i)
      $i = "[[" $i "]]"
    }
  }1
' OFS= file

3.GNU sed使用/e修饰符。

sed -Ee "
  s/'/&\"&\"&/g;tloop
  :loop
    s|(.*[[]{2})([^][]* [^]]*)(]].*)|v='\2';v=\${v// /-};printf '%s' '\1' \"\$v\" '\3'|e
  t loop
" file

  1. 使用 perl 的方式与上面的 awk 相同。
perl -F'(\[\[|]])' -lane 'my $i;
  print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file

  1. GNU sed 通过切割 [[...]] 对,对孤立的对进行转换,然后将其合并回来。继续这样做,直到每对都被检查过。
m='[^\n]'
sed -Ee "
  s/[[]{2}|]]/&\n/g;T;h
  :loop
    s/^$m*\n($m*)\n.*/\1/
    y/ /-/;G
    s/^($m*)\n($m*)\n$m*\n/\2\1/
    h
  /\n/b loop
" file

输出:

Abc [[-\--]] def 
Ghi [[]] jkl 
Mno [[-]] pqr 
abc [["-\'-\\\"]] xyz
abc [[foo$$]] xyz [[a-b-c]] deal
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz
abc [[foo-$bar-baz]] xyz [[FOO-BAR-VAZ]] $#

相关内容