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 sed
und 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 \b
Wort „Grenzen“ eingeschlossen habe?
Antwort1
Dies ist schwierig, da sed
Lookarounds 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 \3
scheint die Zeichenfolge zu sein baz land
, als ob das \b
Vorangehende and
nie .*\band\b
eine Auswirkung gehabt hätte.
Der Befehl
sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'
sed
scheint 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 sed
oder GNU glibc
dazu 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 sed
und 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'
undawk '/(\<co){2}/'
hat auch das fehlerhafte Verhalten,grep -E '(\<co){2}'
gibt aber keine korrekte Ausgabe ausRichtiges Verhalten, keine Ausgabe:
$ echo 'cocoa' | sed -nE '/\bco\bco/p'
Falsche Ausgabe: Es gibt nur 1 ganzes Wort
it
nachwith
$ 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. it
und sit
. Nicht jedoch, wenn am Ende Zeichen stehen, z. B. it
und site
und 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