Regex-Wechsel / oder Operator (foo | bar) in GNU oder BSD Sed
28
Ich kann es scheinbar nicht zum Laufen bringen. Die GNU sed-Dokumentation sagt, dass man aus der Pipe fliehen soll, aber das funktioniert nicht und auch nicht, wenn man eine gerade Pipe ohne Flucht benutzt. 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
echo 'cat dog pear banana cat dog'| sed -E -e 's/cat|dog/Bear/g'
und es wird auf diesen BSD-Systemen und sed -rmit GNU funktionieren .
GNU sedscheint völlig undokumentiert zu sein, aber es funktioniert. -EWenn Sie also ein Multi-Plattform-Skript haben, das auf das Obige beschränkt ist, ist dies Ihre 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 auch -rundokumentierte Aliasnamen unterstützen. OS X funktioniert heute noch nicht und die älteren NetBSD- und OpenBSD-Maschinen, auf die ich Zugriff habe, auch nicht, aber die NetBSD 6.1-Version. Die kommerziellen Unices, die ich erreichen kann, gibt es nicht. Trotzdem wird die Frage nach der Portabilität an dieser Stelle ziemlich kompliziert, aber die einfache Antwort ist, zu wechseln,awk wenn Sie es brauchen, und überall EREs zu verwenden.
Die drei BSDs, die Sie alle erwähnt haben, unterstützen die -rOption als Synonym -Efür Kompatibilität mit GNU sed. OpenBSDs und OS Xs sed -Einterpretieren die Escape-Pipe als Literal-Pipe und nicht als Alternationsoperator. Hier ist ein funktionierender Link zur NetBSD-Manpage und hier einer für OpenBSD, der nicht zehn Jahre alt ist.
Dies liegt daran, dass (a|b)es sich um einen erweiterten regulären Ausdruck handelt, nicht um einen regulären Basisausdruck. Nutzen Sie die -EOption, um damit umzugehen.
echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'
Von der sedManpage:
-E Interpret regular expressions as extended (modern) regular
expressions rather than basic regular expressions (BRE's).
Beachten Sie, dass dies -rein weiteres Flag für dasselbe ist, jedoch -Eportabler ist und sogar in der nächsten Version der POSIX-Spezifikationen enthalten sein wird.
Die übertragbare und effizientere Möglichkeit hierfür sind Adressen. Du kannst das:
printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b'-e '};cBear'
Auf diese Weise wird die aktuelle Zeile automatisch ausgedruckt , wenn die Zeile nicht die Zeichenfolge cat enthält und die Zeichenfolge dogsedb ranches nicht aus dem Skript entfernt wurde, und die nächste Zeile wird eingezogen, um den nächsten Zyklus zu beginnen. Es führt daher nicht den nächsten Befehl aus, der in diesem Beispiel cdie gesamte Zeile zum Lesen von Bear ändert, aber alles tun kann.
Es ist wahrscheinlich auch erwähnenswert, dass jede Anweisung, die !bauf diesen sedBefehl folgt , nur mit einer Zeile übereinstimmen kann , die entweder den String enthält, dogoder cat- so können Sie weitere Tests durchführen, ohne die Gefahr einer Übereinstimmung mit einer Zeile, die nicht übereinstimmt - was bedeutet, dass Sie jetzt Regeln anwenden können auch nur zu dem einen oder anderen.
Aber das ist der nächste. Hier ist die Ausgabe des obigen Befehls:
###OUTPUT###BearBear
pear
banana
BearBear
Sie können eine Nachschlagetabelle mit Rückverweisen auch 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'
Die Einrichtung dieses einfachen Beispielfalls ist sehr viel aufwändiger, kann jedoch sedlangfristig zu wesentlich flexibleren Skripten führen.
In der ersten Zeile xändere ich Hold Space und Pattern Space und füge dann den String <space>cat ein<space> Dog<space> in den Hold Space ein, bevorx .
Von da an und in jeder folgenden Zeile Ghalte ich das Leerzeichen an das Leerzeichenmuster angehängt und überprüfe dann, ob alle Zeichen vom Zeilenanfang bis zur neuen Zeile, die ich gerade am Ende hinzugefügt habe, mit einer von Leerzeichen umgebenen Zeichenfolge übereinstimmen. Wenn ja, ersetze ich die ganze Partie durch Bär und wenn nicht, wird da kein Schaden angerichtet, weil ich weiter macheP nur bis zur ersten im Musterbereich auftretenden Zeile weiterdrucke, und dlösche dann alles.
###OUTPUT###BearBear
pear
banana
BearBear
Und wenn ich "flexibel" sage, dann meine ich das auch so. Hier ersetzt es Katze durch Braunbär und Hund durch Schwarzbär :
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###BrownBearBlackBear
pear
banana
BrownBearBlackBear
Natürlich können Sie den Inhalt der Nachschlagetabelle erheblich erweitern - ich habe die Idee aus Greg Ubbens Usenet-E-Mails zu diesem Thema aufgegriffen, als er in den 90er Jahren beschrieb, wie er aus einer einzigen sed s///Anweisung einen groben Taschenrechner konstruierte .
@ 1_CR - Siehe meine letzte Bearbeitung - nicht meine Idee - was nicht heißt, dass ich das nicht schätze und es als Kompliment betrachte. Aber ich gebe gerne Anerkennung, wo es fällig ist.
mikeserv
1
Dies ist eine ziemlich alte Frage, aber für den Fall, dass 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 Option. Es ist eine logische Entsprechung von oder. So entfernen Sie beispielsweise Zeilen, die einen bestimmten Code enthalten:
Sie können sagen : sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'
Hier ist eine Technik , die Verwendung jeglicher Implementierung spezifische Optionen nicht machen sed(zB -E, -r). Anstatt das Muster als einen einzelnen regulären Ausdruck zu beschreiben cat|dog, können wir einfach sedzweimal ausführen :
echo 'cat
dog
pear
banana
cat
dog'| sed 's/cat/Bear/g'| sed 's/dog/Bear/g'
Es ist eine offensichtliche Problemumgehung, aber es lohnt sich zu teilen. Es wird natürlich auf mehr als zwei Musterketten verallgemeinert, obwohl eine sehr lange Kette von sed's nicht besonders gut aussieht.
Ich verwende häufig sed -i(was in allen Implementierungen gleich funktioniert), um Änderungen an Dateien vorzunehmen. Hier kann eine lange Liste von Musterzeichenfolgen integriert 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
-r
Option als Synonym-E
für Kompatibilität mit GNU sed. OpenBSDs und OS Xssed -E
interpretieren die Escape-Pipe als Literal-Pipe und nicht als Alternationsoperator. Hier ist ein funktionierender Link zur NetBSD-Manpage und hier einer für OpenBSD, der nicht zehn Jahre alt ist.-E
: developer.apple.com/library/mac/documentation/Darwin/Reference/…-E
gnu.org/software/sed/manual/sed.html#index-_002dE .Dies liegt daran, dass
(a|b)
es sich um einen erweiterten regulären Ausdruck handelt, nicht um einen regulären Basisausdruck. Nutzen Sie die-E
Option, um damit umzugehen.Von der
sed
Manpage:Beachten Sie, dass dies
-r
ein weiteres Flag für dasselbe ist, jedoch-E
portabler ist und sogar in der nächsten Version der POSIX-Spezifikationen enthalten sein wird.quelle
Die übertragbare und effizientere Möglichkeit hierfür sind Adressen. Du kannst das:
Auf diese Weise wird die aktuelle Zeile automatisch ausgedruckt , wenn die Zeile nicht die Zeichenfolge cat enthält und die Zeichenfolge dog
sed
b
ranches nicht aus dem Skript entfernt wurde, und die nächste Zeile wird eingezogen, um den nächsten Zyklus zu beginnen. Es führt daher nicht den nächsten Befehl aus, der in diesem Beispielc
die gesamte Zeile zum Lesen von Bear ändert, aber alles tun kann.Es ist wahrscheinlich auch erwähnenswert, dass jede Anweisung, die
!b
auf diesensed
Befehl folgt , nur mit einer Zeile übereinstimmen kann , die entweder den String enthält,dog
odercat
- so können Sie weitere Tests durchführen, ohne die Gefahr einer Übereinstimmung mit einer Zeile, die nicht übereinstimmt - was bedeutet, dass Sie jetzt Regeln anwenden können auch nur zu dem einen oder anderen.Aber das ist der nächste. Hier ist die Ausgabe des obigen Befehls:
Sie können eine Nachschlagetabelle mit Rückverweisen auch portabel implementieren.
Die Einrichtung dieses einfachen Beispielfalls ist sehr viel aufwändiger, kann jedoch
sed
langfristig zu wesentlich flexibleren Skripten führen.In der ersten Zeile
x
ändere ich Hold Space und Pattern Space und füge dann den String<space>
cat ein<space>
Dog<space>
in den Hold Space ein, bevorx
.Von da an und in jeder folgenden Zeile
G
halte ich das Leerzeichen an das Leerzeichenmuster angehängt und überprüfe dann, ob alle Zeichen vom Zeilenanfang bis zur neuen Zeile, die ich gerade am Ende hinzugefügt habe, mit einer von Leerzeichen umgebenen Zeichenfolge übereinstimmen. Wenn ja, ersetze ich die ganze Partie durch Bär und wenn nicht, wird da kein Schaden angerichtet, weil ich weiter macheP
nur bis zur ersten im Musterbereich auftretenden Zeile weiterdrucke, undd
lösche dann alles.Und wenn ich "flexibel" sage, dann meine ich das auch so. Hier ersetzt es Katze durch Braunbär und Hund durch Schwarzbär :
Natürlich können Sie den Inhalt der Nachschlagetabelle erheblich erweitern - ich habe die Idee aus Greg Ubbens Usenet-E-Mails zu diesem Thema aufgegriffen, als er in den 90er Jahren beschrieb, wie er aus einer einzigen
sed s///
Anweisung einen groben Taschenrechner konstruierte .quelle
Dies ist eine ziemlich alte Frage, aber für den Fall, dass 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 Option. Es ist eine logische Entsprechung von oder. So entfernen Sie beispielsweise Zeilen, die einen bestimmten Code enthalten:
Sie können sagen :
sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'
oder fügen Sie dies in Ihre sed-Datei ein:
quelle
Hier ist eine Technik , die Verwendung jeglicher Implementierung spezifische Optionen nicht machen
sed
(zB-E
,-r
). Anstatt das Muster als einen einzelnen regulären Ausdruck zu beschreibencat|dog
, können wir einfachsed
zweimal ausführen :Es ist eine offensichtliche Problemumgehung, aber es lohnt sich zu teilen. Es wird natürlich auf mehr als zwei Musterketten verallgemeinert, obwohl eine sehr lange Kette von
sed
's nicht besonders gut aussieht.Ich verwende häufig
sed -i
(was in allen Implementierungen gleich funktioniert), um Änderungen an Dateien vorzunehmen. Hier kann eine lange Liste von Musterzeichenfolgen integriert werden, da jedes temporäre Ergebnis in der Datei gespeichert wird:quelle