Regex-Matching in einer Bash-if-Anweisung


88

Was habe ich hier falsch gemacht?

Es wird versucht, eine Zeichenfolge zu finden, die Leerzeichen, Kleinbuchstaben, Großbuchstaben oder Zahlen enthält. Sonderzeichen wären auch nett, aber ich denke, das erfordert das Entkommen bestimmter Zeichen.

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

Dies testet offensichtlich nur auf obere, untere, Zahlen und Leerzeichen. Funktioniert aber nicht.

* UPDATE *

Ich denke, ich hätte genauer sein sollen. Hier ist die eigentliche Codezeile.

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* UPDATE *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'

Welche Shell benutzt du eigentlich? / bin / sh? / bin / bash? / bin / csh?
Willem Van Onsem

8
Es ist sicherer, den regulären Ausdruck in eine Variable zu setzen. re='...whatever...'; [[ $string =~ $re ]](ohne Anführungszeichen - dies ist einer der seltenen Fälle, in denen sie etwas kaputt machen, das ohne sie funktionieren würde).
Charles Duffy

3
Setzen Sie stattdessen einfache Anführungszeichen um die Aufgabe. Doppelte Anführungszeichen schützen die Sonderzeichen nicht richtig.
Tripleee

Vielen Dank Charles! Es ist immer noch in Ordnung, es nicht in eine Variable zu setzen, aber es darf NICHT in Anführungszeichen gesetzt werden! Zum Beispiel: [[ $var =~ .* ]]für Match Regex .*(alles). Ich denke, wenn Sie Anführungszeichen verwenden, werden die Anführungszeichen selbst als Teil der Regex betrachtet…
Stéphane

4
Ich habe eine Zusammenfassung gefunden: (1.) Speichern Sie das Muster in einer Variablen mit einfachen Anführungszeichen pattern='^hello[0-9]*$'(2.) im Doppelquadrat-Ausdruck, wenn Sie eine Regex-Übereinstimmung benötigen. Zitieren Sie das Muster NICHT, da das Zitieren die Regex-Muster-Übereinstimmung deaktiviert. (dh der Ausdruck [[ "$x" =~ $pattern ]]stimmt mit Regex überein und der Ausdruck [[ "$x" =~ "$pattern" ]]deaktiviert den Regex-Abgleich und entspricht[[ "$x" == "$pattern" ]] ).
Trevor Boyd Smith

Antworten:


180

Es gibt ein paar wichtige Dinge, die Sie über die [[ ]]Konstruktion von Bash wissen sollten . Der Erste:

Wortaufteilung und Pfadnamenerweiterung werden für die Wörter zwischen [[und nicht ausgeführt ]]. Tilde-Erweiterung, Parameter- und Variablenerweiterung, arithmetische Erweiterung, Befehlssubstitution, Prozessersetzung und Anführungszeichenentfernung werden durchgeführt.

Das zweite:

Ein zusätzlicher binärer Operator, '= ~', ist verfügbar. Die Zeichenfolge rechts neben dem Operator wird als erweiterter regulärer Ausdruck betrachtet und entsprechend abgeglichen. Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um die Übereinstimmung zu erzwingen als Zeichenfolge .

Folglich wird $vauf beiden Seiten von =~auf den Wert dieser Variablen erweitert, aber das Ergebnis wird nicht durch Wörter geteilt oder durch Pfadnamen erweitert. Mit anderen Worten, es ist absolut sicher, variable Erweiterungen auf der linken Seite nicht in Anführungszeichen zu setzen, aber Sie müssen wissen, dass variable Erweiterungen auf der rechten Seite stattfinden.

Wenn Sie also schreiben: [[ $x =~ [$0-9a-zA-Z] ]]wird das $0Innere des regulären Ausdrucks rechts erweitert, bevor der reguläre Ausdruck interpretiert wird. Dies führt wahrscheinlich dazu, dass der reguläre Ausdruck nicht kompiliert werden kann (es sei denn, die Erweiterung $0endet mit einer Ziffer oder einem Interpunktionssymbol, dessen ASCII-Wert kleiner als ist eine Ziffer). Wenn Sie die rechte Seite so zitieren [[ $x =~ "[$0-9a-zA-Z]" ]], wird die rechte Seite als gewöhnliche Zeichenfolge behandelt, nicht als regulärer Ausdruck (und $0wird immer noch erweitert). Was Sie in diesem Fall wirklich wollen, ist[[ $x =~ [\$0-9a-zA-Z] ]]

