¿Cómo reemplazar $ {} marcadores de posición en un archivo de texto?

Quiero canalizar el resultado de un archivo de “plantilla” en MySQL, el archivo tiene variables como ${dbName} intercaladas. ¿Cuál es la utilidad de línea de comandos para reemplazar estas instancias y volcar la salida a la salida estándar?

Sed !

Dado template.txt:

 El número es $ {i}
 La palabra es $ {word}

solo tenemos que decir:

 sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt 

Gracias a Jonathan Leffler por la sugerencia de pasar múltiples argumentos -e a la misma invocación de sed .

Actualizar

Aquí hay una solución de yottatsa sobre una pregunta similar que solo hace el reemplazo de variables como $ VAR o $ {VAR}, y es un breve resumen

 i=32 word=foo envsubst < template.txt 

Por supuesto, si yo y la palabra están en su entorno, entonces es solo

 envsubst < template.txt 

En mi Mac, parece que se instaló como parte de gettext y de MacGPG2

Vieja respuesta

Aquí hay una mejora de la solución de Mogsie en una pregunta similar, mi solución no requiere que escales las comillas dobles, la de Mogsie, ¡pero la suya es un trazador!

 eval "cat < /dev/null 

El poder de estas dos soluciones es que solo se obtienen unos pocos tipos de expansiones de shell que normalmente no ocurren $ ((...)), `...` y $ (...), aunque la barra invertida es una Escape carácter aquí, pero no tienes que preocuparte de que el análisis tiene un error, y hace varias líneas muy bien.

Usa /bin/sh . Cree un pequeño script de shell que establezca las variables y luego analice la plantilla utilizando el propio shell. Al igual que (edición para manejar nuevas líneas correctamente):

Archivo template.txt:

 the number is ${i} the word is ${word} 

Archivo script.sh:

 #!/bin/sh #Set variables i=1 word="dog" #Read in template one line at the time, and replace variables (more #natural (and efficient) way, thanks to Jonathan Leffler). while read line do eval echo "$line" done < "./template.txt" 

Salida:

 #sh script.sh the number is 1 the word is dog 

Estaba pensando en esto de nuevo, dado el reciente interés, y creo que la herramienta en la que originalmente estaba pensando era m4 , el macroprocesador para autotools. Entonces, en lugar de la variable que originalmente especifiqué, usarías:

 $echo 'I am a DBNAME' | m4 -DDBNAME="database name" 

template.txt

 Variable 1 value: ${var1} Variable 2 value: ${var2} 

data.sh

 #!/usr/bin/env bash declare var1="value 1" declare var2="value 2" 

parser.sh

 #!/usr/bin/env bash # args declare file_data=$1 declare file_input=$2 declare file_output=$3 source $file_data eval "echo \"$(< $file_input)\"" > $file_output 

./parser.sh data.sh template.txt parsed_file.txt

parsed_file.txt

 Variable 1 value: value 1 Variable 2 value: value 2 

esta es mi solución con Perl basada en la respuesta anterior, reemplaza las variables de entorno:

 perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile 

Si está abierto a usar Perl , esa sería mi sugerencia. Aunque es probable que haya algunos expertos sed y / o AWK que probablemente saben cómo hacer esto mucho más fácil. Si tiene un mapeo más complejo con algo más que solo dbName para sus reemplazos, podría ampliarlo con bastante facilidad, pero podría colocarlo en un script de Perl estándar en ese punto.

 perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql 

Un script corto de Perl para hacer algo un poco más complicado (manejar múltiples claves):

 #!/usr/bin/env perl my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' ); undef $/; my $buf = ; $buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace; print $buf; 

Si nombra el script anterior como replace-script, podría usarse de la siguiente manera:

 replace-script < yourfile | mysql 

Aquí hay una función Bash robusta que, a pesar de usar eval , debe ser segura de usar.

Todas las referencias de variable ${varName} en el texto de entrada se expanden en función de las variables del intérprete de llamada.

Nada más se expande: ni las referencias de variables cuyos nombres no están encerrados en {...} (como $varName ), ni las sustituciones de comandos ( $(...) y la syntax heredada `...` ), ni las sustituciones aritméticas ( $((...)) y la syntax heredada $[...] ).

Para tratar un $ como un literal, \ -capelo; por ejemplo: \${HOME}

Tenga en cuenta que la entrada solo se acepta a través de stdin .

