Wie oder warum ist die Verwendung von ". *?" Besser als ". *"?

9

Ich habe diese Frage in SuperUser beantwortet , die sich auf reguläre Ausdrücke bezieht, die beim Greifen einer Ausgabe verwendet werden.

Die Antwort, die ich gab, war folgende:

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

Und dann schrieb @Bob in drei Kommentaren zu meiner Antwort Folgendes :

.*ist gierig und könnte mehr erfassen, als Sie wollen. .*?ist normalerweise besser.

Dann das,

Das ?ist ein Modifikator *, der es faul macht, anstatt den gierigen Standard. Angenommen, PCRE.

Ich habe gegoogelt PCRE, konnte aber in meiner Antwort nicht verstehen , welche Bedeutung dies hat.

und schließlich das,

Ich sollte auch darauf hinweisen, dass dies Regex ist (grep macht standardmäßig POSIX Regex), kein Shell Glob.

Ich weiß nur, was ein Regex ist und wie er im Befehl grep sehr einfach verwendet wird. Daher konnte ich keinen dieser drei Kommentare erhalten und habe folgende Fragen:

  • Was sind Unterschiede in der Nutzung von .*?vs. .*?
  • Was ist besser und unter welchen Umständen? Bitte geben Sie Beispiele an.

Auch wäre es hilfreich, die Kommentare zu verstehen, wenn jemand könnte


UPDATE: Als Antwort auf die Frage Wie unterscheidet sich Regex von Shell Globs? @Kusalananda hat diesen Link in seinem Kommentar angegeben.

HINWEIS: Falls erforderlich, lesen Sie bitte meine Antwort auf diese Frage, bevor Sie antworten, um auf den Kontext zu verweisen.

C0deDaedalus
quelle
Dies sind zwei sehr unterschiedliche Fragen. Die erste Frage wird von unix.stackexchange.com/questions/57957/… beantwortet, während die zweite Frage von der Anwendung des Musters abhängt (es kann nicht unter allen Umständen als "besser" bezeichnet werden).
Kusalananda
Sie können diese Frage so bearbeiten , dass sie sich nur auf das Problem .*vs. .*?bezieht. Die Frage "Unterschied zwischen regulären Ausdrücken und Shell-Globs" wurde bereits auf dieser Site behandelt.
Kusalananda

Antworten:

7

Ashok hat bereits auf den Unterschied zwischen .*und hingewiesen.*? , daher werde ich nur einige zusätzliche Informationen bereitstellen.

grep (unter der Annahme der GNU-Version) unterstützt 4 Möglichkeiten, Zeichenfolgen abzugleichen:

  • Feste Zeichenfolgen
  • Grundlegende reguläre Ausdrücke (BRE)
  • Erweiterte reguläre Ausdrücke (ERE)
  • Perl-kompatible reguläre Ausdrücke (PCRE)

grep verwendet standardmäßig BRE.

BRE und ERE sind im Kapitel " Reguläre Ausdrücke" von POSIX dokumentiert, und PCRE ist auf seiner offiziellen Website dokumentiert . Bitte beachten Sie, dass Funktionen und Syntax zwischen den Implementierungen variieren können.

Es ist erwähnenswert, dass weder BRE noch ERE Faulheit unterstützen :

Das Verhalten mehrerer benachbarter Duplizierungssymbole ('+', '*', '?' Und Intervalle) führt zu undefinierten Ergebnissen.

Wenn Sie diese Funktion verwenden möchten, müssen Sie stattdessen PCRE verwenden:

# 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

Bearbeiten 1

Könnten Sie bitte etwas über .*vs erklären .*??

  • .*wird verwendet, um das "längste" 1 mögliche Muster abzugleichen.

  • .*?wird verwendet, um das "kürzeste" 1 mögliche Muster abzugleichen.

Nach meiner Erfahrung ist das meistgesuchte Verhalten normalerweise das zweite.

Nehmen wir zum Beispiel an, wir haben die folgende Zeichenfolge und möchten nur die HTML-Tags 2 abgleichen , nicht den Inhalt zwischen ihnen:

<title>My webpage title</title>

Jetzt vergleiche .*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. Die Bedeutung von "am längsten" und "am kürzesten" in einem Regex-Kontext ist etwas schwierig, wie Kusalananda betonte . Weitere Informationen finden Sie in der offiziellen Dokumentation.
2. Es wird nicht empfohlen, HTML mit Regex zu analysieren . Dies ist nur ein Beispiel für Bildungszwecke. Verwenden Sie es nicht in der Produktion.

