Jak przekazać zmienną zawierającą ukośniki do seda

100

Jak przekazać zmienną zawierającą ukośniki jako wzorzec do sed?

Na przykład, jeśli mam następującą zmienną:

var="/Users/Documents/name/file"

Chcę to przekazać sed tak:

sed "s/$var/replace/g" "$file"

Jednak otrzymuję błędy. Jak mogę obejść ten problem?

buydadip
źródło

Odpowiedzi:

193

Użyj alternatywnego separatora wyrażeń regularnych, co sedpozwala na użycie dowolnego separatora (w tym znaków kontrolnych ):

sed "s~$var~replace~g" $file
anubhava
źródło
2
Nie działa. Próbuję sed -i "s ~ blah ~ $ var ~ g plik" i otrzymuję: "sed -e wyrażenie # 1, char 70: niezakończona komenda` s '. Potwierdziłem, że winowajcą jest ukośnik w $ var. Warianty tego rozwiązania są wszędzie i żadne z nich nie działa. Czy sed został niedawno zaktualizowany? Jestem na wersji 4.2.2 na Ubuntu 14.04.4 LTS.
Paul Ericson
To zależy od tego, jaka jest wartość$var
anubhava,
1
Nie działa z ~, jak zauważył @PaulEricson, ale nadal działa z |
Kenny Ho
1
Ostatnie „~” (po g) wydaje się niepotrzebne, przynajmniej w przypadku AWS Amazon Linux (opartego na CentOS)
Saúl Martínez Vidals
1
Uratowałeś mi dzień
user7131571
62

Czysta odpowiedź basha: użyj interpretacji parametrów, aby odwrócić ukośnik - uciec przed wszystkimi ukośnikami w zmiennej:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
glenn jackman
źródło
1
To dobry sposób, ponieważ nie zawsze wiesz, jakie znaki zawierają zmienną. W razie wątpliwości zawsze możesz uciec od ogranicznika sed.Na przykład: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Piękny. Sprawiłem, że niektóre moje skrypty zmieniające nazwy są znacznie czystsze.
Cloud
Nauczyłem się dziś czegoś pięknego, dziękuję. Powinna być zaakceptowana odpowiedź.
Steve Kehlet
6
Niektóre jasność na deseń używany tutaj: ${parameter/pattern/string}. W tym przypadku parametr to var, wzorzec to /\/, a ciąg to \\/. Wszystkie wystąpienia wzorca są zastępowane, ponieważ wzór zaczyna się od /.
Josh
1
@josh, więc początkowy ukośnik wzoru nie jest w rzeczywistości częścią wzorca do zastąpienia.
glenn jackman
32

Innym sposobem na zrobienie tego, chociaż brzydszym niż odpowiedź Anubhavy, jest uniknięcie wszystkich odwrotnych ukośników varza pomocą innego sedpolecenia:

var=$(echo "$var" | sed 's/\//\\\//g')

wtedy to zadziała:

sed "s/$var/replace/g" $file
buydadip
źródło
12

Użycie /w sedzie jako separatora spowoduje konflikt z ukośnikami w zmiennej podczas podstawiania, w wyniku czego pojawi się błąd. Jednym ze sposobów obejścia tego jest użycie innego ogranicznika, który jest unikalny dla wszystkich znaków znajdujących się w tej zmiennej.

var="/Users/Documents/name/file"

możesz użyć znaku octothorpe, który pasuje do okazji (lub dowolnego innego znaku, który nie jest /łatwy w użyciu)

sed "s#$var#replace#g" 

lub

sed 's#$'$var'#replace#g'

jest to odpowiednie, gdy zmienna nie zawiera spacji

lub

sed 's#$"'$var'"#replace#g'

Rozsądniej jest skorzystać z powyższego, ponieważ jesteśmy zainteresowani zastąpieniem tego, co jest w tej zmiennej, tylko w porównaniu z podwójnym cudzysłowem całego polecenia, co może spowodować, że powłoka interpetuje dowolny znak, który może być uznany za specjalny znak powłoki.

