我有一个文本文件,我想用连字符替换内部的所有空格[[
(]]
括号从不嵌套并且始终匹配)。下面是一个例子:
$ 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/sh
which 可能不支持<<<
输入重定向(因此使用管道等效项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]] $#
!
GNU sed
无需求助于/e
修饰符
sed -Ee '
:loop
s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
t loop
' file
虽然以 Posixly 方式编写它很简单,但为了最大限度地减少我们在启用扩展正则表达式模式的情况下使用的反斜杠,-E
.该循环逐渐转换在“[[...]]”对之间找到的空格字符/迭代。当循环在任何对中找不到任何此类空格时,循环就会停止。然后打印模式空间并将下一行读入模式空间...冲洗...重复直到我们看到 eof。
- 使用
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
- 使用 perl 的方式与上面的 awk 相同。
perl -F'(\[\[|]])' -lane 'my $i;
print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file
- 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]] $#