Espressione regolare utilizzando \\ vs using \


10

Perché lo fa

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

e

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

fare la stessa cosa?

Se aggiungo una terza barra, ha anche lo stesso risultato. MA, una volta aggiunta una quarta barra, non funziona più. Questo ha a che fare con una domanda di un vecchio esame per una lezione. Ha chiesto se quello con due barre rovesciate avrebbe funzionato per produrre la linea con "es." Inizialmente pensavo che non avrebbe funzionato, ma ho cercato di assicurarmene e lo ha fatto. Qual è la spiegazione?


Pensavo che Bash avrebbe preso \\\.e dato grep, \.ma non è così. bella domanda

Risposte:


9

Innanzitutto, nota che la singola barra corrisponde troppo:

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

Per quanto riguarda Bash , un periodo di fuga è uguale a un periodo. Bash passa il periodo a grep . Per grep, un punto corrisponde a qualsiasi cosa.

Ora, considera:

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

Quando Bash vede una doppia barra, la riduce in una singola barra e la passa su grep che, nella prima delle tre prove precedenti, vede, come vogliamo, una singola barra prima di un punto. Quindi, questo fa la cosa giusta.

Con una tripla barra, Bash riduce i primi due a una singola barra. Quindi vede \.. Poiché un periodo di escape non ha un significato speciale per Bash, questo è ridotto a un periodo normale. Il risultato è che grep vede, come vogliamo, una barra prima di un punto.

Con quattro barre, Bash riduce ciascuna coppia in una singola barra. Bash passa a grep due barre e un punto. grep vede le due barre e un punto e riduce le due barre a una singola barra letterale . A meno che l'input non abbia una barra letterale seguita da qualsiasi carattere, non ci sono corrispondenze.

Per illustrare quest'ultimo, ricorda che all'interno delle virgolette singole, tutti i caratteri sono letterali. Pertanto, date le seguenti tre righe di input, il comando grep corrisponde solo sulla riga con la barra letterale nell'input:

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

Riepilogo del comportamento di Bash

Per Bash, le regole sono

  • Due barre vengono ridotte a una singola barra.

  • Una barra davanti a un personaggio normale, come un punto, è solo il personaggio normale (punto).

Così:

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

C'è un modo semplice per evitare tutta questa confusione: sulla riga di comando di Bash, le espressioni regolari dovrebbero essere racchiuse tra virgolette singole. All'interno di virgolette singole, Bash lascia tutto da solo.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.

Domanda: Bastano due barre rovesciate per bash per vederla come una barra rovesciata (una è la sequenza di fuga, l'altra è la barra rovesciata letterale). Quindi, quando ci sono 3, bash considera anche il terzo sbandato come una sequenza di escape? Dal momento che non sfugge a nulla, viene quindi scartato?
Franz Kafka,

@DanielAmaya Il terzo è trattato come una fuga per il personaggio che segue. Nel nostro caso, quel personaggio è il periodo e, per bash (a differenza di grep), un periodo di escape è solo un periodo semplice. bash quindi passa il periodo normale su grep.
Giovanni 1024

@DanielAmaya Vedi la risposta aggiornata per echoun'affermazione che illustra cosa fa bash in questi casi.
Giovanni 1024

2
@DanielAmaya In entrambi i casi, bash riduce le prime due barre in una singola barra. Ciò che rimane è \.o .. Per bash, entrambi sono uguali: equivalgono a un periodo normale. Quindi, in totale, ciò che bash offre a grep è lo stesso per entrambi: una singola barra seguita da un punto.
Giovanni 1024

1
Solo una piccola aggiunta - l'utilizzo echonon è un modo molto affidabile per testare regexp a causa della molte implementazioni di questo programma. Ad esempio sotto my zsh (built-in echo) echo \. \\. \\\. \\\\. \\\\\.. \. \. \. \., ma /bin/echo \. \\. \\\. \\\\. \\\\\.restituisce . \. \. \\. \\.. Qualcosa del genere printf "%s" ...è probabilmente il modo migliore.
Jimmij,

4

L'output è lo stesso solo per la tua stringa, ma in generale quelle espressioni regolari fanno cose diverse. Modifichiamo un po 'il tuo esempio aggiungendo un secondo motivo e,g,(con virgole), un terzo e\.g\.(punti), un quarto e\,g\,(virgole) e -oun'opzione per grep per stampare solo le parti corrispondenti.

  • Nel caso seguente .abbinare qualsiasi carattere (notare in ''giro e.g., ci tornerò più avanti)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Quindi scappiamo .con la barra rovesciata \, quindi .verrà abbinato solo il letterale :

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Ma possiamo scappare \con un altro \, in modo che il letterale \sia abbinato seguito da .(cioè qualsiasi carattere):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Ma se vogliamo abbinare solo \.non è necessario \,ancora un altro \per sfuggire al significato speciale del punto:

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

Ora, poiché non hai usato l' ''argomento grep, devi aggiungere un'altra barra rovesciata per sfuggire alle barre rovesciate dall'interpretazione della shell, quindi:

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)

