Podstawienie zmiennych środowiskowych w sed

202

Jeśli uruchomię te polecenia ze skryptu:

#my.sh
PWD=bla
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
xxx
bla

w porządku.

Ale jeśli uruchomię:

#my.sh
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
$ sed: -e expression #1, char 8: Unknown option to `s' 

Czytam w tutorialach, że aby zastąpić zmienne środowiskowe z powłoki, musisz przestać i „wycenić” tę $varnameczęść, aby nie została ona podstawiona bezpośrednio, co zrobiłem i która działa tylko wtedy, gdy zmienna jest zdefiniowana bezpośrednio wcześniej.

Jak sprawić, by sed rozpoznał $varzmienną środowiskową zdefiniowaną w powłoce?

RomanM
źródło
4
$ PWD zawiera /, które kończy polecenie zastępowania.
derobert
@derobert: tnx. Jedno z rozwiązań rozwiązuje ten problem ...
RomanM
4
Użyj set -xw powłoce, aby powłoka echa każdego polecenia tuż przed ich wykonaniem. To może rozwiązać wiele nieporozumień. (Ponadto często używam, set -uaby set -e
usunięcie
Miałem nadzieję znaleźć sposób, aby sed mógł obsługiwać zmienne środowiskowe, aby nie przeciekać wartości do tabeli procesów, wygląda na to, że sed jest niewłaściwym narzędziem do instalowania sekretów zgodnie ze wszystkimi odpowiedziami w tym wątku
ThorSummoner

Odpowiedzi:

302

Dwa przykłady wyglądają identycznie, co sprawia, że ​​problemy są trudne do zdiagnozowania. Potencjalne problemy:

  1. Możesz potrzebować podwójnych cudzysłowów, jak w sed 's/xxx/'"$PWD"'/'

  2. $PWDmoże zawierać ukośnik, w takim przypadku musisz znaleźć znak, który nie jest zawarty, $PWDaby użyć go jako separatora.

Być może do rozwiązania obu problemów naraz

sed 's@xxx@'"$PWD"'@'
Norman Ramsey
źródło
ale, jakiej postaci mogę użyć, wiem, że na pewno nie pojawi się w nazwie ścieżki?
RomanM
5
Możesz mieć kilku kandydatów, takich jak @ #%! i sprawdź wyrażenie wyrażenia, aby sprawdzić, czy $ PWD je ma. Np. „$ PWD” z @ ) ;; *) delim = "@" ;; esak; powtarzaj, dopóki $ delim nie będzie puste.
Norman Ramsey,
Istnieje inna alternatywa zamiast podwójnych cudzysłowów. Zobacz moją odpowiedź poniżej.
Thales Ceolin
Możesz użyć innego separatora dla sed. Nie musisz używać / możesz także używać, jeśli zmienną środowiskową jest adres URL.
Guchelkaben
123

Oprócz odpowiedzi Normana Ramseya, chciałbym dodać, że możesz podwójnie zacytować cały ciąg (co może sprawić, że instrukcja będzie bardziej czytelna i mniej podatna na błędy).

Więc jeśli chcesz wyszukać „foo” i zastąpić go zawartością $ BAR, możesz umieścić polecenie sed w cudzysłowie.

sed 's/foo/$BAR/g'
sed "s/foo/$BAR/g"

W pierwszym przypadku $ BAR nie rozwija się poprawnie, podczas gdy w drugim $ BAR rozwija się poprawnie.

Jeach
źródło
4
Jest to czystsze niż bałagan z podwójnymi cytatami, pojedynczymi cytatami itp.
Vladislavs Dovgalecs
oto, czego musiałem użyć, aby zmienna środowiskowa prawidłowo się rozwijała w tym poleceniu: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw
2
To miłe, ale to nie działa, gdy chcesz zrobić jakieś bardziej złożone, takie jak podstawienia "2,$s/^/$foo/"jak $szostanie zinterpretowane jako zmienna zbyt i nie powinno.
mjp,
59

W podstawieniu możesz użyć innych znaków oprócz „/”:

sed "s#$1#$2#g" -i FILE
Paulo Fidalgo
źródło
W moim konkretnym przypadku 2 $ było ścieżką do pliku, podobnie jak sedbarfing z powodu interpretacji /zawartości 2 $. Właśnie tego potrzebowałem, żeby to ominąć. Dzięki za świetną wskazówkę!
Boyd Hemphill,
54

Kolejna łatwa alternatywa:

Ponieważ $PWDzwykle będzie zawierał ukośnik /, użyj |zamiast /instrukcji sed:

sed -e "s|xxx|$PWD|"
Thales Ceolin
źródło
4
Powiedziałeś „alternatywa dla używania podwójnych cudzysłowów”, a mimo to twój przykład wykorzystuje podwójne cudzysłowy?
Jeach,
Chyba chodzi o to, że nie używa podwójnych cudzysłowów bezpośrednio wokół $PWD...?
Christian
14

Po edycji pytania widzę twój problem. Powiedzmy, że bieżącym katalogiem jest /home/yourname ... w tym przypadku twoje polecenie poniżej:

sed 's/xxx/'$PWD'/'

zostanie rozwinięty do

sed `s/xxx//home/yourname//

co nie jest poprawne. Jeśli chcesz to zrobić, musisz umieścić \znak przed każdym /w $ PWD.

Eddie
źródło
ale PWD jest zdefiniowane przez powłokę ... jeśli idę echo $ PWD dostaję pwd
RomanM
1
Jak więc uciec od zmiennych środowiskowych?
einpoklum
1
Wybrana odpowiedź opisuje obejście ... nie używaj ukośnika dla ogranicznika
Eddie
Co stanie się problemem, gdy tylko bieżący katalog roboczy będzie zawierał ten inny znak.
blubberdiblub
6

zły sposób: zmień separator

sed 's/xxx/'"$PWD"'/'
sed 's:xxx:'"$PWD"':'
sed 's@xxx@'"$PWD"'@'

może to nie jest ostateczna odpowiedź,

nie możesz wiedzieć, w której postaci wystąpi $PWD, / :LUB @.

dobrym sposobem jest zastąpienie znaku specjalnego w $PWD.

dobry sposób: separator ucieczki

na przykład: spróbuj zastąpić URLjako $ url (ma : /w treści)

x.com:80/aa/bb/aa.js

w ciągu $ tmp

<a href="URL">URL</a>

za. użyj /jako separatora

echo ${url//\//\\/}
x.com:80\/aa\/bb\/aa.js

echo ${url//\//\/}
x.com:80/aa/bb/aa.js

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js

echo $tmp | sed "s/URL/${url//\//\\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

echo $tmp | sed "s/URL/${url//\//\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

LUB

b. użyj :jako separatora (bardziej czytelny niż /)

echo ${url//:/\:}
x.com:80/aa/bb/aa.js

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js

echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>
Yurenchen
źródło
3

W rzeczywistości najprostszą rzeczą (przynajmniej w gnu sed) jest użycie innego separatora dla polecenia podstawienia sed. Więc zamiast s / pattern / '$ mypath' / rozwiniętego do s / pattern // my / path /, który oczywiście myli polecenie s, użyj s! Pattern! '$ Mypath'! który zostanie rozszerzony do s! pattern! / my / path! Użyłem znaku huku (!) (Lub czegokolwiek, co lubisz), co pozwala uniknąć zwykłego ukośnika do przodu jako separatora.

adeger
źródło
3
VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

gdzie VAR zawiera to, co chcesz zastąpić pole

PsychoData
źródło
3

Radzenie sobie ze ZMIENNYMI w sed

[root@gislab00207 ldom]# echo domainname: None > /tmp/1.txt

[root@gislab00207 ldom]# cat /tmp/1.txt

domainname: None

[root@gislab00207 ldom]# echo ${DOMAIN_NAME}

dcsw-79-98vm.us.oracle.com

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: ${DOMAIN_NAME}/g'

 --- Below is the result -- very funny.

domainname: ${DOMAIN_NAME}

 --- You need to single quote your variable like this ... 

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: '${DOMAIN_NAME}'/g'


--- The right result is below 

domainname: dcsw-79-98vm.us.oracle.com
Mamadou Lamine Diatta
źródło
Walczyłem o to, aby działało to w potokach YAML platformy Azure DevOps. Ten komentarz pomógł mi z powodzeniem użyć tej sztuczki do tokenizacji niektórych plików konfiguracyjnych.
andov
1

Miałem podobny problem, miałem listę i muszę zbudować skrypt SQL oparty na szablonie (który zawiera @INPUT@jako element do zamiany):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
aniskop
źródło