bash $ {VAR // pesquisar / substituir} e comportamento estranho de expressões regulares


8

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

Dado o meu PS1 de:

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$

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

Estou tentando fazer:

# readability
search='\\\[\\033\[[0-9]*m\\\]'
# do the magic
sane="${PS1//$search/}"

No entanto, estes parecem ser gananciosos no ponto de [0-9](quase como [0-9]é tratado como um em .vez disso):

echo "${PS1//$search/}"
\[\033]0;[\h] \w\007\]\n$ 

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

$ 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$ 

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

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

3
Não é regex, é apenas a correspondência de padrões semelhante a um arquivo glob. extglobafeta o comportamento de correspondência de padrões.
jordanm

Bum, é por isso - tive um palpite de que poderia ter sido o caso: / Eu estava tentando encontrar esclarecimentos sobre o mecanismo de correspondência, sem muito sucesso. dirige-se para ler sobre extglob (parece um trabalho para sed!)
Drav Sloan

1
*([0-9])é o equivalente a [0-9]*usar extglob.
Jordanm 28/08

1
Se você obteve a resposta correta, é aceitável responder à sua própria pergunta. Fiquei feliz por ter fornecido algumas orientações.
Jordanm 28/08/2013

2
@ DravSloan - este prompt está doente! 8-)
slm

Respostas:


6

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

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

No entanto, a bashsubstituição é tão ineficiente que você provavelmente seria melhor disparar perlou sedaqui, ou fazê-lo 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 padrão do POSIX sh acima, BTW).

E se você quiser o prompt expandido a partir disso:

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

4
Ah, meu dia está completo, outro monte de símbolos de maior magia anciã do Sumo Sacerdote da Linha de Comando, Stephane. Juro por metade das suas postagens que meus olhos estão na taxa de transmissão errada e recebo uma tela de confusão :) E sim, o objetivo final era remover todas as seqüências de escape: não me ocorreu apenas remover entre [e ]. obrigado!
Drav Sloan

5

Após algumas orientações do jordanm (e a leitura da seção "Correspondência de padrões" da página do manual 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 extglobestiver ativado, eu posso fazer:

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

onde *([0-9])é o mesmo que [0-9]*em regex.

Parece que o extglob fornece alguns mecanismos semelhantes ao regex com (da página de manual do 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

2
Sim, extglobimplementa um subconjunto de kshglobs estendidos. ksh93na verdade tem um operador de printf para converter entre padrões e REs (AT & T) ( printf '%P\n' '\\\[[0-9]*\\\]'*\\\[*([0-9])\\\]*)
Stéphane Chazelas

Hmm, parece que * [0-9] funciona em outras consultas regex (sem colchetes).
precisa saber é o seguinte

0

Série completa de Pure Bash de sequências ANSI suportadas

# 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\{|\}~]/}"
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.