为什么这个参数扩展替换在bash 4.2中失败,但在5.1中有效



我正在尝试将一些代码从bash 5.1移植到4.2.46。一个试图从特定格式的字符串中剥离颜色代码的函数停止了工作。

这是这样一个格式的示例字符串text。我为此打开了扩展地球仪。

text="$(printf -- "%b%s%b" "[e[31m]" "hello" "[e[0m]")"
shopt -s extglob

在bash 5.1中,这个参数扩展可以删除所有的颜色代码和转义字符

bash-5.1$ echo "${text//$'[e'[/}"
31m]hello0m]
bash-5.1$ echo "${text//$'[e'[+([0-9])/}"
m]hellom]
bash-5.1$ echo "${text//$'[e'[+([0-9])m$']'/}"
hello

在bash 4.2.46中,在构建参数扩展时,我开始获得不同的行为。

bash-4.2.46$ echo "${text//$'[e'[/}"
31m]hellom]
bash-4.2.46$ echo "${text//$'[e'[+([0-9])/}"
[]hello[]  ## no longer matches because `+([0-9])` doesn't follow `[`

区别来自这一行:echo "${text//$'[e'[/}"

bash-5.1:    31m]hello0m]
bash-4.2.46: 31m]hellom]

以下是printf "%q" "${text//$'[e'[/}"显示的内容:

bash-5.1:    31m\]hello0m\]
bash-4.2.46: \31m\]hello\0m\]

4.2.26中额外的来自哪里?

即使我试图删除它,模式也会停止匹配:

bash-4.2.46$ echo "${text//$'[e'[\/}"
[]hello[]  ## no longer matches because `\` doesn't follow `[`

我猜可能有一个与参数扩展、反斜杠转义和扩展globbing有关的错误。

我的目标是编写能够在bash4.0之后运行的代码,所以我主要在寻找一种变通方法。不过,解释(错误报告等(为什么会出现行为差异会很好。

看起来像是bash中的一个bug。通过将可用版本一分为二,我发现4.2.53(1(-版本是最后一个出现此错误的版本。版本4.3.0(1(-发布修复了该问题。

更改列表提到了一些在这个方向上的错误修复。也许是以下错误修复之一:

本文档详细介绍了此版本bash-4.3-alpha与,以及之前的版本,bash-4.2版本。
[…]
zz。当使用模式替换词展开时,bash现在运行通过移除引号替换字符串,因为它允许在字符串用作转义符。这是不向后兼容的,所以可以通过将bash兼容模式设置为4.2.
[…]
eee来禁用它。修复了一个逻辑错误,该错误导致多字节区域设置中的扩展globbing为使用模式替换字展开时会导致失败。

解决方案

不使用extglobs的参数扩展,而是使用与实际正则表达式匹配的bash模式(在bash 3.0.0及更高版本中可用(:

text=$'[e[31m]hello[e[0m]'
while [[ "$text" =~ (.*)$'[e['[0-9]*'m]'(.*) ]]; do
text="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
done
echo "$text"

或者依赖外部(但posix标准化(工具,如sed:

text=$'[e[31m]hello[e[0m]'
text=$(sed $'s#\[e[[0-9]*m\]##g' <<< "$text")
echo "$text"

问题似乎是在"引号内解析${test//<here>}内的$'...'

$ test='f() { "${text//[$'''e'''[+([0-9])/}"; }; printf "%qn" "$(declare -f f)"'; echo -n 'bash4.1 '; docker run bash:4.1 bash -c "$test" ; echo -n 'bash5.1 '; bash -c "$test"
bash4.1 $'f () n{ n    "${text//\[E\[+([0-9])/}"n}'
bash5.1 $'f () n{ n    "${text//\['E'\[+([0-9])/}"n}'

只需使用一个变量。

esc=$'e'
echo "${text//\[$esc[+([0-9])/}"

相关内容

最新更新