Jak mogę używać zmiennych w LHS i RHS substytucji sed?

60

Chcę zrobić:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

w moim programie.

Ale chcę użyć zmiennych, np

old_run='old_name_952'
new_run='old_name_953'

Próbowałem ich użyć, ale podstawienie się nie zdarzyło (bez błędu). Próbowałem:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'
Michael Durrant
źródło
2
Odpowiedź znajdziesz w temacie Użyj parametru w argumencie polecenia .
manatwork

Odpowiedzi:

44

Mógłbyś:

sed "s/$old_run/$new_run/" < infile > outfile

Ale uważaj, że $old_runbyłoby to traktowane jako wyrażenie regularne, a więc wszelkie znaki specjalne, które zawiera zmienna, takie jak /lub .musiałyby być poprzedzane znakami ucieczki . Podobnie, w $new_run, znaki &i \musiałyby być traktowane specjalnie, a ty musiałbyś uciec od /znaków nowej linii.

Jeśli zawartość $old_runlub $new_runnie jest pod twoją kontrolą, bardzo ważne jest, aby wykonać ucieczkę, lub w przeciwnym razie kod stanowi lukę w zabezpieczeniach polegającą na wstrzykiwaniu kodu .

Stéphane Chazelas
źródło
3
Używałem pojedynczego cudzysłowu w sed params, zmieniłem na podwójne cudzysłowy i działał plik. Niesamowite.
Aakash
to nie jest tak, że sedsam nie użyje wyrażenia regularnego, ale sed -Ezrobiłby to?
Kiwy
@Kiwy, no. -ejest określenie sed e Xpression , więc można przekazać więcej niż jeden (jak w sed -e exp1 -e exp2) lub kombinacje plików i wyrażeń ( sed -f file -e exp). Być może mylisz się z tym, -Eco z niektórymi implementacjami, mówi sedowi, aby używał ERE zamiast BRE.
Stéphane Chazelas
Źle wpisałem, ale OK, to wyjaśnia, dlaczego mam tak wiele problemów z używaniem sed bez -Eopanowania tylko rozszerzonego wyrażenia regularnego, a nie zwykłego. Dzięki za wyjaśnienie.
Kiwy
@ Kiwi, zobacz także powiązane pytania i odpowiedzi, aby uzyskać więcej opcji obsługiwanych przez niektóre sedimplementacje dla jeszcze większej składni wyrażeń regularnych.
Stéphane Chazelas
31

To działało:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

Ponieważ chcę „ponownie wykorzystać” ten sam plik, faktycznie używam tego dla każdego, kto chce mieć podobne podejście:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

Powyższe utworzyło nowy plik, a następnie usuwa bieżący plik, a następnie zmienia nazwę nowego pliku na bieżący.

Michael Durrant
źródło
4
Nie potrzebujesz kota, mv ani dodatkowych '', chyba że masz w nich specjalne znaki. Więc to jest sed -i s/"$old"/"$new"/ update_via_sed.sh. Należy jednak pamiętać, że nie działa to dla żadnej wartości starej i nowej. np. nie może zawierać / itp., ponieważ $ old jest nadal wyszukiwaniem, a $ new wzór zastępczy, w którym niektóre znaki mają specjalne znaczenie.
frostschutz
1
sed -i "s/$old/$new/"jest również w porządku (z powyższymi środkami ostrożności). sedzwykle uważa, że ​​separator jest pierwszym znakiem po spoleceniu - tzn. jeśli twoje zmienne zawierają ukośniki, /ale nie pozwalają na powiedzmy w ( @), możesz użyć „s @ $ old @ $ new @”.
Peter
22

Możesz używać zmiennych w sed, o ile są one w podwójnym cudzysłowie (nie w jednym cudzysłowie):

sed "s/$var/r_str/g" file_name >new_file

Jeśli masz /zmienną ukośnik ( ) w zmiennej, użyj innego separatora, jak poniżej

sed "s|$var|r_str|g" file_name >new_file
mani
źródło
1
+1 za wzmiankę o konieczności stosowania podwójnych cudzysłowów. To był mój problem.
speckledcarp
1
Dokładnie to, czego szukałem, w tym użycie |jak w moim przypadku, zmienne zawierają ścieżkę, więc ona ma /w sobie
rok
Z niektórych powodów potok |działał dla mnie w systemie MacOS 10.14, ale inni separatorzy lubią @lub #nie. Miałem też ukośniki w env var.
davidenke
21

odpowiedzią był „in-place” sed (użycie flagi -i). Dzięki Peterowi.

