tom*_*afe 6 bash glob parameter-expansion
我正在尝试将一些代码从 bash 5.1 移植到 4.2.46。一个尝试从特定格式的字符串中去除颜色代码的函数停止工作。
text这是这种格式的示例字符串。我为此打开了扩展通配符。
text="$(printf -- "%b%s%b" "\[\e[31m\]" "hello" "\[\e[0m\]")"
shopt -s extglob
Run Code Online (Sandbox Code Playgroud)
在 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
Run Code Online (Sandbox Code Playgroud)
在 bash 4.2.46 中,当我构建参数扩展时,我开始出现不同的行为。
bash-4.2.46$ echo "${text//$'\[\e'\[/}"
\31m\]hello\0m\]
bash-4.2.46$ echo "${text//$'\[\e'\[+([0-9])/}"
\[\]hello\[\] ## no longer matches because `+([0-9])` doesn't follow `\[`
Run Code Online (Sandbox Code Playgroud)
差异来自于这一行:echo "${text//$'\[\e'\[/}"
bash-5.1: 31m\]hello0m\]
bash-4.2.46: \31m\]hello\0m\]
Run Code Online (Sandbox Code Playgroud)
显示内容如下printf "%q" "${text//$'\[\e'\[/}":
bash-5.1: 31m\\\]hello0m\\\]
bash-4.2.46: \\31m\\\]hello\\0m\\\]
Run Code Online (Sandbox Code Playgroud)
4.2.26 中的额外内容从哪里来\?
即使当我尝试删除它时,模式也会停止匹配:
bash-5.1: 31m\]hello0m\]
bash-4.2.46: \31m\]hello\0m\]
Run Code Online (Sandbox Code Playgroud)
我猜测可能存在与参数扩展、反斜杠转义和扩展通配符相关的错误。
我的目标是编写适用于 bash 4.0 及以上版本的代码,因此我主要寻找解决方法。不过,对为什么会发生行为差异的解释(错误报告等)会很棒。
看起来像是 bash 中的一个错误。通过平分可用版本,我发现 4.2.53(1)-release 是存在此错误的最后一个版本。版本 4.3.0(1)-release 修复了该问题。
更改列表提到了这方面的一些错误修复。也许这是以下错误修复之一:
本文档详细介绍了此版本 bash-4.3-alpha 与之前版本 bash-4.2-release 之间的更改。
[...]
Z Z。当使用模式替换单词扩展时,bash 现在通过删除引号来运行替换字符串,因为它允许该字符串中的引号充当转义字符。这不向后兼容,因此可以通过将 bash 兼容模式设置为 4.2 来禁用它。
[...]
呃。修复了一个逻辑错误,该错误导致多字节语言环境中的扩展通配符在使用模式替换字扩展时导致失败。
不要使用 extglob 的参数扩展,而是使用与实际正则表达式匹配的 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"
Run Code Online (Sandbox Code Playgroud)
或者依赖外部(但 posix 标准化)工具,例如sed:
text=$'\[\e[31m\]hello\[\e[0m\]'
text=$(sed $'s#\\\[\e[[0-9]*m\\\]##g' <<< "$text")
echo "$text"
Run Code Online (Sandbox Code Playgroud)
问题似乎是在引号内解析$'...'内部。${test//<here>}"
$ test='f() { "${text//\[$'\''\e'\''\[+([0-9])/}"; }; printf "%q\n" "$(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}'
Run Code Online (Sandbox Code Playgroud)
只需使用一个变量即可。
esc=$'\e'
echo "${text//\\\[$esc\[+([0-9])/}"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
209 次 |
| 最近记录: |