it-swarm.com.de

Regex-Wechsel / oder Operator (foo | bar) in GNU oder BSD Sed

Ich kann es scheinbar nicht zum Laufen bringen. GNU sed Dokumentation sagt, dass man aus dem Rohr entkommen soll, aber das funktioniert nicht und es funktioniert auch nicht, ein gerades Rohr ohne Flucht zu verwenden. Das Hinzufügen von Parens macht keinen Unterschied.

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat|dog/Bear/g'
cat
dog
pear
banana
cat
dog

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat\|dog/Bear/g'
cat
dog
pear
banana
cat
dog
31
Gregg Leventhal

Standardmäßig verwendet sedPOSIX Basic Regular Expressions , die den Wechseloperator | Nicht enthalten. Viele Versionen von sed, einschließlich GNU und FreeBSD), unterstützen das Umschalten in Extended Regular Expressions , die | Alternation enthalten Sie tun das variiert: GNU sed verwendet -r , während FreeBSD , NetBSD , OpenBSD , und OS X sed benutze -E. Andere Versionen unterstützen es meistens überhaupt nicht. Du kannst verwenden:

echo 'cat dog pear banana cat dog' | sed -E -e 's/cat|dog/Bear/g'

und es wird auf diesen BSD-Systemen und sed -r mit GNU funktionieren.


GNU sed scheint völlig undokumentiert zu sein, unterstützt jedoch -E. Wenn Sie also ein plattformübergreifendes Skript haben, das auf das oben Gesagte beschränkt ist, ist dies die beste Option. Da es nicht dokumentiert ist, können Sie sich wahrscheinlich nicht wirklich darauf verlassen.

In einem Kommentar wird darauf hingewiesen, dass die BSD-Versionen -r Auch als undokumentierten Alias ​​unterstützen. OS X funktioniert heute noch nicht und die älteren NetBSD- und OpenBSD-Computer, auf die ich Zugriff habe, auch nicht, aber NetBSD 6.1. Die kommerziellen Einheiten, die ich allgemein erreichen kann, tun dies nicht. Bei alledem wird die Portabilitätsfrage an diesem Punkt ziemlich kompliziert, aber die einfache Antwort lautet wechseln Sie zu awk , wenn Sie es brauchen, das überall EREs verwendet.

36
Michael Homer

Dies geschieht, weil (a|b) ist ein erweiterter regulärer Ausdruck, kein regulärer Basisausdruck. Verwenden Sie die -E Option, um damit umzugehen.

echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'

Von der Manpage sed:

 -E      Interpret regular expressions as extended (modern) regular
         expressions rather than basic regular expressions (BRE's).

Beachten Sie, dass -r ist eine andere Flagge für die gleiche Sache, aber -E ist portabler und wird sogar in der nächsten Version der POSIX-Spezifikationen enthalten sein.

9
Nidal

Der tragbare und effizientere Weg, dies zu tun, sind Adressen. Du kannst das:

printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b' -e '};cBear'

Auf diese Weise enthält die Zeile nicht die Zeichenfolge cat und nicht die Zeichenfolge dog sedb verlässt das Skript, druckt die aktuelle Zeile automatisch und zieht die nächste Zeile ein, um den nächsten Zyklus zu beginnen. Es führt daher nicht die nächste Anweisung aus - die in diesem Beispiel c die gesamte zu lesende Zeile ändert Bear , aber es kann alles tun.

Es ist wahrscheinlich auch erwähnenswert, dass jede Aussage nach dem !b in diesem Befehl sed kann nur in einer Zeile übereinstimmen, die entweder die Zeichenfolge dog oder cat - damit Sie weitere Tests durchführen können, ohne dass die Gefahr besteht, dass eine Linie übereinstimmt, die dies nicht tut. Dies bedeutet, dass Sie jetzt auch nur auf die eine oder andere Regel anwenden können.

Aber das ist der nächste. Hier ist die Ausgabe des obigen Befehls:

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

Sie können auch eine Nachschlagetabelle mit Rückreferenzen portabel implementieren.

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ cat dog /;x
};G;s/^\(.*\)\n.* \1 .*/Bear/;P;d'

Das Einrichten für diesen einfachen Beispielfall ist viel aufwändiger, kann aber auf lange Sicht zu viel flexibleren sed - Skripten führen.

In der ersten Zeile ändere ich x den Haltebereich und den Musterbereich und füge dann die Zeichenfolge <space>Katze<space>Hund<space> in den Haltebereich, bevor e x sie zurück ändert.

Von da an und in jeder folgenden Zeile halte ich G und halte den an den Musterbereich angehängten Platz und überprüfe dann, ob alle Zeichen vom Zeilenanfang bis zur neuen Zeile, die ich gerade am Ende hinzugefügt habe, mit einer Zeichenfolge übereinstimmen umgeben von Räumen danach. Wenn ja, ersetze ich das gesamte Los durch Bär und wenn nicht, wird kein Schaden angerichtet, da ich als nächstes P nur bis zum ersten Mal drucke Wenn eine neue Zeile im Musterbereich auftritt, wird mit d alles gelöscht.

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

Und wenn ich flexibel sage, meine ich es auch so. Hier ersetzt es cat durch BrownBear und Hund mit BlackBear :

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ 1cat Brown 2dog Black /;x
};G;s/^\(.*\)\n.* [0-9]\1 \([^ ]*\) .*/\2Bear/;P;d'

###OUTPUT###
BrownBear
BlackBear
pear
banana
BrownBear
BlackBear

Sie können den Inhalt der Nachschlagetabelle natürlich erheblich erweitern - ich habe die Idee von Greg Ubbens Usenet-E-Mails zu diesem Thema aufgegriffen, als er in den 90er Jahren beschrieb, wie er einen groben Taschenrechner konstruierte aus einem einzigen sed s/// Aussage.

6
mikeserv

dies ist eine ziemlich alte Frage, aber falls jemand es versuchen möchte, gibt es einen relativ geringen Aufwand, dies in sed mit sed-Dateien zu tun. Jede Option kann in einer separaten Zeile aufgeführt werden, und sed bewertet jede einzelne. Es ist ein logisches Äquivalent von oder. So entfernen Sie beispielsweise Zeilen, die einen bestimmten Code enthalten:

sie können sagen: sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'

oder legen Sie dies in Ihre sed-Datei:

/^\/\*!40103.*\/;$/d
/^\/\*!40101.*\/;$/d
/^\/\*!40111.*\/;$/d
1
Mordechai

Hier ist eine Technik, die keine implementierungsspezifischen Optionen für sed verwendet (z. B. -E, -r). Anstatt das Muster als einen einzelnen regulären Ausdruck zu beschreiben cat|dog, wir können sed einfach zweimal ausführen:

echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat/Bear/g' | sed 's/dog/Bear/g'

Es ist wirklich eine offensichtliche Problemumgehung, aber es lohnt sich, sie zu teilen. Es verallgemeinert sich natürlich auf mehr als zwei Musterzeichenfolgen, obwohl eine sehr lange Kette von sed nicht allzu gut aussieht.

Ich benutze oft sed -i (funktioniert in allen Implementierungen gleich), um Änderungen an Dateien vorzunehmen. Hier kann eine lange Liste von Musterzeichenfolgen gut eingefügt werden, da jedes temporäre Ergebnis in der Datei gespeichert wird:

for pattern in cat dog owl; do
    sed -i "s/${pattern}/Bear/g" myfile
done
0
jmd_dk