Verwenden Sie den regulären Ausdruck in if-Bedingung in bash

87

Ich frage mich, welche allgemeine Regel es gibt, reguläre Ausdrücke in der if-Klausel in bash zu verwenden.

Hier ist ein Beispiel

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

Warum passen die letzten drei nicht zusammen?

Ich hoffe, Sie konnten so viele allgemeine Regeln wie möglich angeben, nicht nur für dieses Beispiel.

Tim
quelle

Antworten:

127

Bei Verwendung eines Glob-Musters steht ein Fragezeichen für ein einzelnes Zeichen und ein Stern für eine Folge von null oder mehr Zeichen:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

Bei Verwendung eines regulären Ausdrucks steht ein Punkt für ein einzelnes Zeichen und ein Stern für null oder mehr des vorhergehenden Zeichens. " .*" Stellt also null oder mehr Zeichen dar, " a*" steht für null oder mehr "a", " [0-9]*" steht für null oder mehr Ziffern. Ein weiteres nützliches (unter vielen) ist das Pluszeichen, das eines oder mehrere der vorhergehenden Zeichen darstellt. " [a-z]+" Steht also für ein oder mehrere Alpha-Kleinbuchstaben (im Gebietsschema C - und einigen anderen).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi
Bis auf weiteres angehalten.
quelle
Es gibt also zwei Möglichkeiten für den String-Abgleich: Glob-Muster und regulärer Ausdruck? Wird glob pettern nicht nur für Dateinamen verwendet? Wann sollte in Bash das Glob-Muster und wann der reguläre Ausdruck verwendet werden? Vielen Dank!
Tim
1
@ Tim: Globbing ist in den meisten oder allen Versionen von Bash verfügbar. Regex Matching ist nur in Version 3 und höher verfügbar, ich würde jedoch empfehlen, es nur in Version 3.2 und höher zu verwenden. Regexes sind viel vielseitiger als Globbing.
Bis auf weiteres angehalten.
13
if [[ $gg =~ ^....grid.* ]]
Ignacio Vazquez-Abrams
quelle
1
Sie sollten in der Lage sein, ". {4}" anstelle von "...." zu verwenden, dh "^. {4} grid. *". Es kann leichter zu lesen und zu verstehen sein.
user276648
7

Hinzufügen dieser Lösung mit grepund grundlegenden integrierten shFunktionen für diejenigen, die an einer portableren Lösung interessiert sind (unabhängig von der bashVersion; funktioniert auch mit einfachen alten Lösungen sh, auf Nicht-Linux-Plattformen usw.)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Einige grepInkarnationen unterstützen auch die -q(leise) Option als Alternative zur Umleitung zu /dev/null, aber die Umleitung ist wieder die portabelste.

vladr
quelle
habe ein schließendes ")" für egrep vergessen
ghostdog74
5
Verwenden Sie grep -qanstelle von grep >/dev/null.
Bfontaine
3

@OP,

Wird glob pettern nicht nur für Dateinamen verwendet?

Nein, das "Glob" -Muster wird nicht nur für Dateinamen verwendet. Sie können damit auch Zeichenfolgen vergleichen. In Ihren Beispielen können Sie case / esac verwenden, um nach Zeichenfolgenmustern zu suchen.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

Wann sollte in Bash das Glob-Muster und wann der reguläre Ausdruck verwendet werden? Vielen Dank!

Regex sind vielseitiger und "praktischer" als "Glob-Muster". Wenn Sie jedoch keine komplexen Aufgaben ausführen, die durch "Globbing / Extended Globbing" nicht einfach erledigt werden können, müssen Sie Regex nicht verwenden. Regex wird für die Version von bash <3.2 (wie von Dennis erwähnt) nicht unterstützt, Sie können jedoch weiterhin erweitertes Globbing verwenden (durch Festlegen extglob). Informationen zum erweiterten Globbing finden Sie hier und einige einfache Beispiele hier .

Update für OP: Beispiel zum Suchen von Dateien, die mit 2 Zeichen beginnen (die Punkte "." Bedeuten 1 Zeichen), gefolgt von "g" mit Regex

zB Ausgabe

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

Oben werden die Dateien abgeglichen, da ihre Namen 2 Zeichen gefolgt von "g" enthalten. (dh ..g).

Das Äquivalent zu Globbing ist ungefähr so: (Siehe Referenz für die Bedeutung von ?und *)

$ for file in ??g*; do echo $file; done
abg
degree
..g
Ghostdog74
quelle
Danke ghostdog74. Kann in Bash mit einer Version höher als 3.2 der reguläre Ausdruck verwendet werden, um das Glob-Muster dort zu ersetzen, wo letzteres erscheint? Oder kann ein regulärer Ausdruck nur unter bestimmten Umständen verwendet werden? Zum Beispiel habe ich festgestellt, dass "ls ?? g" funktioniert, während "ls ..g" nicht funktioniert.
Tim
Es gibt kein Hindernis für die Verwendung von Regex, wenn dies erforderlich ist. Es liegt an dir. Beachten Sie, dass sich die Regex-Syntax von der Shell-Globbing-Syntax unterscheidet. so ls ..gfunktioniert nicht. Sie weisen die Shell an, nach einer Datei mit dem Namen zu suchen ..g. Was über regex Syntax lernen, können Sie versuchen perldoc perlretut, perldoc perlrequickoder eine nicht info sedauf der Kommandozeile.
Ghostdog74