3

Quando fai un grep e\.g\., la shell consuma la barra rovesciata, quindi stai facendo un grep e.g., che corrisponde. Quando fai un grep e\\.g\\., la shell sta di nuovo consumando una barra, e ora stai facendo un grep e\.\g., che corrisponde di nuovo. Ora sembra una barra rovesciata alla shell \\. Quindi, quando hai \\, il primo è una sequenza di escape, il secondo è una barra rovesciata letterale. Quando fai una grep e\\\.g\\\., finisce per essere grep e\.\g., perché non c'è una sequenza di escape ( \) prima della prima \per renderla letterale \. Tieni presente che \ è una barra rovesciata, quindi grep e\\\\.\\\\gfinisce per essere grep e\\.g\\., che ovviamente non corrisponde.

Per vedere come la shell sta vedendo quello che stai facendo, usa echo (ad es. echo grep e\\.g\\. <<< "this is an e.g. wow"Vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow")


0

I due comandi producono lo stesso output solo per il tuo input, ma per il resto sono diversi. Per capire cosa sta succedendo, dobbiamo sapere come viene prima interpretato il parametro bashe poi da grep.

Scappare a bash

\è un carattere speciale che annulla il significato speciale del personaggio seguente incluso \se stesso. Se il seguente carattere non ha alcun significato speciale, viene passato senza modifiche. Esempi con comando e un risultato:

  • echo \a: a- il carattere ordinario evaso fornisce il personaggio
  • echo \\: \- il carattere speciale evaso fornisce il personaggio
  • echo \\\a: \a- combinazione speciale, ordinaria
  • echo \\\\: \\- combinazione speciale, speciale

echostamperà la stringa risultante dopo bashaverla interpretata. Per maggiori informazioni: documentazione bash , gli hacker bash wiki , specifiche POSIX .

.non ha alcun significato speciale in bash. È un personaggio ordinario per la shell. Di seguito sono riportate le sequenze pertinenti ai tuoi esempi:

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

Soluzione più semplice per stringhe letterali in bash

Per passare letteralmente i parametri bashpuoi usare la singola virgoletta di 'escape. Tra virgolette singole non devi preoccuparti del significato speciale dei personaggi perché la virgoletta singola è l'unico personaggio con un significato speciale lì. È possibile inserire una singola virgoletta dopo aver racchiuso la prima parte della stringa. Esempio
echo 'part1'\''part2':: part1'part2

Regex in grep

\è un personaggio di fuga con un significato simile a quello di bash. .è un personaggio speciale che rappresenta una singola occorrenza di qualsiasi personaggio . Vedi: POSIX regex , GNU grep regex . Esempi di espressioni regex:

  • .- corrisponde a qualsiasi personaggio come ao.
  • \.- corrisponde solo .letteralmente

I tuoi esempi

Sulla seconda riga di ogni esempio di seguito troverete equivalente con le citazioni singolo 'che mostra quali stringa letterale viene passato per basha grep. Quindi, dopo aver grepeseguito la fuga, l'unico carattere speciale possibile negli esempi .corrisponde a qualsiasi carattere. Sulla terza riga c'è una descrizione a cui corrisponde l'espressione.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    equalsiasi carattere gqualsiasi carattere - corrispondenze e.g.e possibilmente altre stringhe comeeagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    equalsiasi carattere gqualsiasi carattere - corrispondenze e.g.e possibilmente altre stringhe comeexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.letteralmente - solo partitee.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.letteralmente - solo partitee.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\qualsiasi personaggio g\qualsiasi personaggio - non corrispondee.g.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.