Warum ersetzt dieser Sed-Befehl nicht das drittletzte „und“?

Warum ersetzt dieser Sed-Befehl nicht das drittletzte „und“?

Aktualisierung 26.05.2020

Es schien, als wäre dies ein Fehler, also habe ich einen Fehler gemeldet. Die ID lautet #41558.


Ich habe einfach ein bisschen herumgespielt sedund mir ist diese Übung eingefallen: Ersetzen Sie das drittletzte Vorkommen von „und“ (das Wort, nicht als Teilzeichenfolge) durch Folgendes:

dog XYZ foo and bar and baz land good

Ich dachte, das würde funktionieren

echo 'dog and foo and bar and baz land good' |
    sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'

aber es ersetzt tatsächlich das vorletzte Vorkommen von „und“. Die einzige Erklärung, die mir einfällt, ist, dass „Land“ als eines der eingeschlossen ist \band\b, aber das sollte nicht der Fall sein, da ich das \bWort „Grenzen“ eingeschlossen habe?

Antwort1

Dies ist schwierig, da sedLookarounds usw. nicht unterstützt werden (wie Sie es in einem PCRE tun können). Es wäre einfacher, die Zeichenfolge umzukehren und das dritte Vorkommen des umgekehrten Wortes vom Anfang an zu ersetzen und dann erneut umzukehren.

$ echo 'dog and foo and bar and baz land good' | rev | sed 's/\<dna\>/XXX/3' | rev
dog XXX foo and bar and baz land good

Warum Ihr Ausdruck nicht funktioniert, scheint ein Fehler zu sein. Der Rückverweis \3scheint die Zeichenfolge zu sein  baz land, als ob das \bVorangehende andnie .*\band\beine Auswirkung gehabt hätte.

Der Befehl

sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'

sedscheint unter OpenBSD mit seiner nativen Version (die \<und \>anstelle von verwendet) das Richtige zu tun \b.

Ich habe bisher noch keinen vorhandenen Fehlerbericht gegen GNU sedoder GNU glibcdazu gefunden, obwohl es mich nicht überraschen würde, wenn es zumindestverwandtZuGlibc-Fehler 25322(weil, siehe unten).

Sie können dieses Problem umgehen, indem Sie etwas ausführlicher vorgehen:

sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'

Antwort2

Ich würde vorschlagen, ein Problem zu melden. Ich habe diese Beispiele getestet, was zu demselben Verhalten mit GNU grep, GNU sedund führt GNU awk. Außer in einem Fall, der unten aufgeführt ist.

  • Falsche Ausgabe:

    $ echo 'cocoa' | sed -nE '/(\bco){2}/p'
    cocoa
    

    sed -nE '/(\<co){2}/p'und awk '/(\<co){2}/'hat auch das fehlerhafte Verhalten, grep -E '(\<co){2}'gibt aber keine korrekte Ausgabe aus

  • Richtiges Verhalten, keine Ausgabe:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • Falsche Ausgabe: Es gibt nur 1 ganzes Wort itnachwith

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
    it line XYZ too
    
  • Korrektes Verhalten, Eingabe wird nicht verändert

    $ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/'
    it line with it here sit too
    
  • Das Ändern der Wortgrenzen in \<und \>führt zu einem anderen Problem.

    Das richtigändert sich nichtdie Eingabe:

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here sit too
    

    Dies ändert die Eingabe korrekt

    $ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line XYZ too
    

    Aber dieser kann die Eingabe nicht ändern

    $ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here it too sit
    

Außerdem tritt das problematische Verhalten nur auf, wenn das Konfliktwort am Anfang zusätzliche Zeichen hat, z. B. itund sit. Nicht jedoch, wenn am Ende Zeichen stehen, z. B. itund siteund item.

$ echo 'it line with it here item too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
it line with it here item too
$ echo 'it line with it here it too item' | sed -E 's/with(.*\<it\>){2}/XYZ/'
it line XYZ too item

verwandte Informationen