Jak zapisać złożony regex do wielokrotnego ponownego użycia w sed?

12

Używając sed, często tworzę raczej skomplikowane i skomplikowane wyrażenia regularne, które muszę dwukrotnie dopasować w pliku. Czy istnieje sposób na zapisanie tego wyrażenia regularnego i odwołanie go dwa razy?

Może coś, co wygląda tak?

sed ' complicated_regex=/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+$/
s/complicated_regex:complicated_regex/simple-output/
' my_file

Aktualizacja: Odpowiedź przedstawiła rozwiązanie polegające na użyciu zmiennej bash. To nie działa Biorąc pod uwagę test.txt.

#test.txt
foo bar
bar foo

I scenariusz

#!/bin/bash

VALUE='foo \([a-z]\+\)'

sed 's/"${VALUE}"/foo happy \1/' test.txt

To powinno dać wynik

foo happy bar
bar foo

Ale zamiast tego pojawia się błąd

sed: -e expression #1, char 24: invalid reference \1 on `s' command's RHS
Cory Klein
źródło
Jeśli używasz Perla, możesz odwoływać się do poprzednich części wyrażenia w wyrażeniu:perl -pe 's/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+:\1$/simple_output/' my_file
glenn jackman 21.09.11
1
Kiedy znajdziesz coś zbyt skomplikowanego dla sed, nie wahaj się wybrać awk lub perla.
Gilles „SO- przestań być zły”,
2
@Cory: Twój przykład działa dobrze, jeśli dobrze zrozumiesz jego składnię. Wciąż masz „zmienną $” zawartą w „pojedynczych cudzysłowach”, która ukrywa ją przed rozszerzeniem powłoki, której się spodziewasz ... powinno byćsed 's/'"${VALUE}"'/foo happy \1/' test.txt
Peter.O

Odpowiedzi:

7

Możesz użyć zmiennych powłoki:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+'
sed s/^"$complicated_regex":"$complicated_regex"\$/'simple-output'/ my_file

Nie jestem pewien, co miałeś na myśli $i, ale może być konieczne umieszczenie go poza pojedynczymi cudzysłowami:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{'"$i"'}})?)+'
Stéphane Gimenez
źródło
Oczywiście działa to tylko wtedy, gdy sedpolecenie jest wywoływane z powłoki, ale istnieje podobne rozwiązanie z prawie wszystkimi językami programowania. (I nie sądzę, aby można było używać zmiennych w sed
środku
Hmm. Próbując tego, odniesienia wsteczne wydają się być zepsute. s/$complicated_regex/\1/podaje błąd informujący, że jest to nieprawidłowe odwołanie.
Cory Klein,
Ach, może moja wina, jestem przyzwyczajony do podstawiania zmiennych zsh. Zobacz zaktualizowaną odpowiedź.
Stéphane Gimenez,
Będziesz musiał usunąć kotwice ze zmiennej i umieścić je w skrypcie sed:sed "s/^${complicated_regex}:${complicated_regex}\$/simple-output/" my_file
glenn jackman
Hه! Tak, zapomniałem sprawdzić, czy otrzymałem prawidłową konkatenację wyrażenia regularnego :-)
Stéphane Gimenez
0

Najłatwiejszym sposobem na dodanie wartości zmiennej powłoki sedi nie martwienie się o to, jak ucieczka w odwrotny ukośnik będzie musiała się zmienić w pozostałej części sedskryptu, jest wypełnienie wszystkiego pojedynczymi cudzysłowami oprócz zmiennej i umieszczenie tego w cudzysłowach.

Wszystkie poniższe przykłady kodu zakładają: VALUE='foo \([a-z]\+\)'

Następujący uszkodzony kod kończy się niepowodzeniem, ponieważ zmienna VALUEnie jest rozwinięta:

sed 's/"${VALUE}"/foo happy \1/' test.txt

Poniższy uszkodzony kod kończy się niepowodzeniem, ponieważ ukośnik odwrotny \1zostaje zjedzony przez powłokę (ponieważ występuje w cudzysłowie, a nie w cudzysłowie), zanim sedgo zobaczy:

sed "s/${VALUE}/foo happy \1/" test.txt

Poniższy kod działa zgodnie z oczekiwaniami:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt

Działa również następujący kod:

sed "s/${VALUE}/foo happy \\1/" test.txt

Podobnie jest w przypadku:

sed s/"${VALUE}"/foo\ happy\ \\1/ test.txt

Ale po co się komplikować? Pojedyncze cudzysłowy wokół sedskryptu sprawiają, że wszystko jest znacznie wyraźniejsze, szczególnie dla guru nie-powłoki-skryptów, który czyta twój kod. Moim ulubionym sposobem jest zrezygnowanie z pojedynczych cudzysłowów z podwójnymi cudzysłowami tylko dla rozszerzenia zmiennej i przejście od razu do pojedynczych cudzysłowów:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt
Dzika karta
źródło