it-swarm-es.com

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

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 comandos para reemplazar estas instancias y volcar la salida a la salida estándar?

125
Dana the Sane

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 sed.

154
user

Actualizar

Aquí hay una solución de yottatsa en una pregunta similar que solo reemplaza variables como $ VAR o $ {VAR}, y es una breve descripción de una sola línea.

i=32 Word=foo envsubst < template.txt

Por supuesto, si i and Word están en su entorno, entonces es justo

envsubst < template.txt

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

Respuesta antigua

Aquí hay una mejora en la solución de mogsie en una pregunta similar, mi solución no requiere que escale las comillas dobles, mogsie's sí, ¡pero su es un liner!

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

El poder de estas dos soluciones es que solo se obtienen algunos tipos de expansiones de Shell que normalmente no se producen $ ((...)), `...` 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 hace varias líneas muy bien.

134
plockc

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
42
gnud

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 originalmente especifiqué, usarías:

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
19
Dana the Sane

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
12
ChaPuZ

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
9
Thomas

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

Todas las referencias de variables ${varName} en el texto de entrada se expanden en función de las variables del Shell que llama.

Nada más está expandido: ninguna de las referencias de variables cuyos nombres son no encerrado en {...} (como $varName), ni sustituciones de comandos ($(...) y sintaxis heredada `...`), ni sustituciones aritméticas ($((...)) sintaxis heredada $[...]).

Para tratar un $ como un literal, \- escúchalo; 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 0x1, 0x2, 0x3, y 0x4 caracteres de control están presentes en la entrada, debido a esos caracteres. se utilizan internamente, ya que la función procesa texto , debe ser una suposición segura.

8
mklement0

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 hacer esto mucho más fácil. Si tiene una asignación más compleja con más que solo dbName para sus reemplazos, podría extender esto con bastante facilidad, pero igual podría incluirlo en un script Perl estándar en ese momento.

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

Un script corto de Perl para hacer algo un poco más complicado (manejar varias 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 la secuencia de comandos anterior como reemplazar secuencia de comandos, se podría utilizar de la siguiente manera:

replace-script < yourfile | mysql
6
Beau Simensen

Crear rendertemplate.sh:

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

Y template.tmpl:

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

Render la plantilla:

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

archivo.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
4
user976433

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

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

Entonces puedes hacer un sencillo de una sola línea 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 expresiones regulares o sed

3
spudfkc

Se puede hacer en bash sí mismo si tiene el control del formato del archivo de configuración. Solo necesita generar (".") El archivo de configuración en lugar de subhazcarlo. 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 '^$'
        | 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 :-).

2
paxdiablo

Hay muchas opciones aquí, pero pensé que tiraría la mía en el montón. Está basado en Perl, solo se dirige a variables de la forma $ {...}, toma el archivo para procesar como un argumento y genera el archivo convertido en la salida estándar:

use Env;
Env::import();

while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }

print "$text";

Por supuesto, no soy realmente una persona de Perl, así que fácilmente podría haber un error fatal (aunque funciona para mí).

2
sfitts

Encontré este hilo mientras me preguntaba lo mismo. Me inspiró a esto (cuidado con las comillas traseras).

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

Aquí hay una forma de hacer que el Shell haga la sustitución por usted, como si el contenido del archivo se escribiera entre comillas dobles.

Usando el ejemplo de template.txt con contenidos:

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 asignadas a la ejecución de sh.
  • sh ejecuta el contenido de la cadena que se pasa.
  • Las cadenas escritas una al lado de la otra se convierten en una cadena, esa cadena es:
    • 'echo "' + "$(cat template.txt)" + '"'
  • Como la sustitución está entre ", "$(cat template.txt)" se convierte en la salida de cat template.txt.
  • Entonces el comando ejecutado por sh -c se convierte en:
    • echo "The number is ${i}\nThe Word is ${Word}",
    • donde i y Word son las variables de entorno especificadas.
0
Apriori