奇怪的 sed 行为
Strange sed behaviour
我有这个 POSIX 兼容的 shell 脚本。它采用分隔字符串 w.r.t。 |
并在子字符串前加上 -
如果它们的长度是单个字符:
#!/bin/sh
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/g'
这输出:
-k|k|jill|hill|-k
注意它没有考虑夹在两个定界符之间的 k(即 |k|
)。
更奇怪的是,如果我将原始片段中的特殊字符更改为其他任何字符,它确实会在前面添加 -
(请注意更改:^
到 something
;$
到 different
), 但显然不是第一个和最后一个 k:
#!/bin/sh
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|something\)\([[:alnum:]]\)\([|]\|different\)/-/g'
输出:
k|-k|jill|hill|k
起初我以为是因为 $
和 ^
位置字符不是可选的。但是,对于第一个示例中的第一个标志中的 $
和最后一个标志中的 ^
,它们显然是可选的。
我很好奇,为什么这不起作用,我可以用类似的 sed 表达式做我想做的事吗?
引擎无法在中间匹配 k
,因为它之前有一个成功的匹配,它的字符 (k|
) 在它之前就被消耗掉了,它不能陷入匹配另一个 |
。说如果你的输入字符串是:
kk|k|jill|hill|k
您会看到期望的输出。对于解决方法,我建议您设置 -r
选项以使 ERE 语法能够使用单词边界标记:
printf '%s\n' "k|k|jill|hill|k" | sed -r 's/\b([[:alnum:]])(\||$)/-/g'
或更一般地说:
printf '%s\n' "k|k|jill|hill|k" | sed -r 's/\b[[:alnum:]]\b/-[=12=]/g'
请注意,如果将 sed 脚本从全局搜索和替换更改为循环,则可以获得所需的输出:
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/g'
-k|k|jill|hill|-k
对比
printf '%s\n' "k|k|jill|hill|k" | sed '
:a
s/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/
ta
'
-k|-k|jill|hill|-k
参考:https://www.gnu.org/software/sed/manual/html_node/Programming-Commands.html
我有这个 POSIX 兼容的 shell 脚本。它采用分隔字符串 w.r.t。 |
并在子字符串前加上 -
如果它们的长度是单个字符:
#!/bin/sh
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/g'
这输出:
-k|k|jill|hill|-k
注意它没有考虑夹在两个定界符之间的 k(即 |k|
)。
更奇怪的是,如果我将原始片段中的特殊字符更改为其他任何字符,它确实会在前面添加 -
(请注意更改:^
到 something
;$
到 different
), 但显然不是第一个和最后一个 k:
#!/bin/sh
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|something\)\([[:alnum:]]\)\([|]\|different\)/-/g'
输出:
k|-k|jill|hill|k
起初我以为是因为 $
和 ^
位置字符不是可选的。但是,对于第一个示例中的第一个标志中的 $
和最后一个标志中的 ^
,它们显然是可选的。
我很好奇,为什么这不起作用,我可以用类似的 sed 表达式做我想做的事吗?
引擎无法在中间匹配 k
,因为它之前有一个成功的匹配,它的字符 (k|
) 在它之前就被消耗掉了,它不能陷入匹配另一个 |
。说如果你的输入字符串是:
kk|k|jill|hill|k
您会看到期望的输出。对于解决方法,我建议您设置 -r
选项以使 ERE 语法能够使用单词边界标记:
printf '%s\n' "k|k|jill|hill|k" | sed -r 's/\b([[:alnum:]])(\||$)/-/g'
或更一般地说:
printf '%s\n' "k|k|jill|hill|k" | sed -r 's/\b[[:alnum:]]\b/-[=12=]/g'
请注意,如果将 sed 脚本从全局搜索和替换更改为循环,则可以获得所需的输出:
printf '%s\n' "k|k|jill|hill|k" | sed 's/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/g'
-k|k|jill|hill|-k
对比
printf '%s\n' "k|k|jill|hill|k" | sed '
:a
s/\([|]\|^\)\([[:alnum:]]\)\([|]\|$\)/-/
ta
'
-k|-k|jill|hill|-k
参考:https://www.gnu.org/software/sed/manual/html_node/Programming-Commands.html