Bash - 当字符串包含未知长度但相同字符时的多字符字符串替换

Bash - Multi-character string replacement when strings consist of unknown length but same character

假设一个多行文本字符串,其中一些行以关键字符(在我们的示例中为“#”)开头。进一步假设您希望用不同的字符(在我们的例子中为“O”)替换目标字符(在我们的例子中为“o”)的所有实例,如果 - 并且仅当 - 该目标字符出现为两个或更多相邻副本(例如,“ooo”)。此替换将在所有不以键字符开头且必须区分大小写的行中完成。

例如,下面几行...

#Foo bar
Foo bar
#Baz foo
Baz foo

应该转换成:

#Foo bar
FOO bar
#Baz foo
Baz fOO

以下使用 sed 的尝试未保留正确数量的目标字符:

$ echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/^#/!s/o\{2,\}/O/g'
#Foo bar
FO bar
#Baz foo
Baz fO

什么代码(使用 sed 或其他)可以正确执行所需的替换?

使用sed

$ echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | sed '/#/!s/o\{2\}/\U&/'
#Foo bar
FOO bar
#Baz foo
Baz fOO

您可以使用 Perl:

echo -e "#Foo bar\nFoo bar\n#Baz foo\nBaz foo" | perl -pe 's/^#.*(*SKIP)(*F)|o{2,}/"O" x length($&)/ge'

这里,^#.*(*SKIP)(*F)匹配并跳过所有以#开头的行,然后o{2,}匹配两个或多个o字符,"O" x length($&)替换这些与 O 匹配,重复匹配大小次数($& 是匹配值)。请注意 g 之后的 e 标志,用于评估 right-hand 侧的字符串。

参见online demo

#!/bin/bash
s="#Foo bar
Foo bar
#Baz foo
Baz foo"
perl -pe 's/^#.*(*SKIP)(*F)|o{2,}/"O" x length($&)/ge' <<< "$s"

输出:

#Foo bar
FOO bar
#Baz foo
Baz fOO

这可能对你有用 (GNU sed):

sed -E '1{x;s/^/O/;x}
        /^#/b
       :a;/oo+/!b;s/oo+/\n&\n/;tb
       :b;G;s/\n\n(.*)\n.$//;ta;s/\n[^\n](.*\n.*)\n(.)$/\n/;tb' file

在概述中,使用一个或多个指定字符(在本例中为 O)替换两个或多个 o,其中一行的开头不是 #

用指定的字符填充space。

如果该行以 # 开头,则打断。

如果该行不包含两个或o,则打断。

否则,用换行符将两个或更多 o 括起来。

追加替换字符,然后用指定字符替换两个换行符之间的 non-newline 个字符。

当前 o 组的所有替换项都已替换后,继续按上述步骤检查更多项。

找到所有替换后,打印修改后的行。


允许多次替换的解决方案:

sed -E '1{x;s/^/oOxX/;x}
        /^#/b;
        :a;G;/((.)+)(.*\n(..)*)/!s/\n.*//;t;s//\n\n/;tb
        :b;s/\n\n(.*)\n.*$//;ta;s/\n(.)(.*\n.*\n(..)*(.))/\n/;tb' file