sed - 解释奇怪行为以替换重复的 ]\.*

sed - explanation of the strange behavior to replace the repetition of ]\.*

提问人:mon 提问时间:10/7/2023 更新时间:10/9/2023 访问量:72

问:

请帮助了解以下意外行为以及如何解决。

要替换 or 的重复,我可以用 .ab[ab]*

$ echo "abab" | sed -n 's/[ab]*/X/gp'
X

但是,不能按预期工作,产生如下结果,导致额外的替换。[]\.*]*a X b

$ echo "a \.*] b " | sed -n 's/[]\.*]*/X/pg'
XaX X XbX X

如果在正则表达式模式中不重复表达式,则一一替换,则有效。*

$ echo "a \.*] b " | sed -n 's/[]\.*]/X/pg'
a XXXX b 

因此,我怀疑重复表达导致了问题,但不确定出了什么问题。*

环境:

Mac OS 13.0.1 (22A400)

$ sed --version
sed (GNU sed) 4.9
正则表达式 SED

评论

3赞 Hans Kesting 10/7/2023
[ab]表示 'a' 或 'b',而不是 'ab'(尝试 aabb 作为输入)。对于 ab,请尝试(ab)
2赞 Renaud Pacalet 10/7/2023
错误的是,在正则表达式中意味着 0 次或更多次重复。尝试 1 个或更多。*\+
1赞 tripleee 10/7/2023
echo "x bb x" | sed -n 's/[ab]*/X/gp'演示了第一个示例如何遇到完全相同的问题。输出:XxX X XxX
0赞 tripleee 10/7/2023
Stack Overflow 正则表达式标记信息页面有一节是关于方括号含义的,如果您确实希望它们之间的文本逐字匹配。投票关闭为错别字(尽管我敢肯定,关于它的含义和它如何匹配根本没有文本,也有重复)。*
0赞 tripleee 10/7/2023
stackoverflow.com/questions/9801630/... 和/或 stackoverflow.com/questions/23479566/ 的可能重复...

答:

2赞 stevesliva 10/7/2023 #1

我发现有趣的是,字符类允许在第一个位置使用方括号,而没有转义。这是我不知道的好速记。

echo '1[]2[]3[]4' | sed 's/[]]/x/g'
1[x2[x3[x4

我以为需要转义,但第一个位置似乎也使反斜杠成为字面上的反斜杠:]\]

echo '1[]2[]3[]4' | sed 's/[\]]*/x/g'
1[]2[]3[]4

...令人惊讶的是,这什么也没取代,因为它正在寻找字面上的反斜杠来锚定比赛。

因此,了解这里发生的事情会有所帮助:

$ echo '1[\]2[\]3[\]4' | sed 's/[\]/x/g'
1[x]2[x]3[x]4
$ echo '1[\]2[\]3[\]4' | sed 's/[\]]/x/g'
1[x2[x3[x4

...第二个是令人困惑的。为什么有一个 x?哦,因为它是作为单匹配匹配的。\]

因此,我必须得出结论,除了字符类中的第一个位置之外,必须在任何地方转义右方括号。字面上的反斜杠有点相同(但在最后一个位置也可以容忍)。事实证明,IEEE标准支持这里。部分。的含义必须在别处讨论。]\]\

正在发生的另一件事是,这意味着“零或多次出现”,因此您还会看到替换与所有字符边界匹配:*

echo '1[]2[]3[]4' | sed 's/[]]*/x/g'
x1x[x2x[x3x[x4x

...对我来说,这并不奇怪。正如评论所说,在 BRE 中,您可以使用而不是表示“一个或多个”。在 ERE 中,这变成了一个简单的 .\+*+

最后,“或更多”可能会令人困惑:

$ echo abab | sed 's/[ab]*/X/g;'
X
$ echo abab | sed 's/[ab]/X/g;'
XXXX
$ echo abcab | sed 's/[ab]/X/g;'
XXcXX
$ echo abcab | sed 's/[ab]*/X/g;'
XcX

...啊哈,是的。 将 A 和 B 的不间断字符串作为单个匹配项进行匹配。[ab]*

评论

0赞 jhnc 10/9/2023
从技术上讲,我认为在 BRE 中作为“一个或多个”无效(尽管某些版本的 sed 可能允许它)。在 BRE 中,您应该使用 - 参见 9.3.6(5)。同样,应该是\+\{1,\}?\{0,1\}
0赞 Walter A 10/9/2023 #2

您从 .
这看起来不错,因为您的输入只有匹配的字符。另一个输入字符串也有同样的问题。
echo "abab" | sed -n 's/[ab]*/X/gp'

echo "string with abab inside" | sed -n 's/[ab]*/X/gp'
XsXtXrXiXnXgX XwXiXtXhX X XiXnXsXiXdXeX

这是因为还匹配长度为 0 的字符串。
您可以通过替换为 来修复它。
**\+

echo "string with abab inside" | sed -n 's/[ab]\+/X/gp'
string with X inside

# Or avoid the backslash with the '-r' option
echo "string with abab inside" | sed -rn 's/[ab]+/X/gp'
string with X inside

其他字符集的大小写也以相同的方式工作。

echo "a \.*] b " | sed -n 's/[]\.*]\+/X/pg'
a X b

使用 BRE,可以这样写

echo "a \.*] b " | sed -rn 's/[]\.*]{1,}/X/pg'
a X b