Regulärer Ausdruck mit \\ vs mit \

10

Warum tut

grep e\\.g\\. <<< "this is an e.g. wow"

und

grep e\.g\. <<< "this is an e.g. wow"

mach das selbe?

Wenn ich einen dritten Schrägstrich hinzufüge, hat dies auch das gleiche Ergebnis. ABER sobald ich einen vierten Schrägstrich hinzufüge, funktioniert es nicht mehr. Dies hat mit einer Frage aus einer alten Prüfung für eine Klasse zu tun. Es wurde gefragt, ob der mit zwei Backslashes funktionieren würde, um die Zeile mit "zB" auszugeben. Ich dachte ursprünglich, dass es nicht funktionieren würde, aber ich versuchte es sicherzustellen und es tat es. Was ist die Erklärung?

Wyatt Grant
quelle
Ich hatte gedacht, Bash würde Grep nehmen \\\.und geben, \.aber das tut es nicht. gute Frage

Antworten:

9

Beachten Sie zunächst, dass der einzelne Schrägstrich zu stark übereinstimmt:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

Für Bash ist eine entkommene Periode dieselbe wie eine Periode. Bash gibt die Periode an grep weiter . Für grep passt ein Punkt zu allem.

Betrachten Sie nun:

$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$

Wenn Bash einen doppelten Schrägstrich sieht, reduziert er ihn auf einen einzelnen Schrägstrich und gibt diesen an grep weiter, der im ersten der drei obigen Tests, wie wir wollen, einen einzelnen Schrägstrich vor einem Punkt sieht. Das macht also das Richtige.

Mit einem dreifachen Schrägstrich reduziert Bash die ersten beiden auf einen einzigen Schrägstrich. Es sieht dann \.. Da eine entkommene Periode für Bash keine besondere Bedeutung hat, wird dies auf eine einfache Periode reduziert. Das Ergebnis ist, dass grep, wie wir wollen, vor einer Periode einen Schrägstrich sieht.

Mit vier Schrägstrichen reduziert Bash jedes Paar auf einen einzigen Schrägstrich. Bash gibt zwei Schrägstriche und einen Punkt weiter. grep sieht die zwei Schrägstriche und einen Punkt und reduziert die beiden Schrägstriche auf einen einzigen wörtlichen Schrägstrich. Sofern die Eingabe keinen wörtlichen Schrägstrich gefolgt von einem Zeichen enthält, gibt es keine Übereinstimmungen.

Denken Sie zur Veranschaulichung daran, dass in einfachen Anführungszeichen alle Zeichen wörtlich sind. In Anbetracht der folgenden drei Eingabezeilen stimmt der Befehl grep nur in der Zeile mit dem wörtlichen Schrägstrich in der Eingabe überein:

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\\\.g\\\\.
e\.g\.

Zusammenfassung von Bashs Verhalten

Für Bash gelten die Regeln

  • Zwei Schrägstriche werden auf einen einzigen Schrägstrich reduziert.

  • Ein Schrägstrich vor einem normalen Zeichen ist wie ein Punkt nur das normale Zeichen (Punkt).

Somit:

$ echo \. \\. \\\. \\\\.
. \. \. \\.

Es gibt eine einfache Möglichkeit, all diese Verwirrung zu vermeiden: In der Bash-Befehlszeile sollten reguläre Ausdrücke in einfache Anführungszeichen gesetzt werden. In einfachen Anführungszeichen lässt Bash alles in Ruhe.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
quelle
Frage: Es dauert zwei Backslashes, bis Bash es als Backslash anzeigt (einer ist die Escape-Sequenz, der andere ist der wörtliche Backslash). Wenn es also 3 gibt, behandelt Bash den dritten Straggler auch als Fluchtsequenz? Wird es dann verworfen, da es nichts entgeht?
Franz Kafka
@DanielAmaya Der dritte wird als Flucht für den folgenden Charakter behandelt. In unserem Fall ist dieses Zeichen die Periode, und für bash (im Gegensatz zu grep) ist eine entkommene Periode nur eine einfache Periode. bash gibt dann die einfache Periode an grep weiter.
John1024
@DanielAmaya In der aktualisierten Antwort finden Sie eine echoErklärung, die zeigt, was Bash in diesen Fällen bewirkt.
John1024
2
@DanielAmaya In beiden Fällen reduziert Bash die ersten beiden Schrägstriche auf einen einzigen Schrägstrich. Was bleibt ist \.oder .. Für Bash sind beide gleich: Sie entsprechen einer einfachen Periode. Insgesamt ist das, was Bash an Grep liefert, für beide gleich: ein einzelner Schrägstrich, gefolgt von einem Punkt.
John1024
1
Nur eine kleine Ergänzung - die Verwendung echoist aufgrund der vielen Implementierungen dieses Programms keine sehr zuverlässige Methode, um Regexp zu testen. Zum Beispiel unter meinem zsh (eingebautes Echo) echo \. \\. \\\. \\\\. \\\\\.gibt . \. \. \. \., aber /bin/echo \. \\. \\\. \\\\. \\\\\.zurück . \. \. \\. \\.. So etwas printf "%s" ...ist wahrscheinlich der bessere Weg.
Jimmy
4