Ejemplo:

 $ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded $HOME is "/Users/jdoe"; `date` and $(ls) 

Código fuente de la función:

 expandVarsStrict(){ local line lineEscaped while IFS= read -r line || [[ -n $line ]]; do # the `||` clause ensures that the last line is read even if it doesn't end with \n # Escape ALL chars. that could trigger an expansion.. IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4') # ... then selectively reenable ${ references lineEscaped=${lineEscaped//$'\4'{/\${} # Finally, escape embedded double quotes to preserve them. lineEscaped=${lineEscaped//\"/\\\"} eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$' done } 

La función asume que no hay caracteres de control 0x1 , 0x2 , 0x3 y 0x4 en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , eso debería ser una suposición segura.

Crear rendertemplate.sh :

 #!/usr/bin/env bash eval "echo \"$(cat $1)\"" 

Y template.tmpl :

 Hello, ${WORLD} Goodbye, ${CHEESE} 

Renderizar la plantilla:

 $ export WORLD=Foo $ CHEESE=Bar ./rendertemplate.sh template.tmpl Hello, Foo Goodbye, Bar 

file.tpl:

 The following bash function should only replace ${var1} syntax and ignore other shell special chars such as `backticks` or $var2 or "double quotes". If I have missed anything - let me know. 

script.sh:

 template(){ # usage: template file.tpl while read -r line ; do line=${line//\"/\\\"} line=${line//\`/\\\`} line=${line//\$/\\\$} line=${line//\\\${/\${} eval "echo \"$line\""; done < ${1} } var1="*replaced*" var2="*not replaced*" template file.tpl > result.txt 

Sugeriría usar algo como Sigil : https://github.com/gliderlabs/sigil

Se comstack en un solo binario, por lo que es extremadamente fácil de instalar en los sistemas.

Entonces puedes hacer un simple delineador como el siguiente:

 cat my-file.conf.template | sigil -p $(env) > my-file.conf 

Esto es mucho más seguro que eval y más fácil que usar regex o sed

Encontré este hilo mientras me preguntaba lo mismo. Me inspiró a esto (cuidado con los apoyos)

 $ echo $MYTEST pass! $ cat FILE hello $MYTEST world $ eval echo `cat FILE` hello pass! world 

Muchas opciones aquí, pero pensé que lanzaría la mía en el montón. Está basado en Perl, solo las variables de destino del formulario $ {…}, toman el archivo para procesarlo como un argumento y emite el archivo convertido en stdout:

 use Env; Env::import(); while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; } print "$text"; 

Por supuesto que no soy realmente una persona perl, por lo que fácilmente podría haber un defecto fatal (aunque a mí me funciona).

Se puede hacer en bash si tienes el control del formato del archivo de configuración. Solo necesita obtener (“.”) El archivo de configuración en lugar de subshell. Eso asegura que las variables se crean en el contexto del shell actual (y continúan existiendo) en lugar de la subcadena (donde la variable desaparece cuando sale el subshell).

 $ cat config.data export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA export parm_user=pax export parm_pwd=never_you_mind $ cat go.bash . config.data echo "JDBC string is " $parm_jdbc echo "Username is " $parm_user echo "Password is " $parm_pwd $ bash go.bash JDBC string is jdbc:db2://box7.co.uk:5000/INSTA Username is pax Password is never_you_mind 

Si su archivo de configuración no puede ser un script de shell, simplemente puede ‘comstackrlo’ antes de ejecutarlo (la comstackción depende de su formato de entrada).

 $ cat config.data parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL parm_user=pax # user name parm_pwd=never_you_mind # password $ cat go.bash cat config.data | sed 's/#.*$//' | sed 's/[ \t]*$//' | sed 's/^[ \t]*//' | grep -v '^$' | sed 's/^/export ' >config.data-compiled . config.data-compiled echo "JDBC string is " $parm_jdbc echo "Username is " $parm_user echo "Password is " $parm_pwd $ bash go.bash JDBC string is jdbc:db2://box7.co.uk:5000/INSTA Username is pax Password is never_you_mind 

En su caso específico, podría usar algo como:

 $ cat config.data export p_p1=val1 export p_p2=val2 $ cat go.bash . ./config.data echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1" $ bash go.bash select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1 

Luego canalice la salida de go.bash a MySQL y listo, con suerte no destruirá su base de datos :-).