在GNU sed
中将是这样
's/foo/bar/3g' <<< "foofoofoofoofoo"
Output: "foofoobarbarbar"
BSD sed
中的同一命令给我以下错误
sed: 1: "s/foo/bar/3g": more than one number or 'g' in substitute flags
如何在BSD sed
上实现它?
我搜索了SO,发现this,但所有答案都是针对GNU的。 我读过这个人,但是很难弄清楚。
答案 0 :(得分:3)
一个选项是使用标签和t
命令实现循环:
$ sed -e ':l' -e 's/foo/bar/3' -e 'tl' <<< 'foofoofoofoofoo'
foofoobarbarbar
请小心,因为如果替换文本与原始RE(例如s/f.x/fox/
)匹配,那么您将陷入无限循环,并且如果替换后生成原始文本,则会出乎意料结果,例如:
$ sed 's/foo/oo/3g' <<< 'foofoofffoo'
foofooffoo
$ sed -e ':l' -e 's/foo/oo/3' -e 'tl' <<< 'foofoofffoo'
foofoooo
请注意,第一个版本之所以有效,是因为它在一次文本传递中进行了所有替换,因此,先前的替换不被视为当前替换过程的字符串的一部分。
答案 1 :(得分:3)
如果它不是简单的s / old / new,则只需使用awk而不是sed。在任何UNIX盒子上的任何外壳中都有任何awk:
$ cat tst.awk
{
head = ""
tail = $0
cnt = 0
while ( match(tail,old) ) {
tgt = substr(tail,RSTART,RLENGTH)
if ( ++cnt >= beg ) {
tgt = new
}
head = head tgt
tail = substr(tail,RSTART+RLENGTH)
}
print head tail
}
$ awk -v old='foo' -v new='bar' -v beg=3 -f tst.awk <<< "foofoofoofoofoo"
foofoobarbarbar
当然,这是几行代码,但是它是解决许多问题的极为通用的代码,所以很高兴知道它,很容易看到它在做什么,并且很容易进行修改以执行其他任何操作。
如果您希望简洁而不是清晰度和效率,可以将其简化为:
$ cat tst.awk
{
head = ""
cnt = 0
while ( match($0,old) ) {
head = head (++cnt < beg ? substr($0,RSTART,RLENGTH) : new)
$0 = substr($0,RSTART+RLENGTH)
}
print head $0
}
甚至是可怕的“单线”:
awk -v o='foo' -v n='bar' -v b=3 '{h="";c=0;while(s=match($0,o)){h=h (++c<b?substr($0,s,RLENGTH):n);$0=substr($0,s+RLENGTH)}$0=h$0}1' <<< "foofoofoofoofoo"
foofoobarbarbar
答案 2 :(得分:2)
awk *中的另一个用于单行处理:
$ echo foofoofoofoofoo |
awk -v n=3 'BEGIN{RS="foo"}{ORS=NR<n?RS:"bar"}1'
foofoobarbarbar
*已在gawk,mawk和Busybox awk上成功测试。在awk-20121220上失败。
答案 3 :(得分:2)
如果perl
没问题:
$ echo 'foofoofoofoofoo' | perl -pe '$c=0; s/foo/++$c<3 ? $& : "bar"/ge'
foofoobarbarbar
$c=0
对于每一行输入,初始化计数器e
修饰符用于允许Perl代码而不是替换部分中的字符串++$c<3 ? $& : "bar"
根据计数器,保留或替换匹配的文本答案 4 :(得分:2)
这可能对您有用:
sed -e ':a' -e 's/foo/\'$'\n/2' -e 'ta' -e 's/\'$'\n/bar/g' file
为第n个事件设置一个循环(在此示例中为2),并将其替换为唯一的字符/字符串(在此示例中为换行符)。如果循环失败,请用预期的字符串全局替换唯一的字符/字符串。
答案 5 :(得分:1)
您不能没有任何困难。
如GNU sed手册中所述:
g
将替换项应用于所有与 regexp 匹配的匹配项,而不仅仅是第一个匹配项。
号码
仅替换 regexp 的 number 个匹配项。
s
命令中的交互注意:POSIX标准未指定当您混合使用
g
和 number 修饰符时应该发生的情况,并且目前尚无广泛共识。在sed实现中的含义。对于GNU sed,交互定义为:忽略 number 之前的匹配项,然后匹配并替换 number th以后的所有匹配项。)
但是,在Mac OS X上,该方法有效:
▶ sed 's/foo/bar/3' <<< 'foofoofoofoofoo'
foofoobarfoofoo
这样做:
▶ sed 's/foo/bar/g' <<< 'foofoofoofoofoo'
barbarbarbarbar
但是,如果将它们一起使用,则会发出问题中指出的错误。
@oguzismail提供了一个聪明而简单的解决方案,我添加了此附加说明,因为我认为这会有所帮助。 1 他的答案的较早版本显示了这一点,令人困惑的是,在测试时什么也没做:
▶ sed ':a; s/foo/bar/3; ta' <<< 'foofoofoofoofoo'
foofoofoofoofoo
与此同时,BSD手册也未提供任何解释。但是,POSIX手册指出:
记录了b,t和:命令以忽略前导空白,但没有提及尾随空白。
因此,这可行:
▶ sed -e :a -e s/foo/bar/3 -e ta <<< 'foofoofoofoofoo'
foofoobarbarbar
这也有效:
▶ sed '
:a
s/foo/bar/3
ta
' <<< 'foofoofoofoofoo'
foofoobarbarbar
在任何情况下,脚本都在循环执行用foo
替换第三个bar
的操作,直到替换失败为止,脚本结束。请注意,t
(测试)的使用仅在上一个s///
命令替换了某些内容后才会分支。
要了解脚本在其每次循环迭代中的作用,这将很有帮助:
▶ sed -n -e :a -e s/foo/bar/3p -e ta <<< 'foofoofoofoofoo'
foofoobarfoofoo
foofoobarbarfoo
foofoobarbarbar
1 该答案的原始版本没有任何解释,尽管现在已经扩展了很多。奥古兹(Oguz)表示,他的偏好是让我在单独的答案中添加此信息。