Wyrażenie regularne przy użyciu \\ vs użycie \

10

Dlaczego

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

i

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

Zrobić to samo?

Jeśli dodam trzeci ukośnik, ma również ten sam wynik. ALE, kiedy dodam czwarty ukośnik, to już nie działa. Ma to związek z pytaniem ze starego egzaminu na klasę. Zapytał, czy ten z dwoma odwrotnymi ukośnikami będzie działał, aby wypisać wiersz z „np.” Początkowo myślałem, że to nie zadziała, ale próbowałem się upewnić i udało się. Jakie jest wyjaśnienie?

Wyatt Grant
źródło
Myślałem, że bash zajmie \\\.i da grep, \.ale tak nie jest. dobre pytanie

Odpowiedzi:

9

Po pierwsze, zwróć uwagę, że pojedynczy ukośnik pasuje zbyt wiele:

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

Jeśli chodzi o Basha , okres ucieczki jest taki sam jak okres. Bash przechodzi na okres grep . Dla grep kropka pasuje do wszystkiego.

Teraz rozważ:

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

Gdy Bash widzi podwójny ukośnik, redukuje go do pojedynczego ukośnika i przekazuje go na grep, który w pierwszym z trzech powyższych testów widzi, jak chcemy, pojedynczy ukośnik przed kropką. Tak więc robi to właściwą rzecz.

Dzięki potrójnemu ukośnikowi Bash redukuje pierwsze dwa do jednego ukośnika. Potem widzi \.. Ponieważ okres ucieczki nie ma specjalnego znaczenia dla Bash, jest on redukowany do zwykłego okresu. W rezultacie grep widzi, jak chcemy, cięcie przed kropką.

Z czterema ukośnikami Bash redukuje każdą parę do jednego ukośnika. Bash przechodzi na grep dwa ukośniki i kropkę. grep widzi dwa ukośniki i kropkę i redukuje dwa ukośniki do jednego dosłownego ukośnika. O ile na wejściu nie ma dosłownego ukośnika, po którym następuje dowolny znak, nie ma żadnych dopasowań.

Aby zilustrować to ostatnie, pamiętaj, że w pojedynczych cudzysłowach wszystkie znaki są dosłowne. Tak więc, biorąc pod uwagę następujące trzy wiersze wejściowe, polecenie grep pasuje tylko do wiersza z literalnym ukośnikiem na wejściu:

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

Podsumowanie zachowania Basha

W przypadku Bash obowiązują następujące zasady

  • Dwa ukośniki są zredukowane do jednego ukośnika.

  • Cięcie przed normalnym znakiem, takim jak kropka, jest po prostu normalnym znakiem (kropka).

A zatem:

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

Istnieje prosty sposób na uniknięcie tego zamieszania: w wierszu poleceń Bash wyrażenia regularne należy umieszczać w cudzysłowach. Wewnątrz pojedynczych cytatów Bash pozostawia wszystko w spokoju.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
źródło
Pytanie: Bash wymaga dwóch odwrotnych ukośników, aby zobaczyć go jako odwrotny ukośnik (jeden to sekwencja ucieczki, drugi to dosłowny odwrotny ukośnik). Więc kiedy są 3, bash traktuje trzeciego marudera również jako sekwencję ucieczki? Skoro nic nie ucieka, to czy należy go odrzucić?
Franz Kafka,
@DanielAmaya Trzeci jest traktowany jako ucieczka dla następnej postaci. W naszym przypadku tą postacią jest kropka, a dla bash (w przeciwieństwie do grep) kropka uciekająca jest po prostu kropką. bash następnie przekazuje zwykły okres do grep.
John1024,
@DanielAmaya Zobacz zaktualizowaną odpowiedź na echooświadczenie, które ilustruje, co robi bash w tych przypadkach.
John1024,
2
@DanielAmaya W obu przypadkach bash redukuje Pierwsze dwa ukośniki do jednego ukośnika. Pozostaje to \.lub .. W przypadku bash oba są takie same: są równoważne zwykłemu okresowi. Dlatego w sumie to, co bash dostarcza grep jest takie samo dla obu: pojedynczy slash, po którym następuje kropka.
John1024,
1
Tylko mały dodatek - użycie echonie jest bardzo niezawodnym sposobem na testowanie wyrażeń regularnych z powodu wielu implementacji tego programu. Na przykład pod moim zsh (wbudowane echo) echo \. \\. \\\. \\\\. \\\\\.daje . \. \. \. \., ale /bin/echo \. \\. \\\. \\\\. \\\\\.zwraca . \. \. \\. \\.. Coś takiego printf "%s" ...jest prawdopodobnie lepszym sposobem.
jimmij
4

