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.