Chcę użyć, sed
aby zastąpić cokolwiek w ciągu między pierwszym AB
i pierwszym wystąpieniem AC
(włącznie) z XXX
.
Na przykład mam ten ciąg (ten ciąg jest tylko do testu):
ssABteAstACABnnACss
i chciałbym wynik podobny do tego: ssXXXABnnACss
.
Zrobiłem to z perl
:
$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
ale chcę to zaimplementować sed
. Następujące (przy użyciu wyrażenia regularnego zgodnego z Perl) nie działa:
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
text-processing
sed
regular-expression
بارپابابا
źródło
źródło
Odpowiedzi:
Wyrażenia regularne Sed pasują do najdłuższego dopasowania. Sed nie ma odpowiednika niechcianego.
Oczywiście chcemy dopasować
AB
,a następnie
AC
,po której następuje
AC
Niestety
sed
nie można zrobić nr 2 - przynajmniej nie dla wyrażenia regularnego składającego się z wielu znaków. Oczywiście, dla wyrażenia regularnego zawierającego jeden znak, takiego jak@
(lub nawet[123]
), możemy zrobić[^@]*
lub[^123]*
. Możemy więc obejść ograniczenia sed, zmieniając wszystkie wystąpieniaAC
na,@
a następnie szukającAB
,a następnie
@
,po których następuje
@
lubię to:
Ostatnia część zmienia niedopasowane przypadki
@
powrotu doAC
.Ale, oczywiście, jest to lekkomyślne podejście, ponieważ dane wejściowe mogą już zawierać
@
znaki, więc dopasowując je, możemy uzyskać fałszywe alarmy. Ponieważ jednak żadna zmienna powłoki nigdy nie będzie zawierała znaku NUL (\x00
), NUL jest prawdopodobnie dobrym znakiem do użycia w powyższym obejściu zamiast@
:Korzystanie z NUL wymaga GNU sed. (Aby upewnić się, że funkcje GNU są włączone, użytkownik nie może ustawiać zmiennej powłoki POSIXLY_CORRECT.)
Jeśli używasz sed z
-z
flagą GNU do obsługi danych wejściowych oddzielonych przez NUL, takich jak dane wyjściowefind ... -print0
, to NUL nie będzie w przestrzeni wzorców, a NUL jest dobrym wyborem do podstawienia tutaj.Chociaż NUL nie może znajdować się w zmiennej bash, możliwe jest włączenie jej do
printf
polecenia. Jeśli Twój ciąg wejściowy może w ogóle zawierać dowolny znak, w tym NUL, zobacz odpowiedź Stéphane Chazelas, która dodaje sprytną metodę ucieczki.źródło
echo
lubprintf
`\ 000 'w porządku w bash (lub dane wejściowe mogą pochodzić z pliku). Ale generalnie ciąg tekstu prawdopodobnie nie ma wartości NUL.AC
sięAC@
i wrócił?Niektóre
sed
implementacje mają na to wsparcie.ssed
ma tryb PCRE:AT&T ast sed ma koniunkcję i negację podczas używania rozszerzonych wyrażeń regularnych :
Przenośnie możesz użyć tej techniki: zastąp ciąg końcowy (tutaj
AC
) pojedynczym znakiem, który nie występuje ani na początku, ani na końcu (jak:
tutaj), abyś mógł to zrobićs/AB[^:]*://
, a na wypadek, gdyby znak ten pojawił się na wejściu , użyj mechanizmu zmiany znaczenia, który nie koliduje z ciągami początkowym i końcowym.Przykład:
W GNU
sed
, podejście polega na użyciu znaku nowej linii jako znaku zastępującego. Ponieważsed
przetwarza jedną linię na raz, nowa linia nigdy nie występuje w obszarze wzorców, więc można wykonać:To na ogół nie działa z innymi
sed
implementacjami, ponieważ nie obsługują[^\n]
. W GNUsed
musisz się upewnić, że kompatybilność z POSIX nie jest włączona (jak w przypadku zmiennej środowiskowej POSIXLY_CORRECT).źródło
Nie, wyrażenia regularne sed nie mają nieprzystosowanego dopasowania.
Możesz dopasować cały tekst do pierwszego wystąpienia
AC
, używając „niczego niezawierającegoAC
”, poAC
którym następuje , co robi to samo co Perla.*?AC
. Chodzi o to, że „niczego nie zawierającegoAC
” nie można łatwo wyrazić jako wyrażenie regularne: zawsze istnieje wyrażenie regularne, które rozpoznaje negację wyrażenia regularnego, ale wyrażenie regularne negacji szybko się komplikuje. A w przenośnym sed nie jest to w ogóle możliwe, ponieważ regex negacji wymaga zgrupowania alternacji występującej w rozszerzonych wyrażeniach regularnych (np. W awk), ale nie w przenośnych podstawowych wyrażeniach regularnych. Niektóre wersje sed, takie jak GNU sed, mają rozszerzenia BRE, które umożliwiają wyrażanie wszystkich możliwych wyrażeń regularnych.Ze względu na trudność negowania wyrażenia regularnego nie uogólnia to dobrze. Zamiast tego możesz tymczasowo przekształcić linię. W niektórych implementacjach sed możesz używać znaków nowej linii jako znacznika, ponieważ nie mogą one pojawiać się w linii wejściowej (a jeśli potrzebujesz wielu znaczników, użyj nowej linii, po której następuje zmienny znak).
Uważaj jednak na to, że backslash-newline nie działa w zestawie znaków w niektórych wersjach sed. W szczególności nie działa to w GNU sed, który jest implementacją sed w niewbudowanym systemie Linux; w GNU sed możesz
\n
zamiast tego użyć :W tym konkretnym przypadku wystarczy zastąpić pierwszy
AC
nowym znakiem. Podejście, które przedstawiłem powyżej, jest bardziej ogólne.Bardziej potężnym podejściem w sed jest zapisanie linii w przestrzeni wstrzymania, usunięcie wszystkich oprócz pierwszej „interesującej” części linii, zamiana przestrzeni wstrzymania i przestrzeni wzorców lub dołączenie przestrzeni wzorców do przestrzeni wstrzymania i powtórzenie. Jeśli jednak zaczniesz robić rzeczy, które są tak skomplikowane, powinieneś naprawdę pomyśleć o przejściu na awk. Awk nie ma również chciwego dopasowania, ale możesz podzielić ciąg i zapisać części na zmienne.
źródło
s/\n//g
usuwa wszystkie nowe wiersze.sed - nie chciwe dopasowanie przez Christopha Siegharta
źródło
W twoim przypadku możesz po prostu zanegować znak zamknięcia w ten sposób:
źródło
AB
a pierwszym wystąpieniemAC
zXXX
…” i podajessABteAstACABnnACss
jako przykładowy wkład. Ta odpowiedź działa w tym przykładzie , ale ogólnie nie odpowiada na pytanie. Na przykładssABteCstACABnnACss
powinien również dać wynikaaXXXABnnACss
, ale twoje polecenie przechodzi przez ten wiersz bez zmian.Rozwiązanie jest dość proste.
.*
jest chciwy, ale nie jest absolutnie chciwy. Rozważ dopasowaniessABteAstACABnnACss
z wyrażeniem regularnymAB.*AC
.AC
Że następuje.*
powinno być rzeczywiście mecz. Problem polega na tym, że ponieważ.*
jest chciwy, kolejneAC
będą pasować do ostatniego,AC
a nie pierwszego..*
zjada pierwszy,AC
podczas gdy literałAC
w wyrażeniu regularnym pasuje do ostatniego w ssABteAstACABnn AC ss. Aby temu zapobiec, po prostu zastąp pierwszyAC
z czymś niedorzecznym, aby odróżnić go od drugiego i od wszystkiego innego.Chciwy
.*
będzie teraz zatrzymać u stóp-foobar-
wssABteAst-foobar-ABnnACss
bo nie ma innego-foobar-
niż ten-foobar-
, a regexp-foobar-
MUSI mieć mecz. Poprzedni problem polegał na tym, że wyrażenie regularneAC
miało dwa dopasowania, ale ponieważ.*
był zachłanny,AC
wybrano ostatnie dopasowanie . Jednak z-foobar-
, tylko jeden mecz jest możliwy, a ten mecz dowodzi, że.*
nie jest absolutnie chciwy. Przystanek autobusowy dla.*
występuje, gdy pozostała tylko jedna pasująca reszta wyrażeń regularnych następuje.*
.Zauważ, że to rozwiązanie zawiedzie, jeśli
AC
pojawi się przed pierwszym,AB
ponieważ niewłaściweAC
zostanie zastąpione przez-foobar-
. Na przykład po pierwszejsed
zamianieACssABteAstACABnnACss
staje się-foobar-ssABteAstACABnnACss
; dlatego nie można znaleźć dopasowania przeciwkoAB.*-foobar-
. Jednak jeśli sekwencją jest zawsze ... AB ... AC ... AB ... AC ..., to rozwiązanie się powiedzie.źródło
Jedną z możliwości jest zmiana łańcucha, aby uzyskać pożądane dopasowanie
Użyj,
rev
aby odwrócić ciąg, odwróć kryteria dopasowania, użyjsed
w zwykły sposób, a następnie odwróć wynik ....źródło