¿Cómo reemplazar los marcadores de posición $ {} en un archivo de texto?
-
03-07-2019 - |
Pregunta
Quiero canalizar la salida de una " 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 salida estándar?
Solución
Sed !
Dado template.txt:
The number is ${i} The word is ${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 argumentos -e
a la misma invocación sed
.
Otros consejos
Actualizar
Aquí hay una solución de yottatsa en una pregunta similar que solo reemplaza variables como $ VAR o $ {VAR }, y es un breve resumen
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
Respuesta anterior
Aquí hay una mejora en la solución de mogsie en una pregunta similar, mi solución no requiere que escales el doble citas, lo hace mogsie, pero el suyo 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 algunos tipos de expansiones de shell que normalmente no ocurren $ ((...)), `...`, y $ (...), aunque La barra invertida es un personaje de escape aquí, pero no tiene que preocuparse de que el análisis tenga un error y haga varias líneas muy bien.
Utilice / bin / sh
. Cree un pequeño script de shell que establezca las variables y luego analice la plantilla utilizando el propio shell. Me gusta (edita para manejar las 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 nuevamente, dado el interés reciente, y creo que la herramienta en la que originalmente pensaba era m4
, el procesador de macros para autotools. Entonces, en lugar de la variable que especifiqué originalmente, usaría:
$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
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
Aquí hay una función robusta de Bash que, a pesar de usar eval
, debería ser segura de usar.
Todas las referencias de variable $ {varName}
en el texto de entrada se expanden en función de las variables del shell que llama.
Nada más se expande: ninguna de las referencias de variables cuyos nombres son no se encuentra dentro de {...}
(como $ varName
), ni sustituciones de comandos ( $ (...)
y sintaxis heredada `...`
), ni sustituciones aritméticas ( $ ( (...))
y la sintaxis heredada $ [...]
).
Para tratar un $
como un literal, \
-escape it; 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 '`([
La función asume que no hay caracteres de control 0x1
, 0x2
, 0x3
y 0x4
Presente en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , eso debería ser una suposición segura.
'\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//
La función asume que no hay caracteres de control 0x1
, 0x2
, 0x3
y 0x4
Presente en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , eso debería ser una suposición segura.
\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([
La función asume que no hay caracteres de control 0x1
, 0x2
, 0x3
y 0x4
Presente en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , eso debería ser una suposición segura.
done
}
La función asume que no hay caracteres de control 0x1
, 0x2
, 0x3
y 0x4
Presente en la entrada, porque esos caracteres. se usan internamente, ya que la función procesa texto , eso debería ser una suposición segura.
Si está abierto a usar Perl , esa sería mi sugerencia. Aunque probablemente hay algunos sed y / o AWK expertos que probablemente saben cómo hacerlo mucho más fácilmente. Si tiene un mapeo más complejo con más que solo 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 script corto en 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 = <STDIN>;
$buf =~ s/\$\{ Si está abierto a usar Perl , esa sería mi sugerencia. Aunque probablemente hay algunos sed y / o AWK expertos que probablemente saben cómo hacerlo mucho más fácilmente. Si tiene un mapeo más complejo con más que solo 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 script corto en Perl para hacer algo un poco más complicado (manejar múltiples claves):
replace-script < yourfile | mysql
Si nombra la secuencia de comandos anterior como reemplazo de secuencia de comandos, podría utilizarse de la siguiente manera:
<*>\}/$replace{ Si está abierto a usar Perl , esa sería mi sugerencia. Aunque probablemente hay algunos sed y / o AWK expertos que probablemente saben cómo hacerlo mucho más fácilmente. Si tiene un mapeo más complejo con más que solo 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 script corto en Perl para hacer algo un poco más complicado (manejar múltiples claves):
<*>
Si nombra la secuencia de comandos anterior como reemplazo de secuencia de comandos, podría utilizarse de la siguiente manera:
<*>}/g for keys %replace;
print $buf;
Si nombra la secuencia de comandos anterior como reemplazo de secuencia de comandos, podría utilizarse de la siguiente manera:
<*> Crear rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Y template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Renderiza 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 compila en un solo binario, por lo que es extremadamente fácil de instalar en los sistemas.
Entonces 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 que eval
y más fácil que usar expresiones regulares o sed
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
Hay muchas opciones aquí, pero pensé que tiraría la mía al montón. Se basa en Perl, solo se dirige a variables de la forma $ {...}, toma el archivo para procesarlo como un argumento y genera el archivo convertido en la salida estándar:
use Env;
Env::import();
while(<>) { Hay muchas opciones aquí, pero pensé que tiraría la mía al montón. Se basa en Perl, solo se dirige a variables de la forma $ {...}, toma el archivo para procesarlo como un argumento y genera el archivo convertido en la salida estándar:
<*>
Por supuesto que no soy una persona perl, así que podría haber un error fatal (aunque funciona para mí).
=~ s/(\${\w+})/$1/eeg; $text .= Hay muchas opciones aquí, pero pensé que tiraría la mía al montón. Se basa en Perl, solo se dirige a variables de la forma $ {...}, toma el archivo para procesarlo como un argumento y genera el archivo convertido en la salida estándar:
<*>
Por supuesto que no soy una persona perl, así que podría haber un error fatal (aunque funciona para mí).
; }
print "$text";
Por supuesto que no soy una persona perl, así que podría haber un error fatal (aunque funciona para mí).
Se puede hacer en bash si tiene el control del formato del archivo de configuración. Solo necesita obtener (". & Quot;) el archivo de configuración en lugar de subshell. Eso asegura 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, puede simplemente '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 '^
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 canaliza la salida de go.bash a MySQL y listo, espero que no destruyas tu base de datos :-).
| 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:
<*>Luego canaliza la salida de go.bash a MySQL y listo, espero que no destruyas tu base de datos :-).
Esta es una forma de hacer que el shell haga la sustitución por ti, como si el contenido del archivo se escribiera 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 una salida estándar.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Explicación:
-
i
yword
se pasan como variables de entorno a la ejecución desh
. -
sh
ejecuta el contenido de la cadena que se pasa. - Las cadenas escritas una junto a otra se convierten en una cadena, esa cadena es:
- '
echo "
' + "$ (cat template.txt)
" + '"
'
- '
- Dado que la sustitución es entre
"
, "$ (cat template.txt)
" se convierte en la salida decat template.txt
. - Por lo tanto, el comando ejecutado por
sh -c
se convierte en:-
echo " El número es $ {i} \ nLa palabra es $ {word} "
, - donde
i
yword
son las variables de entorno especificadas.
-