Die Ausgabe ist nur für Ihre Zeichenfolge gleich, aber im Allgemeinen bewirken diese regulären Ausdrücke unterschiedliche Funktionen. Lassen Sie uns Ihr Beispiel ein wenig ändern, indem Sie ein zweites Muster e,g,(mit Koma), ein drittes e\.g\.(Punkte), ein viertes e\,g\,(Koma) und eine Grep- -oOption hinzufügen, um nur übereinstimmende Teile zu drucken.

  • Im folgenden Fall .passen alle Zeichen (Ankündigung ''um e.g., werde ich dazu kommen später)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Als nächstes entkommen wir .mit einem Backslash \, sodass nur das Literal .übereinstimmt:

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Aber wir können \mit einem anderen entkommen \, so dass das Literal \übereinstimmt, gefolgt von .(dh einem beliebigen Zeichen):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Aber wenn wir nur \.nicht übereinstimmen wollen, wird \,noch eine weitere \benötigt, um der besonderen Bedeutung des Punktes zu entgehen:

    $ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.

Da Sie das ''Argument grep nicht verwendet haben , müssen Sie weitere Backslashes hinzufügen, um Backslashes bei der Shell-Interpretation zu vermeiden.

grep 'e\.g\.'     => grep e\\.g\\.
grep 'e\\.g\\.'   => grep e\\\\.g\\\\.  (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
jimmij
quelle
3

Wenn Sie a grep e\.g\.ausführen, verbraucht die Shell den Backslash, also machen Sie a grep e.g., was übereinstimmt. Wenn Sie grep e\\.g\\.a ausführen, verbraucht die Shell erneut einen Schrägstrich, und jetzt führen Sie einen aus grep e\.\g., der wieder übereinstimmt. Nun sieht ein Backslash zur Shell aus \\. Wenn Sie also haben \\, ist die erste eine Escape-Sequenz, die zweite ein wörtlicher Backslash. Wenn Sie a ausführen grep e\\\.g\\\., ist dies immer noch der Fall grep e\.\g., da \vor der ersten keine Escape-Sequenz ( ) steht \, die es zu einem Literal macht \. Denken Sie daran, \ ist ein Backslash und grep e\\\\.\\\\gendet damit grep e\\.g\\., was offensichtlich nicht übereinstimmt.

Verwenden Sie echo (z. B. echo grep e\\.g\\. <<< "this is an e.g. wow"vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow") , um zu sehen, wie die Shell sieht, was Sie tun.

Franz Kafka
quelle
0

Die beiden Befehle erzeugen nur für Ihre Eingabe dieselbe Ausgabe, ansonsten sind sie jedoch unterschiedlich. Um zu verstehen, was vor sich geht, müssen wir wissen, wie der Parameter zuerst von bashund dann von interpretiert wird grep.

Flucht in Bash

\ist ein Sonderzeichen, das die Sonderbedeutung des folgenden Zeichens einschließlich sich \selbst aufhebt . Wenn das folgende Zeichen keine besondere Bedeutung hat, wird es unverändert übergeben. Beispiele mit Befehl und Ergebnis:

  • echo \a: a- gewöhnliches entkommenes Zeichen gibt das Zeichen
  • echo \\: \- Sonderzeichen entkommen gibt den Charakter
  • echo \\\a: \a- Kombination speziell, gewöhnlich
  • echo \\\\: \\- Kombination speziell, speziell

echodruckt die resultierende Zeichenfolge, nachdem sie bashinterpretiert wurde. Weitere Informationen: Bash-Dokumentation , Bash-Hacker-Wiki , POSIX-Spezifikation .

.hat keine besondere Bedeutung in bash. Es ist ein gewöhnlicher Charakter für die Schale. Nachfolgend sind die für Ihre Beispiele relevanten Sequenzen aufgeführt:

  • echo .:: .
  • echo \.:: .
  • echo \\.:: \.
  • echo \\\.:: \.
  • echo \\\\.:: \\.

Einfachere Lösung für wörtliche Zeichenfolgen in Bash

Um Parameter buchstäblich zu übergeben bash, können Sie das einfache Anführungszeichen verwenden '. Zwischen einfachen Anführungszeichen müssen Sie sich nicht um die besondere Bedeutung von Zeichen kümmern, da einfache Anführungszeichen das einzige Zeichen mit einer besonderen Bedeutung sind. Sie können ein einfaches Anführungszeichen einfügen, nachdem Sie den ersten Teil der Zeichenfolge eingeschlossen haben. Beispiel:
echo 'part1'\''part2': part1'part2

Regex in grep

\ist ein Fluchtzeichen mit ähnlicher Bedeutung wie in bash. .ist ein Sonderzeichen, das ein einzelnes Vorkommen eines Zeichens darstellt . Siehe: POSIX-Regex , GNU-Grep-Regex . Beispiele für Regex-Ausdrücke:

  • .- Entspricht einem beliebigen Zeichen wie aoder.
  • \.- passt nur .wörtlich

Ihre Beispiele

In der zweiten Zeile jedes Beispiel unter Ihnen gleichwertig mit einfachen Anführungszeichen werden feststellen , die 'zeigt , welche Zeichenkette durch geführt wird bashzu grep. Nach dem grepEscape-Vorgang stimmt das einzig mögliche Sonderzeichen in den Beispielen .mit einem beliebigen Zeichen überein. In der dritten Zeile finden Sie eine Beschreibung, mit der der Ausdruck übereinstimmt.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    eBeliebiges Zeichen gBeliebiges Zeichen - Übereinstimmungen e.g.und möglicherweise andere Zeichenfolgen wieeagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    eBeliebiges Zeichen gBeliebiges Zeichen - Übereinstimmungen e.g.und möglicherweise andere Zeichenfolgen wieexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.buchstäblich - nur Übereinstimmungene.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.buchstäblich - nur Übereinstimmungene.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\irgendein Charakter g\irgendein Charakter - stimmt nicht übereine.g.
Pabouk
quelle