Macht die bash-support-Wort-Grenze reguläre Ausdrücke?
Ich bin versucht, auf das Vorhandensein eines Wortes in einer Liste hinzufügen, bevor wieder dieses Wort (um Duplikate zu vermeiden). Ich bin mit der bash 4.2.24 und versuche die unten:
[[ $foo =~ \bmyword\b ]]
auch
[[ $foo =~ \<myword\> ]]
Jedoch weder zu funktionieren scheint. Sie sind erwähnt in der bash docs Beispiel: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_01.html.
Ich vermute ich mache etwas falsch, aber ich bin nicht sicher, was.
- Nebenbei: das Verhalten der bash ist
=~
Betreiber ist Plattform-abhängig, da die host-Plattform mit der regex-Bibliotheken verwendet. So, zum Beispiel, auch mit dem workaround in der akzeptierten Antwort,\b
und\<
/\>
funktionieren nicht auf BSD-ähnlichen Systemen wie OSX. Umgekehrt OSX unterstützt[[:\<:]]
und[[:\>:]]
, die nicht auf Linux zu arbeiten.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ja, alle aufgeführten regex-Erweiterungen unterstützt werden, aber du wirst mehr Glück haben, setzen die Muster in einer Variablen, bevor Sie Sie verwenden. Versuchen Sie dies:
Graben um, ich fand diese Frage, deren Antworten scheint zu erklären, warum das Verhalten ändert, wenn die regex geschrieben inline, wie in deinem Beispiel.
Anmerkung der Redaktion: Die verlinkte Frage ist nicht erklären, die OP ' s problem, sondern lediglich erklärt, wie, beginnend mit der Bash-version 3.2 regexes (oder zumindest die spezielle regex-chars.) muss standardmäßig werden nicht börsennotierten als solche behandelt zu werden - das ist genau das, was der OP versucht.
Aber die Problemumgehungen, die in dieser Antwort sind wirksam.
Werden Sie wahrscheinlich haben, um schreiben Sie Ihre tests so verwenden Sie eine temporäre variable, die für Ihre regexes, oder verwenden Sie die 3.1-Kompatibilität-Modus:
[[ $foo =~ $(echo '\<myword\>') ]]
. Es ist immer noch störend ausführlich, aber zumindest nicht erforderlich, einen streunenden variable.tl;dr
Sicher zu sein, nicht mit einem regex wörtliche mit
=~
.Verwenden Sie stattdessen:
=~
RS.Ob
\b
und\<
/\>
sind unterstützt hängt von der die host-Plattform, nicht Bash:[[:<:]]
und[[:>:]]
statt, die im Rahmen einer nicht börsennotierten regex wörtliche, die geschützt werden müssen, wie[[:\<:]]
und[[:\>:]]
; das folgende funktioniert wie erwartet, aber nur auf BSD/macOS:[[ ' myword ' =~ [[:\<:]]myword[[:\>:]] ]] && echo YES # OK
Den problem würde nicht entstehen - auf jeder Plattform - , wenn Sie beschränkt Ihre regex, um die Konstrukte in der POSIX-ERE (extended regular expression) - Spezifikation.
Leider POSIX-EREs tun nicht support-Wort-Grenze Behauptungen, aber Sie können emulieren Sie - siehe den letzten Abschnitt.
Als auf macOS, keine
\
-Präfix-Konstrukte werden unterstützt, so dass der praktische Charakter-Klasse Verknüpfungen wie\s
und\w
sind nicht verfügbar.Jedoch, die up-Seite ist, dass solche ERE-konform regexes sind dann portable (arbeiten auf Linux-und macOS zum Beispiel)
=~
ist der seltene Fall (der einzige Fall?) der built-in Bash-Funktion, deren Verhalten ist Plattform-abhängigen: Er verwendet die regex-Bibliotheken, die von der Plattform ausgeführt wird, was in verschiedenen regex-Aromen, die auf verschiedenen Plattformen.So, es ist in der Regel nicht trivial und erfordert zusätzliche Pflege, um zu schreiben portable code, der verwendet die
=~
Betreiber.Kleben mit POSIX-EREs ist der einzige robuste Ansatz, was bedeutet, dass Sie arbeiten müssen, um Ihre Einschränkungen - siehe unten Abschnitt.
Wenn Sie mehr wissen wollen, Lesen Sie weiter.
Auf Bash v3.2+ (es sei denn, die
compat31
shopt
option gesetzt ist), die RHS (right-hand side-operand) der=~
Betreiber muss nicht börsennotierten, um zu erkennen, wie ein regex (wenn Sie zitieren der rechten Operanden=~
führt regelmäßig string-Vergleich statt).genauer, zumindest die spezielle regex-Zeichen und Sequenzen müssen ohne Anführungszeichen, so ist es OK und sinnvoll zu zitieren, diejenigen Teilstrings, die ergriffen werden sollten buchstäblich; z.B.
[[ '*' =~ ^'*' ]]
entspricht, weil^
ist nicht börsennotierten und damit richtig erkannt, der start-of-string-Anker, in der Erwägung, dass*
, die in der Regel eine spezielle regex-char, Spiele buchstäblich aufgrund der Quotierung.Jedoch, es scheint sich um ein design-Einschränkung in (mindestens)
bash 3.x
dass verhindert die Verwendung von\
-Präfix regex-Konstrukte (z.B.,\<
,\>
,\b
,\s
,\w
, ...) in einem wörtliche=~
RHS; die Einschränkung betrifft Linux, in der Erwägung, dass BSD/macOS-Versionen sind nicht betroffen, aufgrund der grundsätzlich nicht unterstützt jede\
-Präfix regex-Konstrukte:Das problem:
Tipp der Hut, um Fólkvangr für seine Ideen.
Eine wörtliche RHS von
=~
ist von design analysiert anders als nicht börsennotierte Token als argument, in einer versuchen, dem Benutzer zu ermöglichen, den Fokus auf escaping von Zeichen nur für die regex, ohne auch sorgen über die üblichen shell Flucht-Vorschriften in nicht börsennotierten Token.Beispielsweise
entspricht, weil die
\
ist _passed durch die regex-engine (das ist der regex-engine sieht wörtlichea\[b
), in der Erwägung, dass, wenn Sie das gleiche nicht börsennotierten token als eine reguläre argument, die üblichen shell-Erweiterungen angewendet, um nicht börsennotierte Token würde "Essen" die\
, denn es wird interpretiert als ein shell escape-Zeichen:Jedoch im Rahmen der
=~
dieses außergewöhnliche Durchreise von\
wird nur angewendet, bevor die Zeichen werden in regex Metazeichen selbst, wie definiert durch die ERE (extended regular expressions), die POSIX-Spezifikation (um Ihnen zu entkommen für die regex -, so dass Sie behandelt sind wie Literale:\ ^ $ [ { . ? * + ( ) |
Umgekehrt diese regex-Metazeichen kann ausnahmsweise verwendet werden nicht börsennotierten - und in der Tat muss gelassen werden ohne Anführungszeichen zu haben, die Ihre Besondere regex Bedeutung - obwohl die meisten von Ihnen erfordern in der Regel
\
-Flucht in nicht börsennotierten tokens zu verhindern, dass die shell von Sie zu interpretieren.Noch ein Teilmenge der shell Metazeichen tun müssen noch die Flucht, für die shell's sake, um nicht zu brechen die syntax der
[[ ... ]]
bedingt:& ; < > space
Da diese Charaktere nicht auch regex Metazeichen, besteht keine Notwendigkeit zu unterstützen und Ihnen zu entkommen, die auf den regex-Seite, so dass, zum Beispiel, die regex-engine zu sehen
\&
in der RHS als nur&
funktioniert einwandfrei.Für alle anderen Zeichen vorangestellt
\
die shell entfernt die\
vor dem senden die Zeichenfolge an die regex-engine (wie bei normalen shell-Erweiterung), was bedauerlich ist, denn dann auch Zeichen, die die shell nicht betrachten besonderen können nicht übergeben werden, als\<char>
an die regex-engine, weil die Schale immer übergibt Sie als nur<char>
.E. g,
\b
wird immer gesehen als nurb
durch die regex-engine.Deshalb ist es derzeit unmöglich, eine (per definition nicht-POSIX -) regex-Konstrukt in der form
\<char>
(z.B.,\<
,\>
,\b
,\s
,\w
,\d
, ...) in einem buchstäblichen, nicht börsennotierten=~
RHS, weil keine form der Flucht können Sie sicherstellen, dass diese Konstrukte gesehen werden, die durch die regex engine als solche, nach der Analyse durch die shell:Da weder
<
,>
nochb
sind regex Metazeichen der shell entfernt die\
aus\<
,\>
,\b
(wie geschieht in regelmäßigen shell-expansion). Also, vorbei\<word\>
, zum Beispiel, macht die regex-engine finden Sie unter<word>
, das ist nicht die Absicht:[[ '<word>' =~ \<word\> ]] && echo YES
entspricht, da die regex-engine sieht<word>
.[[ 'boo' =~ ^\boo ]] && echo YES
entspricht, da die regex-engine sieht^boo
.Versuchen
\\<word\\>
bricht den Befehl, weil die shell behandelt jeden\\
als ein entflohener\
, was bedeutet, dass Metazeichen<
ist dann als nicht börsennotierten, was zu einer syntax-Fehler:[[ ' word ' =~ \\<word\\> ]] && echo YES
verursacht einen Syntaxfehler.\\b
, aber\\b
ist Durchlaufen (aufgrund der\
vor einer regex metachar,\
), die auch nicht funktioniert:[[ '\boo' =~ ^\\boo ]] && echo YES
entspricht, da die regex-engine sieht\\boo
, was mit wörtlichen\boo
.Versuchen
\\\<word\\\>
- die durch die normalen shell-Erweiterung Regeln Ergebnisse in\<word\>
(versuchenprintf %s \\\<word\\\>
) - auch nicht funktioniert:Was passiert, ist, dass die shell isst die
\
im\<
(dito für\b
und andere\
-Präfix-Sequenzen), und dann geht der vorhergehenden\\
durch die regex-engine ist (wieder, weil\
beibehalten wird, bevor ein regex metachar):[[ ' \<word\> ' =~ \\\<word\\\> ]] && echo YES
entspricht, da die regex-engine sieht\\<word\\>
, was mit wörtlichen\<word\>
.Kurz:
Bash beim Parsen von
=~
RHS Literale wurde mit single-Charakter regex-Metazeichen im Sinn und unterstützt nicht multi-Charakter Konstrukte, die beginnen mit\
wie\<
.Weil POSIX-EREs-Unterstützung keine solchen Konstrukte,
=~
wie vorgesehen funktioniert, wenn Sie sich beschränken auf solche regexes.Aber selbst innerhalb dieser Einschränkung für das design ist etwas umständlich, aufgrund der Notwendigkeit der mix-regex-und-shell-bezogenen
\
-Flucht (Zitat).Fólkvangr fand die offizielle design Begründung in der Bash-FAQ hier, die jedoch weder Adressen, sagte Unbeholfenheit noch die fehlende Unterstützung für die (immer nicht-POSIX -)
\<char>
regex-Konstrukte; Sie erwähnt, dass mithilfe eines aux. variable als workaround, allerdings nur in Bezug auf so dass es leichter zu vertreten Leerzeichen.All diese parsing-Probleme gehen Weg, wenn die Zeichenfolge, die die regex-engine sehen sollten, ist vorgesehen, über eine variable oder über die Ausgabe von einer Substitutionen, wie oben gezeigt.
Optional zu Lesen: Ein Tragbarer emulation von word-boundary Behauptungen mit POSIX-konformen EREs (extended regular expressions):
(^|[^[:alpha:][:digit:]_])
statt\<
/[[:<:]]
([^[:alpha:][:digit:]_]|$)
statt\>
/[[:>:]]
Hinweis:
\b
können nicht emuliert werden, mit einem EINZIGEN Ausdruck - verwenden Sie die oben an den entsprechenden stellen.Den potenziellen Nachteil ist, dass die oben genannten Ausdrücke werden auch erfassen das nicht-Wort-Zeichen übereinstimmen, in der Erwägung, dass wahre Behauptungen wie
\<
/[[:<:]]
und nicht.Den oben genannten Spiele, wie erwartet.
Akzeptierte Antwort konzentriert sich auf die Verwendung Hilfs-Variablen zu tun mit der syntax Merkwürdigkeiten von regular expressions in der Bash ist
[[ ... ]]
Ausdrücken. Sehr gute info.Jedoch die richtige Antwort ist:
\b
\<
und\>
funktionieren nicht auf OS X 10.11.5 (El Capitan) mit der bash-version 4.3.42(1)-release (x86_64-apple-darwin15.0.0).Verwenden Sie stattdessen
[[:<:]]
und[[:>:]]
.\
-Präfix Konstrukte sind grundsätzlich nicht verfügbar, da (und das[[:<:]]
und[[:>:]]
alternativen gibt es in einem unquoted=~
RHS, Sie müssen fliehen, Sie als[[:\<:]]
und[[:\>:]]
). Auf Linux eine Bash-design-Fehler verhindert die Nutzung der zur Verfügung\
-Präfix-Konstrukte (der Notwendigkeit) nicht börsennotierte=~
RHS Literale und akzeptiert die Antwort und zeigen andere wirksame Abhilfen.Nicht genau, "\b", aber für mich besser lesbar (und portable) als die anderen Vorschläge:
Habe ich die folgenden zu Wort Grenzen auf älteren Systemen. Der Schlüssel ist, um wickeln
$foo
mit Leerzeichen, da[^[:alpha:]]
nicht entsprechen, werden Wörter am Anfang oder Ende der Liste.Zwicken die Charakter-Klasse, wie erforderlich, basierend auf den zu erwartenden Inhalt
myword
werden, ansonsten kann keine gute Lösung.Tangential auf deine Frage, aber wenn Sie verwenden können
grep -E
(oderegrep
seine effektive, aber veraltete alias) in Ihrem Skript:Landete ich mit diesem nach flailing mit der bash ist
=~
.Beachten Sie, dass während der regex-Konstrukte
\<
,\>
, und\b
sind nicht POSIX-konform, sowohl die BSD (macOS) und GNU (Linux) Implementierungen vongrep -E
unterstützen, die diesen Ansatz weit nutzbar in der Praxis.Kleine Einschränkung (kein Problem in dem Fall bei der hand): nicht mit
=~
, verlieren Sie die Fähigkeit zu inspizieren Erfassung Teilausdrücke (capture-Gruppen) über${BASH_REMATCH[@]}
später.Dieser arbeitete für mich
bar='\<myword\>'
undbar='\bmyword\b'
(oder ohne Anführungszeichen, als in der akzeptierten Antwort,bar=\\bmyword\\b
) arbeiten.Können Sie mit grep, das ist mehr tragbar als bash den regexp so:
grep
selbst ist tragbar, aber\<
als grep-Funktion wird nicht - es sei denn, Sie zeigen können, wo der POSIX-Spezifikation definiert es? Ich sehe keine Verweise in pubs.opengroup.org/onlinepubs/9699919799/basedefs/... oder pubs.opengroup.org/onlinepubs/9699919799.2008edition/utilities/...echo $foo
stellt seine eigenen Fehler; siehe BashPitfalls #14).\<
,\>
und\b
nicht Bestandteil von POSIX, sondern auch die BSD (macOS) und GNUgrep
(Linux) - Implementierungen zu unterstützen, die macht dieser Ansatz allgemein anwendbar in der Praxis.echo $foo
(ohne Anführungszeichen variable Referenz), dass Karl der Punkte aus, beachten Sie, dassgrep
benutzt standardmäßig nur basic, reguläre Ausdrücke (- Fasern), in der Erwägung, dass=~
verwendet erweitert regular expressions (EREs); um diegrep
- Befehl funktioniert mehr wie die letztgenannten, verwenden Siegrep -E -q ...
- aber dann müssen Sie dupliziert diesen bereits vorhandenen Antwort.