Come o perché usare `. *?` È meglio di `. *`?


9

Ho risposto a questa domanda su SuperUser che era qualcosa correlato al tipo di espressioni regolari utilizzate durante il grepping di un output.

La risposta che ho dato è stata questa:

 tail -f log | grep "some_string.*some_string"

E poi, in tre commenti alla mia risposta, @Bob ha scritto questo:

.*è avido e potrebbe catturare più di quanto desideri. .*?di solito è meglio.

Poi questo,

il ?è un modificatore su *, rendendolo pigro al posto del default avidi. Supponendo PCRE.

Ho cercato su Google PCRE, ma non sono riuscito a capire qual è il significato di questo nella mia risposta?

e infine questo,

Vorrei anche sottolineare che si tratta di regex (grep che fa regex POSIX di default), non di shell glob.

So solo cos'è un Regex e un suo utilizzo molto semplice nel comando grep. Quindi, non sono riuscito a ottenere nessuno di quei 3 commenti e ho in mente queste domande:

  • Quali sono le differenze nell'uso di .*?vs. .*?
  • Quale è meglio e in quali circostanze? Si prega di fornire esempi.

Inoltre sarebbe utile capire i commenti, se qualcuno potesse


AGGIORNAMENTO: Come risposta alla domanda In cosa differiscono Regex dai Shell Globs? @Kusalananda ha fornito questo link nel suo commento.

NOTA: se necessario, leggere la mia risposta a questa domanda prima di rispondere per fare riferimento al contesto.


Queste sono due domande molto diverse. Alla prima domanda risponde unix.stackexchange.com/questions/57957/… mentre la seconda domanda dipende dall'applicazione del modello (non si può dire che sia "migliore" in tutte le circostanze).
Kusalananda

È possibile modificare questa domanda in modo che riguardi solo il problema .*vs. .*?La domanda "differenza tra espressioni regolari e globs di shell" è già stata affrontata su questo sito.
Kusalananda

Risposte:


7

Ashok ha già sottolineato la differenza tra .*e .*?, quindi fornirò solo alcune informazioni aggiuntive.

grep (supponendo la versione GNU) supporta 4 modi per abbinare le stringhe:

  • Stringhe fisse
  • Espressioni regolari di base (BRE)
  • Espressioni regolari estese (ERE)
  • Espressioni regolari compatibili con Perl (PCRE)

grep utilizza BRE per impostazione predefinita.

BRE ed ERE sono documentati nel capitolo Espressioni regolari di POSIX e PCRE è documentato nel suo sito Web ufficiale . Si noti che le funzionalità e la sintassi possono variare tra le implementazioni.

Vale la pena dire che né BRE né ERE supportano la pigrizia :

Il comportamento di più simboli di duplicazione adiacenti ('+', '*', '?' E intervalli) produce risultati indefiniti.

Quindi, se si desidera utilizzare quella funzione, è necessario utilizzare PCRE invece:

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

Modifica 1

Potresti spiegare un po 'di .*vs .*??

  • .*viene utilizzato per abbinare il modello "più lungo" 1 possibile.

  • .*?viene utilizzato per abbinare la "breve" 1 modello possibile.

Nella mia esperienza, il comportamento più desiderato è di solito il secondo.

Ad esempio, supponiamo di avere la seguente stringa e vogliamo solo abbinare i tag html 2 , non il contenuto tra di loro:

<title>My webpage title</title>

Ora confronta .*vs .*?:

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Il significato di "più lungo" e "più breve" in un contesto regex è un po 'complicato, come ha sottolineato Kusalananda . Fare riferimento alla documentazione ufficiale per ulteriori informazioni.
2. Non è consigliabile analizzare html con regex . Questo è solo un esempio a scopo educativo, non usarlo in produzione.


Potresti spiegare un po 'di .*vs .*??
C0deDedalo

@ C0deDaedalus Aggiornato.
nxnev,

9

Supponiamo che prenda una stringa come:

can cats eat plants?

L'uso di greedy c.*sfarà corrispondere l'intera stringa poiché inizia con ce finisce con s, essendo un operatore avido continua a corrispondere fino al verificarsi finale di s.

Considerando che usare il pigro c.*?scorrisponderà solo fino a quando non sviene trovata la prima occorrenza di , cioè stringa can cats.

Dall'esempio sopra, potresti essere in grado di raccogliere che:

"Greedy" significa abbinare la stringa più lunga possibile. "Pigro" significa abbinare la stringa più corta possibile. Aggiunta di un ?di un quantificatore come *, +, ?o {n,m}rende pigri.


1
"Il più breve possibile" sarebbe cats, quindi non impone "il più breve possibile" rigorosamente in questo senso.
Kusalananda

2
@Kusalananda vero, non strettamente in questo senso, ma "il più breve possibile" qui significa tra la prima occorrenza di c e s.
Ashok,

1

Una stringa può essere abbinata in diversi modi (da semplice a più complesso):

  1. Come stringa statica (supponiamo var = 'Hello World!'):

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. Come glob:

    echo ./* # elenca tutti i file in pwd.
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Ci sono globs di base ed estesi. L' caseesempio usa globs di base. L' [[esempio bash usa globi estesi. La prima corrispondenza del file potrebbe essere di base o estesa su alcune shell come l'impostazione extglobin bash. Entrambi sono identici in questo caso. Grep non ha potuto usare i globs.

    L'asterisco in un glob significa qualcosa di diverso da un asterisco in una regex :

    * matches any number (including none) ofqualsiasi personaggio .
    * matches any number (including none) of theelemento precedente .

  3. Come espressione regolare di base (BRE):

    echo "$var" | sed 's/W.*d//' # print: Ciao!
    grep -o 'W.*d' <<<"$var" # print World!

    Non ci sono BRE in shell (di base) o awk.

  4. Espressioni regolari estese (ERE):

    [[ "$var" =~ (H.*l) ]] # match: Hello Worl
    echo "$var" | sed -E 's/(d|o)//g' # print: Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print: Hello
    grep -oE 'H.*l' <<<"$var" # print: Hello Worl

  5. Espressioni regolari compatibili Perl:

    grep -oP 'H.*?l # print: Hel

Solo in un PCRE a *?ha un significato specifico di sintassi.
Rende l'asterisco pigro (non grasso): pigrizia anziché avidità .

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

Questa è solo la punta dell'iceberg, ci sono golosi, pigri e docili o possessivi . Ci sono anche lookahead e lookbehind ma quelli non si applicano all'asterisco *.

Esiste un'alternativa per ottenere lo stesso effetto di una regex non avida:

$ grep -o 'e[^o]*o' <<<"$var"
ello

L'idea è molto semplice: non usare un punto ., annulla la corrispondenza del personaggio successivo [^o]. Con un tag web:

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

Quanto sopra dovrebbe chiarire completamente tutti i commenti di @Bob 3. parafrasando:

  • A. * È una regex comune, non un glob.
  • Solo un regex potrebbe essere compatibile con PCRE.
  • In PCRE: a? modificare il * quantificatore. .*è avido .*?no.

Domande

  • Quali sono le differenze nell'uso di. ? vs. ?

    • Una .*?è valida solo nella sintassi PCRE.
    • A .*è più portatile.
    • Lo stesso effetto di una partita non avida potrebbe essere fatto sostituendo il punto con un intervallo di caratteri negato: [^a]*
  • Quale è meglio e in quali circostanze? Si prega di fornire esempi.
    Meglio? Dipende dall'obiettivo. Non c'è di meglio, ognuno è utile per scopi diversi. Ho fornito diversi esempi sopra. Ne hai bisogno di più?

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.