Sustitución de variables de entorno en sed


202

Si ejecuto estos comandos desde un script:

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

está bien.

Pero si corro:

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

Leí en los tutoriales que para sustituir las variables de entorno del shell, debe detenerse y 'citar' la $varnameparte para que no se sustituya directamente, que es lo que hice, y que funciona solo si la variable se define inmediatamente antes.

¿Cómo puedo hacer que sed reconozca a $varcomo una variable de entorno tal como se define en el shell?


44
$ PWD contiene un / que está finalizando el comando sustituto.
derobert

@derobert: tnx. Una de las soluciones aborda esto ...
RomanM

44
Use set -xen el shell para que el shell haga eco de cada comando justo antes de que los ejecute. Esto puede aclarar mucha confusión. (Además, a menudo uso set -upara hacer que la desreferenciación de las variables no definidas sea un error difícil. (Ver set -etambién.))
Bobbogo

Esperaba encontrar una forma para que sed manejara las variables de entorno para no filtrar los valores en la tabla de procesos, parece que sed es la herramienta incorrecta para instalar secretos de acuerdo con todas las respuestas en este hilo
ThorSummoner

Respuestas:


302

Sus dos ejemplos parecen idénticos, lo que hace que los problemas sean difíciles de diagnosticar. Problemas potenciales:

  1. Es posible que necesite comillas dobles, como en sed 's/xxx/'"$PWD"'/'

  2. $PWDpuede contener una barra oblicua, en cuyo caso debe buscar un carácter que no esté en$PWD para usarlo como delimitador.

Para resolver ambos problemas a la vez, tal vez

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

pero, ¿qué personaje puedo usar? Sé que no aparecerá en el nombre de la ruta.
RomanM

55
¡Puedes tener varios candidatos como @ #%! y verifique con una expresión de caso para encontrar si $ PWD los tiene. Por ejemplo, el caso "$ PWD" de @ ) ;; *) delim = "@" ;; esac; repita hasta que $ delim no esté vacío.
Norman Ramsey

Hay otra alternativa en lugar de usar comillas dobles. Vea mi respuesta a continuación.
Thales Ceolin

Puede usar otro delimitador para sed. No necesita usar / puede usar, también si su variable de entorno es una url.
Guchelkaben

123

Además de la respuesta de Norman Ramsey, me gustaría agregar que puede comillas dobles la cadena completa (lo que puede hacer que la declaración sea más legible y menos propensa a errores).

Entonces, si desea buscar 'foo' y reemplazarlo con el contenido de $ BAR, puede encerrar el comando sed entre comillas dobles.

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

En el primero, $ BAR no se expandirá correctamente, mientras que en el segundo $ BAR se expandirá correctamente.


44
Esto es más limpio que jugar con comillas dobles, comillas simples, etc.
Vladislavs Dovgalecs

esto es lo que tuve que usar para que la variable de entorno se expandiera correctamente en este comando: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw

2
Esto es bueno, pero no funciona cuando desea realizar algunas sustituciones más complejas, como que "2,$s/^/$foo/"también $sse interpreta como una variable y no debería.
mjp

59

Puede usar otros caracteres además de "/" en sustitución:

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

En mi caso específico, $ 2 era una ruta de acceso de archivo, por lo que sedera un fastidio debido a la interpretación /de los contenidos de $ 2. Esto era exactamente lo que necesitaba para superarlo. Gracias por un gran consejo!
Boyd Hemphill

54

Otra alternativa fácil:

Como $PWDgeneralmente contendrá una barra oblicua /, use en |lugar de /para la declaración sed:

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

44
¿Dijiste "una alternativa al uso de comillas dobles" y aún así tu ejemplo usa comillas dobles?
Jeach

Supongo que el punto es que no usa comillas dobles directamente alrededor $PWD...
Christian

14

Con la edición de su pregunta, veo su problema. Digamos que el directorio actual es /home/yourname ... en este caso, su comando a continuación:

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

se ampliará a

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

Que no es válido. Necesitas poner un \personaje delante de cada uno /en tu $ PWD si quieres hacer esto.


pero PWD está definido por el shell ... si voy echo $ PWD obtengo el pwd
RomanM

1
Entonces, ¿cómo escapas de las variables de entorno?
einpoklum

1
La respuesta seleccionada describe una solución alternativa ... no use la barra diagonal para el delimitador
Eddie

Lo que se convertirá en un problema tan pronto como su directorio de trabajo actual contenga ese otro carácter.
blubberdiblub

6

mal camino: cambiar delimitador

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

tal vez esa no sea la respuesta final,

no puede saber en qué personaje se producirá $PWD, / :OR @.

La buena manera es reemplazar el carácter especial en $PWD.

buena manera: delimitador de escape

por ejemplo: intente reemplazar URLcomo $ url (tiene : /contenido)

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

en cadena $ tmp

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

a. utilizar /como delimitador

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

si. utilizar :como delimitador (más legible 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>

3

En realidad, lo más simple (al menos en gnu sed) es usar un separador diferente para el comando de sustitución (es) de sed. Entonces, en lugar de que s / pattern / '$ mypath' / se expanda a s / pattern // my / path / que, por supuesto, confundirá el comando s, ¡use s! Pattern! '$ Mypath'! que se expandirá a s! pattern! / my / path! He usado el carácter de explosión (!) (O uso cualquier cosa que desee) que evita la barra diagonal habitual, pero de ninguna manera su única opción como separador.


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

donde VAR contiene lo que desea reemplazar el campo con


3

Manejo de VARIABLES dentro de 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

Estaba luchando para que esto funcionara en las canalizaciones Azure DevOps YAML. Este comentario me ayudó a utilizar con éxito este truco para tokenizar algunos archivos de configuración.
andov

1

Tuve un problema similar, tenía una lista y tengo que construir un script SQL basado en una plantilla (que contenía @INPUT@como elemento para reemplazar):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.