In ähnlicher Weise wird der Ausdruck zwischen [[und ]]in Wörter aufgeteilt, bevor der reguläre Ausdruck interpretiert wird. Leerzeichen in der Regex müssen also maskiert oder in Anführungszeichen gesetzt werden. Wenn Sie Buchstaben, Ziffern oder Leerzeichen abgleichen möchten, können Sie Folgendes verwenden : [[ $x =~ [0-9a-zA-Z\ ] ]]. Andere Zeichen müssen ebenfalls maskiert werden #, was einen Kommentar auslösen würde, wenn sie nicht zitiert würden. Natürlich können Sie das Muster in eine Variable einfügen:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

Für reguläre Ausdrücke, die viele Zeichen enthalten, die maskiert oder in Anführungszeichen gesetzt werden müssten, um durch Bashs Lexer zu gelangen, bevorzugen viele Menschen diesen Stil. Aber Vorsicht: In diesem Fall können Sie die Variablenerweiterung nicht zitieren:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

Schließlich denke ich, dass Sie versuchen zu überprüfen, ob die Variable nur gültige Zeichen enthält. Der einfachste Weg, diese Prüfung durchzuführen, besteht darin, sicherzustellen, dass sie kein ungültiges Zeichen enthält. Mit anderen Worten, ein Ausdruck wie dieser:

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

!negiert den Test und verwandelt ihn in einen Operator "stimmt nicht überein", und eine [^...]Regex-Zeichenklasse bedeutet "jedes andere Zeichen als ...".

Die Kombination von Parametererweiterungs- und Regex-Operatoren kann die Syntax von Bash-regulären Ausdrücken "fast lesbar" machen, aber es gibt immer noch einige Fallstricke. (Gibt es nicht immer?) Einer ist , dass Sie nicht setzen könnten ]in $valid, auch wenn $validzitiert wurden, außer ganz am Anfang. (Dies ist eine Posix-Regex-Regel: Wenn Sie ]in eine Zeichenklasse aufnehmen möchten , muss sie am Anfang stehen. Sie -kann am Anfang oder am Ende stehen. Wenn Sie also beide benötigen ]und -, müssen Sie mit beginnen ]und mit enden -. was zu dem regulären Ausdruck : „ich weiß , was ich tue“ Emoticon: [][-])


6
Ich möchte nur darauf hinweisen, dass "! ~ Ist der" nicht übereinstimmende "Operator" nicht wahr ist. Entweder verwenden if ! [[ $x =~ $y ]]oderif [[ ! $x =~ $y ]]
Alkohol

Shellchecker ist anderer Meinung ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Leonardo

4
@leonard: Wie unterscheidet sich das von meiner Aussage "Sie können die Variablenerweiterung nicht zitieren" und dem Kommentar "Das funktioniert nicht"? Was ist daran unklar?
Rici

1
@jinbeomhong: Der Ausdruck selbst wird wie gewohnt mit Leerzeichen in Wörter unterteilt. Parameter- und Befehlserweiterungen werden jedoch nicht in Wörter aufgeteilt.
Rici

1
@ Jinbeomhong: Ich sage nichts anderes als das Bash-Handbuch. "Die Wörter zwischen [[und ]]" werden aus dem Programmtext analysiert, genauso wie Befehlszeilen in Wörter analysiert werden. Im Gegensatz zu Befehlszeilen werden die Wörter nach Erweiterungen jedoch nicht geteilt.
Rici

27

Falls jemand ein Beispiel mit Variablen haben wollte ...

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi

13

Ich würde es vorziehen, dafür zu verwenden [:punct:]. Könnte a-zA-Z09-9auch nur sein [:alnum:]:

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.