bash $ {VAR // pesquisa / substituição} e comportamento regex estranho

8

Estou tentando pesquisar e substituir uma variável usando a expansão do parâmetro $ {VAR // search / replace}. Eu tenho um muito longo e malvado PS1, que eu quero resolver o tamanho de após a expansão. Para fazer isso eu tenho que remover um monte de seqüências de escape que eu coloco nele. No entanto, ao tentar remover todas as sequências do SGR ANSI CSI, me deparei com um problema com a minha sintaxe.

Dado meu PS1 de:

PS1=\[3]0;[\h] \w
# readability
search='\\[\033\[[0-9]*m\\]'
# do the magic
sane="${PS1//$search/}"
7\]\[3[1m\]\[3[37m\](\[3[m\]\[3[35m\]\u@\[3[m \]\[3[32m\]\h\[3[1m\]\[3[37m\]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m \]\t\[3[37m\]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m\]\[3[36m\]\w\[3[1m \]\[3[37m\])\[3[35m\]${git_branch}\[3[m\]\n$

(sim, está doente, eu sei ...)

Estou tentando fazer:

echo "${PS1//$search/}"
\[3]0;[\h] \w
$ search='\\[\033\[[0-9][0-9]m\\]'
$ echo "${PS1//$search/}"
\[3]0;[\h] \w
echo $PS1 | sed "s/$search//g"
\[3]0;[\h] \w
PS1=\[3]0;[\h] \w
# readability
search='\\[\033\[[0-9]*m\\]'
# do the magic
sane="${PS1//$search/}"
7\]\[3[1m\]\[3[37m\](\[3[m\]\[3[35m\]\u@\[3[m \]\[3[32m\]\h\[3[1m\]\[3[37m\]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m \]\t\[3[37m\]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m\]\[3[36m\]\w\[3[1m \]\[3[37m\])\[3[35m\]${git_branch}\[3[m\]\n$
7\](\u@\h)-(\t)-(\w)$(git_branch)\n$
7\]\[3[1m\](\[3[m\]\u@\[3[m\]\h\[3[1m \]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m\]\t\[3[1m\])\[3[m\]-\[3[1m \](\[3[m\]\w\[3[1m\])$(git_branch)\[3[m\]\n$
7\]\n$

No entanto, eles parecem ser gananciosos no ponto de [0-9] (quase como [0-9] é tratado como . ):

echo "${PS1//$search/}"
\[3]0;[\h] \w
$ search='\\[\033\[[0-9][0-9]m\\]'
$ echo "${PS1//$search/}"
\[3]0;[\h] \w
echo $PS1 | sed "s/$search//g"
\[3]0;[\h] \w%pre%7\](\u@\h)-(\t)-(\w)$(git_branch)\n$
7\]\[3[1m\](\[3[m\]\u@\[3[m\]\h\[3[1m \]\[3[1m\])\[3[m\]-\[3[1m\](\[3[m\]\t\[3[1m\])\[3[m\]-\[3[1m \](\[3[m\]\w\[3[1m\])$(git_branch)\[3[m\]\n$
7\]\n$

Se eu remover o * e alterar [0-9] para [0-9][0-9] (como isso é mais ilustrativo), chego mais perto do resultado esperado:

%pre%

Por que o * (zero ou mais) está fazendo coisas malucas? estou faltando alguma coisa aqui? Se eu passar a mesma regex através do sed, obtenho o resultado esperado:

%pre%     
por Drav Sloan 28.08.2013 / 04:22

3 respostas

5

Parece-me que você deseja remover coisas entre \[ e \] :

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

No entanto, bash de substituição é tão ineficiente que você provavelmente seria melhor disparar perl ou sed aqui, ou fazer isso em um loop como:

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

(essa é a sintaxe POSIX sh padrão acima, BTW).

E se você quiser o prompt expandido :

ep=$(PS4=$np;exec 2>&1;set -x;:); ep=${ep%:}
    
por 28.08.2013 / 11:01
5

Após algumas orientações do jordanm (e leitura da seção "Pattern Matching" da página man do bash), verifica-se que esses padrões usados pela expansão de parâmetros não são regex. No entanto, no meu caso específico, se shopt extglob estiver ativado, posso fazer isso:

search='\\[\033\[*([0-9])m\\]'

em que *([0-9]) é igual a [0-9]* em regex.

Parece que o extglob fornece alguns mecanismos semelhantes ao regex com (da bash man page):

          ?(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
    
por 28.08.2013 / 04:56
0

Faixa completa de seqüências ANSI suportadas pelo Bash puro

# Strips ANSI CSI (ECMA-48, ISO 6429) codes from text
# Param:
# 1: The text
# Return:
# &1: The ANSI stripped text
strip_ansi() {
  echo -n "${1//$'\e'[@A-Z\[\\]\^_]*([0-9:;<=>?])*([ \!\"#$%&\'()\^*+,\-.\/])[@A-Z\[\\]\^_\'a-z\{|\}~]/}"
}
    
por 15.02.2019 / 11:05