bash ${VAR//search/replace} 和奇怪的正则表达式行为

Dra*_*oan 13 bash variable-substitution

我正在尝试使用 ${VAR//search/replace} 参数扩展对变量进行一些搜索和替换。我有一个很长很邪恶的 PS1,我想算出扩展后的大小。为此,我必须删除一堆我塞进去的转义序列。但是,在尝试删除所有 ANSI CSI SGR 序列时,我遇到了语法问题。

鉴于我的 PS1:

PS1=\[\033]0;[\h] \w\007\]\[\033[1m\]\[\033[37m\](\[\033[m\]\[\033[35m\]\u@\[\033[m
\]\[\033[32m\]\h\[\033[1m\]\[\033[37m\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m
\]\t\[\033[37m\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m\]\[\033[36m\]\w\[\033[1m
\]\[\033[37m\])\[\033[35m\]${git_branch}\[\033[m\]\n$
Run Code Online (Sandbox Code Playgroud)

(是的,我知道它生病了...)

我正在尝试做:

# readability
search='\\\[\\033\[[0-9]*m\\\]'
# do the magic
sane="${PS1//$search/}"
Run Code Online (Sandbox Code Playgroud)

然而,这些似乎是贪婪的[0-9](几乎就像[0-9]被当作​​ a.来对待):

echo "${PS1//$search/}"
\[\033]0;[\h] \w\007\]\n$ 
Run Code Online (Sandbox Code Playgroud)

如果我删除*, 并更改[0-9][0-9][0-9](因为这更具说明性),我会更接近预期的结果:

$ search='\\\[\\033\[[0-9][0-9]m\\\]'
$ echo "${PS1//$search/}"
\[\033]0;[\h] \w\007\]\[\033[1m\](\[\033[m\]\u@\[\033[m\]\h\[\033[1m
\]\[\033[1m\])\[\033[m\]-\[\033[1m\](\[\033[m\]\t\[\033[1m\])\[\033[m\]-\[\033[1m
\](\[\033[m\]\w\[\033[1m\])$(git_branch)\[\033[m\]\n$ 
Run Code Online (Sandbox Code Playgroud)

为什么*(零个或多个)做疯狂的事情?我在这里错过了什么吗?如果我通过 sed 传递相同的正则表达式,我会得到预期的结果:

echo $PS1 | sed "s/$search//g"
\[\033]0;[\h] \w\007\](\u@\h)-(\t)-(\w)$(git_branch)\n$
Run Code Online (Sandbox Code Playgroud)

Dra*_*oan 8

经过 jordanm 的一些指导(并阅读了 bash 手册页的“模式匹配”部分),结果证明参数扩展使用的这些模式不是正则表达式。但是对于我的具体情况,如果shopt extglob打开,我可以这样做:

search='\\\[\\033\[*([0-9])m\\\]'
Run Code Online (Sandbox Code Playgroud)

哪里*([0-9])一样[0-9]*正则表达式。

似乎 extglob 提供了一些类似于正则表达式的机制(来自 bash 手册页):

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns
Run Code Online (Sandbox Code Playgroud)

  • 是的,`extglob` 实现了 `ksh` 扩展 glob 的一个子集。`ksh93` 实际上有一个 printf 操作符来在模式和 (AT&T) REs 之间进行转换(`printf '%P\n' '\\\[[0-9]*\\\]'` 给出了 `*\\\[ *([0-9])\\\]*`) (2认同)

Sté*_*las 8

在我看来,您想删除\[和之间的内容\]

$ shopt -s extglob
$ printf '%s\n' "${PS1//\\\[*(\\[^]]|[^\\])\\\]/}"
(\u@\h)-(\t)-(\w)${git_branch}\n$
Run Code Online (Sandbox Code Playgroud)

但是,bash替换效率非常低,您可能最好触发perlsed在这里,或者在循环中执行,例如:

p=$PS1 np=
while :; do
  case $p in
    (*\\\[*\\\]*) np=$np${p%%\\\[*};p=${p#*\\\]};;
    (*) break;;
  esac
done
np=$np$p
printf '%s\n' "$np"
Run Code Online (Sandbox Code Playgroud)

(这是上面的标准 POSIX sh 语法,顺便说一句)。

如果您想要扩展提示:

ep=$(PS4=$np;exec 2>&1;set -x;:); ep=${ep%:}
Run Code Online (Sandbox Code Playgroud)

  • 啊,我的一天结束了,来自命令行大祭司 Stephane 的另一堆更高级魔法的符号。我发誓在你的一半帖子中,我的眼睛设置了错误的波特率,我得到了一个混乱的屏幕:) 是的,最终目标是删除所有转义序列:我并没有想到只是在` [` 和`]`。谢谢! (5认同)