sed -i "s@$old@$new@" file
Michael Durrant
źródło
2
Masz cytaty w niewłaściwym miejscu. cudzysłowy muszą znajdować się wokół zmiennych, a nie s@(co w żaden sposób nie jest wyjątkowe dla powłoki). Najlepiej jest umieścić wszystko w cudzysłowie sed -i "s@$old@$new@" file. Pamiętaj, że -inie jest to standardowa sedopcja.
Stéphane Chazelas
jak mogę uciec $w tym poleceniu. Podobnie, zamierzam zmienić $xxxjako całość wartość zmiennej $JAVA_HOME. Próbowałem sed -i "s@\$xxx@$JAVA_HOME@" file, ale błąd mówino previous regular expression
hakunami
3

man bash daje to o pojedynczym cytowaniu

Umieszczanie znaków w pojedynczych cudzysłowach zachowuje dosłowną wartość każdego znaku w cudzysłowach. Pojedynczy cytat może nie wystąpić między pojedynczymi cudzysłowami, nawet jeśli poprzedzony jest odwrotnym ukośnikiem.

Cokolwiek wpiszesz w wierszu poleceń, bash interpretuje to, a następnie wysyła wynik do programu, do którego ma zostać wysłany. W tym przypadku, jeśli użyjesz sed 's/$old_run/$new_run/', bash najpierw zobaczy sed, rozpoznaje go jako plik wykonywalny obecny w $PATHzmiennej . Plik sedwykonywalny wymaga danych wejściowych. Bash szuka danych wejściowych i znajduje 's/$old_run/$new_run/'. Pojedyncze cytaty mówią bash, aby nie interpretować zawartych w nich treści i przekazać je takimi, jakie są. Więc bash następnie podaje je sed. Sed podaje błąd, ponieważ $może wystąpić tylko na końcu linii.

Zamiast tego, jeśli użyjemy podwójnych cudzysłowów, tj. "s/$old_run/$new_run/"Wtedy bash widzi to i interpretuje $old_runjako nazwę zmiennej i dokonuje podstawienia (ta faza nazywana jest rozszerzaniem zmiennej). Właśnie tego wymagaliśmy.

Ale musisz zachować ostrożność, używając podwójnych cudzysłowów, ponieważ są one interpretowane najpierw przez bash, a następnie podawane do sed. Dlatego niektóre symbole, takie jak `, muszą być poprzedzone znakami ucieczki.

nitishch
źródło
1

Ryzykujesz nieposłuszeństwo - czy zastanawiałeś się perl.

Co - między innymi - jest całkiem dobre w wykonywaniu operacji w stylu „sed”, ale także w pełni funkcjonalnym zagadnieniu programistycznym.

To wygląda jak w przykładzie, co ty właściwie próbuje zrobić, to zwiększyć liczbę.

Co powiesz na:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

lub jako jedna wkładka - styl sed:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'
Sobrique
źródło
1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

W $d1przechowuję wartości

Nie mogę zastąpić wartości zmiennej, więc wypróbowałem następujące polecenie, które działa

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

brak podwójnego cudzysłowu w "${d1}"tym był błąd

deva
źródło
-2

Założono $old_runi $new_runsą już ustawione:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

Pokazuje zmianę na ekranie. W razie potrzeby możesz przekierować dane wyjściowe do pliku.

Orlando
źródło
-2

Aby użyć zmiennej w sed, użyj podwójnych cudzysłowów „”, aby zamknąć zmienną we wzorcu, którego używasz. Na przykład: a = „Cześć” Jeśli chcesz dołączyć zmienną „a” do wzoru, możesz użyć poniższego: sed -n „1, / pattern” $ {a} „pattern / p” nazwa pliku

Sher
źródło
2
Rozwinięcie $anie jest tutaj cytowane, co oznacza, że ​​wszelkie spacje w jego wartości będą wywoływały dzielenie słów w powłoce.
Kusalananda
-2

Możesz także użyć Perla:

perl -pi -e ""s/$val1/$val2/g"" file
Samba
źródło
Rozważ zmienne, które mogą mieć spacje w swoich wartościach. Puste łańcuchy wokół wyrażenia Perla ( "") nic nie zrobią.
Kusalananda
-2

przerwać ciąg

bad => linux zobacz ciąg $ old_run:

sed 's/$old_run/$new_run/'

good => linux zobacz zmienną $ old_run:

sed 's/'$old_run'/'$new_run'/'
marcdahan
źródło
1
A jeśli $old_runlub $new_runzawiera spacje?
Kusalananda