repzero
źródło
Nie działa. Próbuję sed -i "s ~ blah ~ $ var ~ g plik" i otrzymuję: "sed -e wyrażenie # 1, char 70: niezakończona komenda` s '. Potwierdziłem, że winowajcą jest ukośnik w $ var. Warianty tego rozwiązania są wszędzie i żadne z nich nie działa. Czy sed został niedawno zaktualizowany? Jestem na wersji 4.2.2 na Ubuntu 14.04.4 LTS.
Paul Ericson
@PaulEricson Nie mogę tego powtórzyć na żadnym dostępnym dla mnie Ubuntu. Jeśli $varzawiera ~, oczywiście będziesz musiał wybrać inny separator. Błąd „niezakończony” brzmi tak, jakby twój $varzawierał nową linię; możesz to naprawić, usuwając odwrotnym ukośnikiem każdą nową linię w wartości, ale nie sądzę, że jest to całkowicie przenośne. W zasadzie nie ma to związku z problemem cięć wartości.
tripleee
@PaulEricson Aha, cytat jest nieprawidłowy, nie powinieneś umieszczać nazwy pliku w cudzysłowie. sed -i "s~blah~$var~g" filez cudzysłowami tylko wokół właściwego sedskryptu.
tripleee
5

To stare pytanie, ale żadna z odpowiedzi nie omawia operacji poza s/from/to/bardzo szczegółowymi informacjami.

Ogólna forma sedoświadczenia to

*address* *action*

gdzie adres może być zakresem wyrażenia regularnego lub zakresem numerów wierszy (lub pustym, w którym to przypadku akcja jest stosowana do każdego wiersza wejściowego). Na przykład

sed '1,4d' file

usunie linie od 1 do 4 ( adres to zakres numerów linii, 1,4a czynnością jest dpolecenie usunięcia); i

sed '/ick/,$s/foo/bar/' file

zastąpi pierwsze wystąpienie fooze barna każdej linii pomiędzy pierwszym meczu na regex icki końca pliku (the adres jest zasięg /ick/,$i działanie jest skomenda substytutems/foo/bar/ ).

W tym kontekście, jeśli ickpochodzi ze zmiennej, możesz to zrobić

sed "/$variable/,\$s/foo/bar/"

(zwróć uwagę na użycie podwójnych cudzysłowów zamiast pojedynczych, aby powłoka mogła interpolować zmienną i konieczność cytowania dosłownego znaku dolara wewnątrz podwójnych cudzysłowów), ale jeśli zmienna zawiera ukośnik, pojawi się błąd składniowy. (Powłoka rozwija zmienną, a następnie przekazuje wynikowy ciąg do sed; więc sedwidzi tylko tekst dosłowny - nie ma pojęcia o zmiennych powłoki).

Lekarstwem jest użycie innego separatora (gdzie oczywiście musisz mieć możliwość użycia znaku, który nie może występować w wartości zmiennej), ale w przeciwieństwie do s%foo%bar%przypadku, potrzebujesz również ukośnika odwrotnego przed separatorem, jeśli chcesz użyć innego separator niż domyślny /:

sed "\\%$variable%,\$s/foo/bar/" file

(w pojedynczych cudzysłowach wystarczyłby oczywiście pojedynczy lewy ukośnik); lub możesz osobno zmienić znaczenie każdego ukośnika w wartości. Ta konkretna składnia to tylko Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

lub jeśli używasz innej powłoki, spróbuj

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Dla jasności, gdyby $variablezawierał łańcuch, 1/2powyższe polecenia byłyby równoważne

sed '\%1/2%,$s/foo/bar/' file

w pierwszym przypadku i

sed '/1\/2/,$s/foo/bar/' file

w sekundę.

tripleee
źródło
4

Użyj Perla, gdzie zmienne są obywatelami pierwszej klasy, a nie tylko rozwijaniem makr:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p czyta dane wejściowe wiersz po wierszu i drukuje wiersz po przetworzeniu
  • \Qcytuje wszystkie metaznaki w następującym ciągu (nie są potrzebne dla wartości tutaj prezentowanej, ale konieczne, jeśli wartość zawierała [lub inne wartości specjalne dla regularnych wyrażeń)
choroba
źródło
Jedyna ogólnie użyteczna odpowiedź na dziesiątki pytań dotyczących „używania zmiennych w wyrażeniu sed” w ramach wymiany stosów. Wszystko inne skutecznie „unika wszystkiego, co jest szczególne dla seda”, co jest praktycznie bezużyteczne, biorąc pod uwagę zmienność zmiennych i sed.
Heath Raftery