Dane wyjściowe są takie same tylko dla łańcucha, ale ogólnie te wyrażenia regularne robią różne rzeczy. Zmodyfikujmy trochę twój przykład, dodając drugi wzór e,g,(z przecinkami), trzeci e\.g\.(kropki), czwarty e\,g\,(przecinki) i -oopcję grep, aby wydrukować tylko pasujące części.

  • W poniższym przypadku .znaleziono żadnych char (zawiadomienie ''dookoła e.g., wejdę do tego później)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Następnie uciekamy .z ukośnikiem odwrotnym \, więc .dopasowany zostanie tylko literał :

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Ale możemy uciec \z innym \, tak że dosłowność \zostanie dopasowana, a następnie .(tj. Dowolny znak):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Ale jeśli chcemy dopasować tylko \.nie, potrzebujemy \,jeszcze jednego \, aby uniknąć specjalnego znaczenia kropki:

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

Ponieważ nie ''używałeś argumentu grep, musisz dodać kolejne odwrotne ukośniki, aby uniknąć odwrotnych ukośników przed interpretacją powłoki, więc:

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
źródło
3

Kiedy robisz a grep e\.g\., powłoka zużywa odwrotny ukośnik, więc robisz to grep e.g., co pasuje. Kiedy robisz a grep e\\.g\\., powłoka znów pochłania ukośnik, a teraz robisz to grep e\.\g., co znów pasuje. Teraz wygląda odwrotny ukośnik do powłoki \\. Tak więc, gdy masz \\, pierwszy to sekwencja ucieczki, drugi to dosłowny odwrotny ukośnik. Kiedy zrobisz a grep e\\\.g\\\., to wciąż kończy się grep e\.\g., ponieważ nie ma sekwencji ucieczki ( \) przed pierwszą, \aby uczynić ją dosłowną \. Należy pamiętać, że \ jest odwrotnym ukośnikiem, więc grep e\\\\.\\\\gostatecznie jest grep e\\.g\\., co oczywiście nie pasuje.

Aby zobaczyć, jak powłoka widzi to, co robisz, użyj echa (np. echo grep e\\.g\\. <<< "this is an e.g. wow"Vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow")

Franz Kafka
źródło
0

Oba polecenia generują takie same dane wyjściowe tylko dla danych wejściowych, ale poza tym są różne. Aby zrozumieć, co się dzieje, musimy wiedzieć, w jaki sposób parametr jest interpretowany najpierw, basha następnie przez grep.

Ucieczka przed uderzeniem

\jest znakiem specjalnym, który anuluje specjalne znaczenie następującego znaku, w tym \samego siebie. Jeśli poniższy znak nie ma specjalnego znaczenia, jest przekazywany bez zmian. Przykłady z poleceniem i wynikiem:

  • echo \a: a- zwykły znak uciekł daje postać
  • echo \\: \- Specjalny znak uciekł daje postać
  • echo \\\a: \a- kombinacja specjalna, zwykła
  • echo \\\\: \\- kombinacja specjalna, specjalna

echowypisze powstały ciąg po bashinterpretacji. Więcej informacji: Dokumentacja bash , hakerzy bash wiki , specyfikacji POSIX .

.nie ma specjalnego znaczenia w bash. Jest to zwykła postać dla powłoki. Poniżej znajdują się sekwencje istotne dla twoich przykładów:

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

Prostsze rozwiązanie dla dosłownych ciągów znaków w bash

Aby przekazać parametry dosłownie bash, możesz użyć pojedynczego cudzysłowu '. Pomiędzy pojedynczymi cudzysłowami nie musisz przejmować się specjalnym znaczeniem znaków, ponieważ pojedynczy cudzysłów jest jedynym znakiem o specjalnym znaczeniu. Możesz wstawić pojedynczy cytat po dołączeniu pierwszej części ciągu. Przykład
echo 'part1'\''part2': part1'part2

Regex w grep

\jest znakiem ucieczki o podobnym znaczeniu jak w bash. .jest znakiem specjalnym, który reprezentuje pojedyncze wystąpienie dowolnego znaku . Patrz: POSIX regex , GNU grep regex . Przykłady wyrażeń regularnych:

  • .- pasuje do dowolnej postaci, takiej jak alub.
  • \.- pasuje tylko .dosłownie

Twoje przykłady

W drugiej linii każdego przykładu poniżej znajdziesz równoznaczne z apostrofami 'pokazano ciągiem znaków, który jest przekazywany przez bashsię grep. Następnie po grepwykonaniu zmiany jedynym możliwym znakiem specjalnym w przykładach jest .dopasowanie dowolnego znaku. W trzecim wierszu znajduje się opis pasujący do wyrażenia.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    edowolny znak gdowolny znak - dopasowania e.g.i ewentualnie inne ciągi, takie jakeagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    edowolny znak gdowolny znak - dopasowania e.g.i ewentualnie inne ciągi, takie jakexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.dosłownie - tylko dopasowaniae.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.dosłownie - tylko dopasowaniae.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\dowolna postać g\dowolna postać - nie pasujee.g.
pabouk
źródło