Substitution de variables d'environnement dans sed

202

Si j'exécute ces commandes à partir d'un script:

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

c'est bon.

Mais si je cours:

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

J'ai lu dans des tutoriels que pour substituer des variables d'environnement à partir du shell, vous devez arrêter et `` citer '' la $varnamepartie afin qu'elle ne soit pas substituée directement, ce que j'ai fait et qui ne fonctionne que si la variable est définie immédiatement avant.

Comment puis-je obtenir sed pour reconnaître une $varvariable d'environnement comme il est défini dans le shell?

RomanM
la source
4
$ PWD contient un / qui termine la commande de substitution.
derobert
@derobert: tnx. Une des solutions résout ce problème ...
RomanM
4
Utilisez set -xdans le shell pour que le shell fasse écho à chaque commande juste avant de les exécuter. Cela peut dissiper beaucoup de confusion. (En outre, j'utilise souvent set -upour faire du déréférencement des variables non définies une erreur difficile. (Voir set -eaussi.))
bobbogo
J'espérais trouver un moyen pour sed de gérer les variables d'environnement afin de ne pas divulguer les valeurs dans la table de processus, il semble que sed soit le mauvais outil pour installer des secrets selon toutes les réponses de ce fil
ThorSummoner

Réponses:

302

Vos deux exemples semblent identiques, ce qui rend les problèmes difficiles à diagnostiquer. Problèmes potentiels:

  1. Vous devrez peut-être des guillemets doubles, comme dans sed 's/xxx/'"$PWD"'/'

  2. $PWDpeut contenir une barre oblique, auquel cas vous devez trouver un caractère non contenu dans $PWDpour l'utiliser comme délimiteur.

Pour résoudre les deux problèmes à la fois, peut-être

sed 's@xxx@'"$PWD"'@'
Norman Ramsey
la source
mais alors quel caractère puis-je utiliser je sais que n'apparaîtra pas dans un nom de chemin?
RomanM
5
Vous pouvez avoir plusieurs candidats comme @ #%! et vérifiez avec une expression de casse pour savoir si $ PWD en a. Par exemple, cas "$ PWD" de @ ) ;; *) delim = "@" ;; esac; répéter jusqu'à ce que $ delim ne soit pas vide.
Norman Ramsey
Il existe une autre alternative au lieu d'utiliser des guillemets doubles. Voir ma réponse ci-dessous.
Thales Ceolin
Vous pouvez utiliser un autre délimiteur pour sed. Vous n'avez pas besoin d'utiliser / vous pouvez également utiliser, si votre variable d'environnement est une URL.
Guchelkaben
123

En plus de la réponse de Norman Ramsey, je voudrais ajouter que vous pouvez citer deux fois la chaîne entière (ce qui peut rendre l'instruction plus lisible et moins sujette aux erreurs).

Donc, si vous voulez rechercher 'foo' et le remplacer par le contenu de $ BAR, vous pouvez mettre la commande sed entre guillemets.

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

Dans le premier, $ BAR ne se développera pas correctement tandis que dans le second $ BAR se développera correctement.

Jeach
la source
4
C'est plus propre que de jouer avec les guillemets doubles, les guillemets simples, etc.
Vladislavs Dovgalecs
voici ce que j'ai dû utiliser pour que la variable d'environnement se développe correctement dans cette commande: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw
2
C'est bien, mais cela ne fonctionne pas lorsque vous souhaitez effectuer des substitutions plus complexes, telles "2,$s/^/$foo/"que $sobtient également interprété comme une variable et cela ne devrait pas.
mjp
59

Vous pouvez utiliser d'autres caractères que "/" en remplacement:

sed "s#$1#$2#g" -i FILE
Paulo Fidalgo
la source
Dans mon cas spécifique, $ 2 était un chemin de fichier, tout sedcomme le barfing en raison de l'interprétation /du contenu de $ 2. C'était exactement ce dont j'avais besoin pour le dépasser. Merci pour un bon conseil!
Boyd Hemphill du
54

Une autre alternative simple:

Puisque $PWDcontiendra généralement une barre oblique /, utilisez |plutôt que /pour l'instruction sed:

sed -e "s|xxx|$PWD|"
Thales Ceolin
la source
4
Vous avez dit "une alternative à l'utilisation de guillemets doubles" et pourtant votre exemple utilise des guillemets doubles?
Jeach
Je suppose que le fait est qu'il n'utilise pas de guillemets directement autour de $PWD...?
Christian
14

Avec la modification de votre question, je vois votre problème. Disons que le répertoire courant est /home/yourname ... dans ce cas, votre commande ci-dessous:

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

sera étendu à

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

ce qui n'est pas valable. Vous devez mettre un \caractère devant chacun /dans votre $ PWD si vous voulez le faire.

Eddie
la source
mais PWD est défini par le shell ... si je vais echo $ PWD j'obtiens le pwd
RomanM
1
Alors, comment échappez-vous aux variables d'environnement?
einpoklum
1
La réponse sélectionnée décrit une solution de contournement ... n'utilisez pas de barre oblique pour le délimiteur
Eddie
Ce qui deviendra un problème dès que votre répertoire de travail actuel contiendra cet autre caractère.
blubberdiblub
6

mauvaise façon: changer le délimiteur

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

peut-être que ce n'est pas la réponse finale,

vous ne pouvez pas savoir dans quel personnage apparaîtra $PWD, / :OU @.

la bonne façon est de remplacer le caractère spécial dans $PWD.

bonne façon: échapper le délimiteur

par exemple: essayez de remplacer URLcomme $ url (a : /dans le contenu)

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

dans la chaîne $ tmp

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

une. utiliser /comme délimiteur

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>

OU

b. utiliser :comme délimiteur (plus lisible que /)

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
la source
3

En fait, la chose la plus simple (dans gnu sed au moins) est d'utiliser un séparateur différent pour la commande sed substitution (s). Ainsi, au lieu que s / pattern / '$ mypath' / soit développé en s / pattern // my / path / qui confondra bien sûr la commande s, utilisez s! Pattern! '$ Mypath'! qui sera étendu à s! pattern! / my / path! J'ai utilisé le caractère bang (!) (Ou utilisez tout ce que vous voulez) qui évite la barre oblique habituelle, mais en aucun cas votre seul choix comme séparateur.

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

où VAR contient ce que vous souhaitez remplacer le champ par

PsychoData
la source
3

Gérer les VARIABLES dans 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
la source
J'avais du mal à faire fonctionner cela dans les pipelines Azure DevOps YAML. Ce commentaire m'a aidé à utiliser avec succès cette astuce pour symboliser certains fichiers de configuration.
andov
1

J'ai eu un problème similaire, j'avais une liste et je dois construire un script SQL basé sur un modèle (qui contenait @INPUT@comme élément à remplacer):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
aniskop
la source