Regex alternation / or operator (foo | bar) w GNU lub BSD Sed

28

Nie mogę sprawić, żeby działało. Dokumentacja GNU sed mówi, że trzeba uciec z rury, ale to nie działa, podobnie jak używanie prostej rury bez tej ścieżki. Dodanie parens nie ma znaczenia.

$ 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
Gregg Leventhal
źródło

Odpowiedzi:

33

Domyślniesed używa podstawowych wyrażeń regularnych POSIX , które nie zawierają |operatora alternacji. Wiele wersji sed, w tym GNU i FreeBSD, obsługuje przełączanie na rozszerzone wyrażenia regularne , które zawierają |naprzemiennie. Jak to zrobić jest różna: GNU sed zastosowań-r , podczas gdy FreeBSD , NetBSD , OpenBSD , a OS X sed wykorzystania -E. Inne wersje w większości go nie obsługują. Możesz użyć:

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

i będzie działać na tych systemach BSD i na sed -rGNU.


sedWydaje się, że GNU ma całkowicie nieudokumentowane, ale działające wsparcie -E, więc jeśli masz skrypt wieloplatformowy ograniczony do powyższego, to najlepsza opcja. Ponieważ nie jest to udokumentowane, prawdopodobnie nie możesz na nim polegać.

Komentarz zauważa, że ​​wersje BSD obsługują również -rjako nieudokumentowany alias. System OS X nadal nie działa dzisiaj, a starsze maszyny NetBSD i OpenBSD, do których mam dostęp, też nie, ale NetBSD 6.1 tak. Komercyjne Unices, do których mogę dotrzeć, nie są powszechnie dostępne. W związku z tym pytanie dotyczące przenośności staje się w tym momencie dość skomplikowane, ale prostą odpowiedzią jest przejście na,awk jeśli jest to potrzebne, która korzysta z ERE wszędzie.

Michael Homer
źródło
Trzy BSD wspomniałeś wszystko wspierać -ropcję jako synonim -Edla kompatybilności z GNU sed. OpenBSD i OS X sed -Ebędą interpretować ucieczkę potoku jako literał potoku, a nie jako operator przemiany. Oto działający link do strony podręcznika NetBSD, a tutaj jest jeden dla OpenBSD, który nie ma dziesięciu lat.
damien
9

Dzieje się tak, ponieważ (a|b)jest rozszerzonym wyrażeniem regularnym, a nie podstawowym wyrażeniem regularnym. Skorzystaj z -Eopcji, aby sobie z tym poradzić.

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

Ze strony podręcznika sed:

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

Zauważ, że -rjest to kolejna flaga dla tej samej rzeczy, ale -Ejest bardziej przenośna i będzie nawet w następnej wersji specyfikacji POSIX.

Networker
źródło
6

Przenośnym sposobem na to - i bardziej wydajnym - są adresy. Możesz to zrobić:

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

W ten sposób, jeśli linia nie zawiera ciąg kota i nie zawiera łańcuch pies sed b Rancza z skryptu autoprints obecną linię i ciągnie w następnym, aby rozpocząć kolejny cykl. Dlatego nie wykonuje następnej instrukcji - która w tym przykładzie custawia całą linię do czytania Niedźwiedzia, ale może zrobić wszystko.

Prawdopodobnie warto również zauważyć, że każda instrukcja występująca po !btym sedpoleceniu może pasować tylko do wiersza zawierającego ciąg doglub cat- dzięki czemu można wykonywać dalsze testy bez niebezpieczeństwa dopasowania wiersza, który tego nie robi - co oznacza, że ​​można teraz zastosować reguły tylko do jednego lub drugiego.

Ale to jest następne. Oto dane wyjściowe powyższego polecenia:

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

Możesz także przenośnie zaimplementować tabelę odnośników z referencjami wstecznymi.

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

Konfiguracja tego prostego przykładowego przypadku wymaga dużo więcej pracy, ale sedna dłuższą metę może zapewnić znacznie bardziej elastyczne skrypty.

W pierwszej linii I e xzmiana miejsca przechowywania i wzór przestrzeni następnie włóż ciąg <space>kota <space>psa<space> do miejsca przechowywania przed e xzmieniającym je z powrotem.

Odtąd i na każdej kolejnej linii Gtrzymam spację dołączoną do spacji wzoru, a następnie sprawdzam, czy wszystkie znaki od początku linii do nowej linii, którą właśnie dodałem na końcu, pasują do ciągu otoczonego spacjami po niej. Jeśli tak, to zamieniam całą partię na Niedźwiedzia, a jeśli nie, to nic złego się nie dzieje, ponieważ następnie Printuję tylko do pierwszej nowej linii w przestrzeni wzorów, a następnie dusuwam wszystko.

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

A kiedy mówię „elastyczny”, mam na myśli to. Tutaj zastępuje kota z BrownBear, a psa z 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

Oczywiście możesz znacznie rozszerzyć zawartość tabeli odnośników - podniosłem ten pomysł z e-maili usenetowych Grega Ubbena na ten temat, kiedy w latach 90. opisał, jak zbudował prymitywny kalkulator na podstawie jednej sed s///instrukcji.

mikeserv
źródło
1
uff, +1. Masz
ochotę
@ 1_CR - Zobacz moją ostatnią edycję - nie mój pomysł - co nie znaczy, że nie doceniam tego i uważam to za komplement. Ale lubię uznawać, kiedy jest to należne.
mikeserv
1

jest to dość stare pytanie, ale na wypadek, gdyby ktoś chciał spróbować, istnieje dość niewielki wysiłek, aby to zrobić w plikach sed. Każda opcja może być wymieniona w osobnym wierszu, a sed oceni każdą z nich. To logiczny odpowiednik lub. Na przykład, aby usunąć wiersze zawierające określony kod:

możesz powiedzieć : sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'

lub umieść to w swoim pliku sed:

/^\/\*!40103.*\/;$/d
/^\/\*!40101.*\/;$/d
/^\/\*!40111.*\/;$/d
Mordechaj
źródło
0

Oto technika, która nie korzysta z żadnej opcji realizacja specyficznych sed(np -E, -r). Zamiast opisywać wzorzec jako pojedyncze wyrażenie regularne cat|dog, możemy po prostu uruchomić seddwa razy:

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

To naprawdę oczywiste obejście, ale warto się nim podzielić. Naturalnie uogólnia się na więcej niż dwa ciągi wzorców, chociaż bardzo długi łańcuch sednie jest zbyt dobrze wyglądający.

Często używam sed -i(co działa tak samo we wszystkich implementacjach) do wprowadzania zmian w plikach. Tutaj długa lista ciągów znaków może być ładnie włączona, ponieważ każdy wynik tymczasowy jest zapisywany w pliku:

for pattern in cat dog owl; do
    sed -i "s/${pattern}/Bear/g" myfile
done
jmd_dk
źródło