Bash $ {VAR // Suchen / Ersetzen} und seltsames Regex-Verhalten

8

Ich versuche, eine Variable mithilfe der Parametererweiterung $ {VAR // search / replace} zu suchen und zu ersetzen. Ich habe eine ziemlich lange und böse PS1, deren Größe ich nach der Erweiterung herausfinden möchte. Dazu muss ich eine Reihe von Escape-Sequenzen entfernen, die ich hineingesteckt habe. Beim Versuch, alle ANSI CSI SGR-Sequenzen zu entfernen, bin ich jedoch auf ein Problem mit meiner Syntax gestoßen.

Angesichts meiner PS1 von:

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$

(Ja, es ist krank, ich weiß ...)

Ich versuche zu tun:

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

Diese scheinen jedoch an der Stelle gierig zu sein [0-9](fast so, als würde [0-9]man sie .stattdessen behandeln ):

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

Wenn ich das entferne *und [0-9]zu [0-9][0-9](wie dies veranschaulichender ist) wechsle , komme ich dem erwarteten Ergebnis näher:

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

Warum macht die *(null oder mehr) verrückte Dinge? vermisse ich hier etwas Wenn ich den gleichen regulären Ausdruck durch sed gebe, erhalte ich das erwartete Ergebnis:

echo $PS1 | sed "s/$search//g"
\[\033]0;[\h] \w\007\](\u@\h)-(\t)-(\w)$(git_branch)\n$
Drav Sloan
quelle
3
Es ist kein regulärer Ausdruck, sondern nur ein Mustervergleich ähnlich einem Dateiglob. extglobbeeinflusst das Mustervergleichsverhalten.
Jordan
Bum, das wird der Grund sein - ich hatte eine Ahnung, dass es der Fall gewesen sein könnte: / Ich habe versucht, eine Klärung des Matching-Mechanismus zu finden, ohne großen Erfolg. macht sich auf den Weg, um über Extglob zu lesen (sieht aus wie ein Job für Sed!)
Drav Sloan
1
*([0-9])ist das Äquivalent zur [0-9]*Verwendung extglob.
Jordan
1
Wenn Sie die richtige Antwort erhalten haben, ist es akzeptabel, Ihre eigene Frage zu beantworten. Ich war froh, eine Anleitung gegeben zu haben.
Jordan
2
@ DravSloan - diese Aufforderung ist krank! 8-)
slm

Antworten:

6

Klingt für mich so, als ob Sie Dinge zwischen \[und entfernen möchten \]:

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

Allerdings bashist die Substitution so ineffizient , dass Sie wahrscheinlich besser wären aus dem Brennen perloder sedhier, oder es in einer Schleife tun:

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

(Das ist die Standard-POSIX-Sh-Syntax oben, übrigens).

Und wenn Sie die erweiterte Eingabeaufforderung daraus möchten :

ep=$(PS4=$np;exec 2>&1;set -x;:); ep=${ep%:}
Stéphane Chazelas
quelle
4
Ah, mein Tag ist vorbei, eine weitere Reihe von Symbolen größerer älterer Magie vom Hohenpriester der Kommandozeile, Stephane. Ich schwöre für die Hälfte Ihrer Beiträge, meine Augen sind auf die falsche Baudrate eingestellt, und ich bekomme einen Bildschirm voller Unordnung :) Und ja, das Endziel war es, alle Fluchtsequenzen zu entfernen: Es fiel mir nicht auf, nur zwischen [und zu entfernen ]. Vielen Dank!
Drav Sloan
5

Nach einigen Anleitungen von jordanm (und dem Lesen des Abschnitts "Pattern Matching" auf der Bash-Manpage) stellt sich heraus, dass diese von der Parametererweiterung verwendeten Muster keine regulären Ausdrücke sind. Jedoch für meinen speziellen Fall, wennshopt extglob kann ich jedoch Folgendes tun:

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

wo *([0-9]) ist das gleiche wie [0-9]*in Regex.

Es scheint, dass extglob einige Mechanismen bietet, die Regex ähneln (von der Bash-Manpage):

          ?(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
Drav Sloan
quelle
2
Ja, extglobimplementiert eine Teilmenge ksherweiterter Globs. ksh93hat tatsächlich einen printf-Operator zum Konvertieren zwischen Mustern und (AT & T) REs ( printf '%P\n' '\\\[[0-9]*\\\]'gibt *\\\[*([0-9])\\\]*)
Stéphane Chazelas
Hmm, es scheint, dass * [0-9] in anderen Regex-Abfragen funktioniert (ohne runde Klammern).
Macieksk
0

Pure Bash unterstützt alle ANSI-Sequenzen

# 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\{|\}~]/}"
}
Léa Gris
quelle