nxnev
quelle
Könnten Sie bitte etwas über .*vs erklären .*??
C0deDaedalus
@ C0deDaedalus Aktualisiert.
Nxnev
9

Angenommen, ich nehme eine Zeichenfolge wie:

can cats eat plants?

Die Verwendung von gierig c.*sstimmt mit der gesamten Zeichenfolge überein, da sie mit beginnt cund mit endet. Als sgieriger Operator stimmt sie bis zum endgültigen Auftreten von s weiter überein.

Während die Verwendung der Lazy c.*?snur übereinstimmt, bis das erste Vorkommen von sgefunden wird, dh Zeichenfolge can cats.

Aus dem obigen Beispiel können Sie möglicherweise Folgendes entnehmen:

"Gierig" bedeutet, dass die längste mögliche Zeichenfolge gefunden wird. "Lazy" bedeutet, die kürzestmögliche Zeichenfolge zu finden. Hinzufügen eines ?zu einem quantifier wie *, +, ?oder {n,m}es faul macht.

Ashok
quelle
1
"Kürzestmöglich" wäre cats, also wird "kürzestmöglich" nicht streng in diesem Sinne durchgesetzt.
Kusalananda
2
@Kusalananda wahr, nicht streng in diesem Sinne, aber "kürzest möglich" bedeutet hier zwischen dem ersten Auftreten von c und s.
Ashok
1

Eine Zeichenfolge kann auf verschiedene Arten abgeglichen werden (von einfach bis komplexer):

  1. Als statische Zeichenfolge (Angenommen, var = 'Hello World!'):

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

  2. Als Globus:

    echo ./* # listet alle Dateien in pwd auf.
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    Es gibt einfache und erweiterte Globs. Im caseBeispiel werden grundlegende Globs verwendet. Im Bash- [[Beispiel werden erweiterte Globs verwendet. Die erste Dateiübereinstimmung kann einfach sein oder auf einer Shell wie der Einstellung extglobin Bash erweitert werden. Beide sind in diesem Fall identisch. Grep konnte keine Globs verwenden.

    Das Sternchen in einem Globus bedeutet etwas anderes als ein Sternchen in einem regulären Ausdruck :

    * matches any number (including none) ofalle Zeichen .
    * matches any number (including none) of thevorhergehendes Element .

  3. Als grundlegender regulärer Ausdruck (BRE):

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

    Es gibt kein BRE in (Grund-) Muscheln oder Awk.

  4. Erweiterte reguläre Ausdrücke (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: Hallo
    grep -oE 'H.*l' <<<"$var" # print: Hallo Welt

  5. Perl-kompatible reguläre Ausdrücke:

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

Nur in einer PCRE *?hat a eine bestimmte Syntaxbedeutung.
Es macht das Sternchen faul (unhöflich): Faulheit statt Gier .

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

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

Dies ist nur die Spitze des Eisbergs, es gibt gierige, faule und fügsame oder besitzergreifende . Es gibt auch Lookahead und Lookbehind, aber diese gelten nicht für das Sternchen *.

Es gibt eine Alternative, um den gleichen Effekt wie bei einem nicht gierigen regulären Ausdruck zu erzielen:

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

Die Idee ist sehr einfach: Verwenden Sie keinen Punkt ., negieren Sie das nächste übereinstimmende Zeichen [^o]. Mit einem Web-Tag:

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

Das Obige sollte alle @ Bob 3-Kommentare vollständig verdeutlichen. Umschreibung:

  • A. * Ist ein allgemeiner Regex, kein Glob.
  • Nur ein Regex kann PCRE-kompatibel sein.
  • In PCRE: a? Ändern Sie den * Quantifizierer. .*ist gierig .*?ist nicht.

Fragen

  • Was sind Unterschiede in der Verwendung von. ? vs. ?

    • A .*?ist nur in der PCRE-Syntax gültig.
    • A .*ist tragbarer.
    • Der gleiche Effekt wie bei einer nicht gierigen Übereinstimmung kann erzielt werden, indem der Punkt durch einen negierten Zeichenbereich ersetzt wird: [^a]*
  • Was ist besser und unter welchen Umständen? Bitte geben Sie Beispiele an.
    Besser? Es kommt auf das Ziel an. Es gibt kein besseres, jedes ist für verschiedene Zwecke nützlich. Ich habe oben einige Beispiele angegeben. Brauchst du mehr?

Isaac
quelle