Sostituzione variabile d'ambiente in sed


202

Se eseguo questi comandi da uno script:

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

va bene.

Ma, se corro:

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

Ho letto nei tutorial che per sostituire le variabili di ambiente dalla shell è necessario interrompere e "citare" la $varnameparte in modo che non venga sostituita direttamente, cosa che ho fatto e che funziona solo se la variabile è stata definita immediatamente prima.

Come posso convincere sed a riconoscere una $varvariabile d'ambiente come definita nella shell?


4
$ PWD contiene un / che sta terminando il comando sostitutivo.
derobert,

@derobert: tnx. Una delle soluzioni si rivolge a questo ...
RomanM,

4
Utilizzare set -xnella shell per fare in modo che la shell esegua l'eco di ciascun comando appena prima di eseguirli. Questo può chiarire molta confusione. (Inoltre, uso spesso set -uper rendere de-referencing variabili unset un errore set -e
grave

Speravo di trovare un modo per sed di gestire le variabili di ambiente in modo da non perdere i valori nella tabella dei processi, sembra che sed sia lo strumento sbagliato per installare i segreti in base a tutte le risposte in questo thread
ThorSummoner

Risposte:


302

I tuoi due esempi sembrano identici, il che rende difficile diagnosticare i problemi. Potenziali problemi:

  1. Potresti aver bisogno di virgolette doppie, come in sed 's/xxx/'"$PWD"'/'

  2. $PWDpuò contenere una barra, nel qual caso è necessario trovare un carattere non contenuto $PWDda utilizzare come delimitatore.

Per risolvere entrambi i problemi contemporaneamente, forse

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

ma quale personaggio posso usare so per certo che non comparirà nel nome di un percorso?
RomanM,

5
Puoi avere diversi candidati come @ #%! e controlla con un'espressione case per scoprire se $ PWD li ha. Ad esempio, il caso "$ PWD" di @ ) ;; *) delim = "@" ;; esac; ripetere fino a quando $ delim non è vuoto.
Norman Ramsey,

C'è un'altra alternativa invece di usare le doppie virgolette. Vedi la mia risposta qui sotto.
Thales Ceolin,

Puoi usare un altro delimitatore per sed. Non è necessario utilizzare / è possibile utilizzare, anche se la variabile di ambiente è un URL.
Guchelkaben,

123

Oltre alla risposta di Norman Ramsey, vorrei aggiungere che puoi fare una doppia citazione dell'intera stringa (il che può rendere l'istruzione più leggibile e meno soggetta a errori).

Quindi se vuoi cercare 'pippo' e sostituirlo con il contenuto di $ BAR, puoi racchiudere il comando sed tra virgolette.

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

Nel primo, $ BAR non si espanderà correttamente mentre nel secondo $ BAR si espanderà correttamente.


4
Questo è più pulito del pasticcio con doppie virgolette, virgolette singole ecc.
Vladislavs Dovgalecs

questo è quello che ho dovuto usare per ottenere l'espansione corretta della variabile d'ambiente in questo comando: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw

2
Questo è pulito ma non funziona quando si desidera effettuare alcune sostituzioni più complesse, come ad "2,$s/^/$foo/"esempio $sviene interpretato come una variabile e non dovrebbe.
mjp,

59

È possibile utilizzare altri caratteri oltre a "/" in sostituzione:

sed "s#$1#$2#g" -i FILE

Nel mio caso specifico, $ 2 era un percorso di file, così come lo sedera il barf a causa dell'interpretazione /del contenuto di $ 2. Questo era esattamente ciò di cui avevo bisogno per superarlo. Grazie per un ottimo consiglio!
Boyd Hemphill,

54

Un'altra alternativa semplice:

Poiché di $PWDsolito conterrà una barra /, utilizzare |invece di /per l'istruzione sed:

sed -e "s|xxx|$PWD|"

4
Hai detto "un'alternativa all'utilizzo delle virgolette doppie" eppure il tuo esempio usa le virgolette doppie?
Jeach,

Immagino che non usi virgolette direttamente in giro $PWD...?
Christian,

14

Con la tua domanda di modifica, vedo il tuo problema. Diciamo che la directory corrente è /home/yourname ... in questo caso, il tuo comando qui sotto:

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

sarà espanso a

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

che non è valido. Devi mettere un \personaggio davanti a ciascuno /nel tuo $ PWD se vuoi farlo.


ma PWD è definito dalla shell ... se vado echo $ PWD ottengo il pwd
RomanM

1
Quindi, come sfuggire alle variabili di ambiente?
einpoklum,

1
La risposta selezionata descrive una soluzione alternativa ... non usare la barra per il delimitatore
Eddie,

Il che diventerà un problema non appena la directory di lavoro corrente contiene quell'altro carattere.
blubberdiblub,

6

brutta strada: cambia delimitatore

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

forse quelli non sono la risposta finale,

non puoi sapere in quale personaggio si verificherà $PWD, / :OR@ .

il buon modo è sostituire il carattere speciale in $PWD .

buon modo: sfuggire al delimitatore

ad esempio: prova a sostituire URLcome $ url (ha : /nel contenuto)

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

nella stringa $ tmp

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

un. usare /come delimitatore

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>

O

b. usa :come delimitatore (più leggibile di /)

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>

3

In realtà, la cosa più semplice (almeno in gnu sed) è usare un separatore diverso per il comando di sostituzione sed. Quindi invece di s / pattern / '$ mypath' / essere espanso in s / pattern // my / path / che ovviamente confonderà il comando s, usa s! Pattern! '$ Mypath'! che verrà espanso in s! pattern! / my / path! Ho usato il carattere bang (!) (O usare qualsiasi cosa ti piaccia) che eviti la solita barra di separazione scelta come separatore.


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

dove VAR contiene ciò con cui si desidera sostituire il campo


3

Trattare con VARIABILI all'interno di 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

Stavo lottando per farlo funzionare nelle pipeline di Azure DevOps YAML. Questo commento mi ha aiutato a utilizzare con successo questo trucco per tokenizzare alcuni file di configurazione.
andov

1

Ho avuto un problema simile, avevo un elenco e devo costruire uno script SQL basato sul modello (che conteneva @INPUT@come elemento da sostituire):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.