Quiero canalizar la salida de un archivo de "plantilla" en MySQL, el archivo tiene variables como ${dbName}
intercaladas. ¿Cuál es la utilidad de línea de comando para reemplazar estas instancias y volcar la salida a la salida estándar?
Quiero canalizar la salida de un archivo de "plantilla" en MySQL, el archivo tiene variables como ${dbName}
intercaladas. ¿Cuál es la utilidad de línea de comando para reemplazar estas instancias y volcar la salida a la salida estándar?
Respuestas:
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 el consejo de pasar múltiples -e
argumentos a la misma sed
invocación.
cat
. Todo lo que necesita es sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
esperará un texto escapado, lo cual es una molestia.
Aquí hay una solución de yottatsa en una pregunta similar que solo reemplaza variables como $ VAR o $ {VAR}, y es breve.
i=32 word=foo envsubst < template.txt
Por supuesto, si i y word 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
Aquí hay una mejora a la solución de mogsie en una pregunta similar, mi solución no requiere que escale las comillas dobles, lo hace mogsie, ¡pero la suya es de una sola línea!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
El poder de estas dos soluciones es que solo obtienes unos pocos tipos de expansiones de shell que no ocurren normalmente $ ((...)), `...` y $ (...), aunque la barra diagonal inversa es un escapar del personaje aquí, pero no tiene que preocuparse de que el análisis tenga un error, y hace varias líneas muy bien.
envsubst
parece que lo desnudo no funciona si tus envars no se exportan.
envsubst
, como su nombre lo indica, solo reconoce variables de entorno , no variables de shell . También vale la pena señalar que envsubst
es una utilidad GNU y, por lo tanto, no está preinstalada ni disponible en todas las plataformas.
Utilizar /bin/sh
. Cree un pequeño script de shell que establezca las variables y luego analice la plantilla utilizando el propio shell. Me gusta así (edite para manejar líneas nuevas correctamente):
the number is ${i}
the word is ${word}
#!/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"
#sh script.sh
the number is 1
the word is dog
bash
Se ejecutará todo el comando en la entrada. Si la plantilla es: "las palabras son; rm -rf $ HOME", perderá archivos.
read
comando, como está escrito, recorta los espacios en blanco iniciales y finales de cada línea y \
caracteres 'come' ., (C) solo use esto si confiar o controlar la entrada, porque las sustituciones de comandos ( `…`
o $(…)
) incrustadas en la entrada permiten la ejecución de comandos arbitrarios debido al uso de eval
. Finalmente, existe una pequeña posibilidad de que echo
confunda el comienzo de una línea con una de sus opciones de línea de comandos.
Estaba pensando en esto nuevamente, dado el interés reciente, y creo que la herramienta en la que estaba pensando originalmente era m4
el macroprocesador para autotools. Entonces, en lugar de la variable que especifiqué originalmente, usaría:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
para este uso simple de reemplazo / plantilla variable, como se menciona en otras respuestas. m4
es una gran herramienta, pero es un preprocesador completo con muchas más funciones y, por lo tanto, complejidad que puede no ser necesaria si simplemente desea reemplazar algunas variables.
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
`…`
o $(…)
) incrustadas en la entrada permiten la ejecución de comandos arbitrarios debido al uso de eval
, y la ejecución directa del código de shell debido al uso de source
. Además, las comillas dobles en la entrada se descartan silenciosamente y echo
podrían confundir el comienzo de una línea con una de sus opciones de línea de comandos.
Aquí hay una función Bash robusta que, a pesar de usar eval
, debería ser segura de usar.
Todas ${varName}
las referencias de variables en el texto de entrada se expanden en función de las variables del shell de llamada.
No se expande nada más : ni referencias de variables cuyos nombres no están encerrados {...}
(como $varName
), ni sustituciones de comandos ( $(...)
y sintaxis heredada `...`
), ni sustituciones aritméticas ( $((...))
y sintaxis heredada)$[...]
).
Para tratar a $
como un literal, \
-escápelo; p.ej:\${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 0x1
, 0x2
, 0x3
, y 0x4
caracteres de control están presentes en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , debería ser una suposición segura.
eval
es bastante seguro de usar.
"
correctamente!)
${FOO:-bar}
o solo generará algo si está configurado ${HOME+Home is ${HOME}}
. Sospecho que con una pequeña extensión también podría devolver códigos de salida para las variables que faltan, ${FOO?Foo is missing}
pero actualmente tldp.org/LDP/abs/html/parameter-substitution.html tiene una lista de estos si eso ayuda
Crear rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Y template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Renderice la plantilla:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, lo está ejecutando como código.
eval "echo \"$(cat $1)\""
Funciona genial !
Aquí está 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ás abierto a usar Perl , esa sería mi sugerencia. Aunque probablemente haya algunos expertos en sed y / o AWK que probablemente sepan cómo hacerlo mucho más fácilmente. Si tiene un mapeo más complejo con algo más que dbName para sus reemplazos, podría extender esto con bastante facilidad, pero también podría incluirlo en un script Perl estándar en ese momento.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Un breve script de Perl para hacer algo un poco más complicado (manejar múltiples teclas):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Si nombra el guión anterior como guión de reemplazo, podría usarse de la siguiente manera:
replace-script < yourfile | mysql
Aquí hay una manera de hacer que el shell realice la sustitución por usted, como si el contenido del archivo estuviera escrito entre comillas dobles.
Usando el ejemplo de template.txt con contenido:
The number is ${i}
The word is ${word}
La siguiente línea hará que el shell interpole el contenido de template.txt y escriba el resultado en estándar.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Explicación:
i
y word
se pasan como variables de entorno limitadas a la ejecución de sh
.sh
ejecuta el contenido de la cadena que se pasa.echo "
' + " $(cat template.txt)
" + ' "
'"
, " $(cat template.txt)
" se convierte en la salida decat template.txt
.sh -c
convierte en:
echo "The number is ${i}\nThe word is ${word}"
,i
y word
son las variables de entorno especificadas.'$(rm -rf ~)'$(rm -rf ~)
las comillas literales en el archivo de plantilla coincidirán con las que agregó antes de su expansión.
'$(echo a)'$(echo a)
. Produce 'a'a
. Lo principal que está sucediendo es que lo primero echo a
dentro de esto '
es ser evaluado, lo que puede no ser lo que esperas ya que está dentro '
, pero es el mismo comportamiento que incluir '
en una "
cadena entre comillas.
"
cadena entre comillas (incluido $(...)
) es el punto.
${varname}
, no otras expansiones de mayor riesgo de seguridad.
echo "
, seguida de una cadena de comillas dobles con los contenidos literales de template.txt
, seguido de otra cadena literal "
, todos concatenados en un solo argumento pasado a sh -c
. Tiene razón en que '
no se puede hacer coincidir (ya que fue consumido por la capa externa en lugar de pasar a la interna), pero "
ciertamente puede, por lo que se Gotcha"; rm -rf ~; echo "
podría ejecutar una plantilla que contenga .
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
\$(date)
while IFS= read -r line; do
como read
comando, de lo contrario, eliminará los espacios en blanco iniciales y finales de cada línea de entrada. Además, echo
podría confundir el comienzo de una línea con una de sus opciones de línea de comandos, por lo que es mejor usarla printf '%s\n'
. Finalmente, es más seguro hacer comillas dobles ${1}
.
Sugeriría usar algo como Sigil : https://github.com/gliderlabs/sigil
Se compila en un solo binario, por lo que es extremadamente fácil de instalar en sistemas.
Luego puede hacer una línea simple como la siguiente:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Esto es mucho más seguro eval
y más fácil que usar regex osed
cat
y usar <my-file.conf.template
en su lugar para que le dé sigil
un identificador de archivo real en lugar de un FIFO.
Encontré este hilo mientras me preguntaba lo mismo. Me inspiró a esto (cuidado con los backticks)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
es$(< file)
eval echo "\"$(cat FILE)\""
pero aún puede fallar en que las comillas dobles en la entrada se descartan.
`…`
o $(…)
) incrustadas en la entrada permiten la ejecución de comandos arbitrarios debido al uso de eval
.
Muchas opciones aquí, pero pensé que tiraría la mía al montón. Está basado en Perl, solo se dirige a variables de la forma $ {...}, toma el archivo para procesarlo como argumento y genera el archivo convertido en stdout:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Por supuesto, no soy realmente una persona perl, por lo que fácilmente podría haber un defecto fatal (aunque funciona para mí).
Env::import();
línea; la importación está implícita en use
. Además, sugiero no construir primero toda la salida en la memoria: simplemente use en print;
lugar de $text .= $_;
dentro del bucle y suelte el print
comando post-loop .
Se puede hacer en bash si tienes control del formato del archivo de configuración. Solo necesita fuente (".") Del archivo de configuración en lugar de subshell. Eso garantiza que las variables se creen en el contexto del shell actual (y continúen existiendo) en lugar de la subshell (donde la variable desaparece cuando la subshell sale).
$ 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 'compilarlo' antes de ejecutarlo (la compilació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 en MySQL y listo, con suerte no destruirá su base de datos :-).
go.bash
), tiene el extremo equivocado del palo: no son parte de la solución, son solo una forma de mostrar que las variables están siendo ajustado correctamente
Querrá algo más robusto que las sugerencias actuales porque, aunque funcionan para su caso de uso limitado (por ahora), no serán suficientes para situaciones más complejas.
Necesitas un mejor renderizador. Necesitas el mejor renderizador. ¡Necesitas The Renderest!
Dado template.txt:
Hola persona}}!
Correr:
$ persona = Bob ./render template.txt
Y verás la salida
¡Hola Bob!
Escríbalo a un archivo redirigiendo stdout a un archivo:
$ persona = Bob ./render template.txt >dered.txt
Y si está renderizando un script que tiene $ {} variables que no desea interpolar, ¡The Renderest lo tiene cubierto sin tener que hacer nada más!
Continúe y obtenga su copia en https://github.com/relaxdiego/renderest