¿Templa con Linux en un script de Shell?

26

lo que quiero lograr es:

1.) Tener un archivo de configuración como plantilla, con variables como $ version $ path (por ejemplo, configuración de apache)

2.) Tener un script de shell que "completa" las variables de la plantilla y escribe el archivo generado en el disco.

¿Es esto posible con un script de shell? Estaría muy agradecido si puede nombrar algunos comandos / herramientas, puedo lograr esto o algunos buenos enlaces.

Markus
fuente

Respuestas:

23

Esto es muy posible Una forma muy sencilla de implementar esto sería que el archivo de plantilla sea realmente el script y use variables de shell como

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
cat > /tmp/destfile <<-EOF
here is some config for version $version which should
also reference this path $path
EOF

Incluso podría hacer que esto sea configurable en la línea de comando especificando version=$1y path=$2, para que pueda ejecutarlo como bash script /foo/bar/baz 1.2.3. El -EOF anterior causa espacios en blanco antes de que se ignoren las líneas, use plain <<EOFsi no desea ese comportamiento.

Otra forma de hacerlo sería utilizar la funcionalidad de búsqueda y reemplazo de sed

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
sed -e "s/VERSION/$version/g" -e "s/PATH/$path/" /path/to/templatefile > /tmp/destfile

que reemplazaría cada instancia de las cadenas VERSION y PATH. Si hay otras razones por las cuales esas cadenas estarían en el archivo de plantilla, puede hacer que su búsqueda y reemplazo sea VERSIÓN o% VERSIÓN% o algo menos probable que se active accidentalmente.

mtinberg
fuente
18

No se necesitan herramientas que no sean /bin/sh. Dado un archivo de plantilla del formulario

Version: ${version}
Path: ${path}

o incluso con código de shell mixto incluido

Version: ${version}
Path: ${path}
Cost: ${cost}\$
$(i=1; for w in one two three four; do echo Param${i}: ${w}; i=$(expr $i + 1); done)

y un archivo de configuración analizable de shell como

version="1.2.3-r42"
path="/some/place/under/the/rainbow/where/files/dance/in/happiness"
cost="42"

es simple expandir esto a

Version: 1.2.3-r42
Path: /some/place/under/the/rainbow/where/files/dance/in/happiness
Cost: 42$
Param1: one
Param2: two
Param3: three
Param4: four

De hecho, dada la ruta al archivo de configuración en la variable de shell config_filey la ruta al archivo de plantilla template_file, todo lo que necesita hacer es:

. ${config_file}
template="$(cat ${template_file})"
eval "echo \"${template}\""

Esto es quizás más bonito que tener un script de shell completo como archivo de plantilla (solución de @ mtinberg).

El programa completo de expansión de plantillas ingenuo:

#!/bin/sh

PROG=$(basename $0)

usage()
{
    echo "${PROG} <template-file> [ <config-file> ]"
}

expand()
{
    local template="$(cat $1)"
    eval "echo \"${template}\""
}

case $# in
    1) expand "$1";;
    2) . "$2"; expand "$1";;
    *) usage; exit 0;;
esac

Esto generará la expansión a la salida estándar; simplemente redirija la salida estándar a un archivo o modifique lo anterior de manera obvia para producir el archivo de salida deseado.

Advertencias: la expansión del archivo de plantilla no funcionaría si el archivo contuviera comillas dobles sin escape ( "). Por razones de seguridad, probablemente deberíamos incluir algunas comprobaciones de cordura obvias o, mejor aún, realizar una transformación de escape de shell si el archivo de plantilla es generado por una entidad externa.

FooF
fuente
1
Es genial ver a la gente que usa activamente scripts de shell para hacer cosas divertidas como esta. Un amigo mío escribió un reloj en shell script. github.com/tablespoon/fun/blob/master/cli-clock Diversión de verdad.
pollitos
2
Cuando encontré esta respuesta cuando se busca hacer esto por mí mismo, he implementado el enfoque bastante ingenuo y lo publicó en Github: github.com/nealian/bash-unsafe-templates
Pyrocater
9

La forma más fácil de hacer esto simplemente en la CLI de Linux es usar envsubstVariables de entorno.

Archivo de plantilla de ejemplo apache.tmpl:

<VirtualHost *:${PORT}>
    ServerName ${SERVER_NAME}
    ServerAlias ${SERVER_ALIAS}
    DocumentRoot "${DOCUMENT_ROOT}"
</VirtualHost>

Ejecutar envsubsty generar el resultado en un nuevo archivo my_apache_site.conf:

export PORT="443"
export SERVER_NAME="example.com"
export SERVER_ALIAS="www.example.com"
export DOCUMENT_ROOT="/var/www/html/"
envsubst < apache.tmpl > my_apache_site.conf

Salida:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>
Lirt
fuente
2
Es parte de GNU gettext y, por lo tanto, está disponible en todas las plataformas principales de Unix (incluido macOS X), así como en Microsoft Windows.
Tricasse
4

Probablemente debería buscar un sistema de gestión de configuración como Puppet o Chef . Estos pueden hacer fácilmente lo que describió anteriormente y mucho más.

EEAA
fuente
1
Gracias. Absolutamente, tengo Chef instalado y funcionando. Pero agrega mucha sobrecarga cuando tienes que escribir tus propios libros de cocina. No conozco el lenguaje de programación ruby ​​y mi conclusión fue. es más fácil hacer esto con un script de shell para los casos más fáciles (si es posible).
Markus
Parece que Puppet y Chef usan ERB para las plantillas, y eso es ridículamente fácil de comenzar. Dada una variable name, la cadena <%= name %>en una plantilla será reemplazada por nameel valor de. La forma en que define namefuera de la plantilla difiere entre los dos sistemas, obviamente.
Mike Renfro
1
Sí, crear plantillas (con Chef) en sí mismo es absolutamente fácil. Pero usar chef como Framework (y escribir los libros de cocina) requiere mucho tiempo. Para obtener los datos en las plantillas, debe comprender dónde y cómo Chef gestiona la "fusión" de fuentes de datos y muchas otras cosas. Empecé a escribir mis propios libros de cocina, pero un script de shell en mi caso especial sería 100 veces más rápido ...
Markus
Poner en marcha la infraestructura para Chef o Puppet puede ser una molestia o puedes tratar de descubrir cómo ejecutarlos sin cabeza, lo cual es una aventura divertida. Ansible funciona bien en el modo pull o push fuera de la caja, por lo que podría lograr un mejor equilibrio al descifrarlo y escribirlo usted mismo. docs.ansible.com/template_module.html
pollitos
4

Si desea plantillas ligeras y reales en lugar de código shell que genera nuevos archivos, las opciones habituales son sed& awko perl. Aquí hay un enlace: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

Yo, usaría un lenguaje real como perl, tcl, python, ruby ​​o algo más en esa clase. Algo construido para las secuencias de comandos. Todos tienen buenas y sencillas herramientas de plantillas y toneladas de ejemplos en google.

marca
fuente
4

Yo uso shtpl para eso. (proyecto privado mío, lo que significa que no se usa mucho. Pero tal vez quieras probarlo de todos modos)

Por ejemplo, si desea generar un / etc / network / interfaces a partir de un archivo csv, puede hacerlo así:

Contenido del archivo CSV (aquí test.csv):

eth0;10.1.0.10;255.255.0.0;10.1.0.1
eth1;192.168.0.10; 255.255.255.0;192.168.0.1

Plantilla (aquí interfaces.tpl):

#% IFS=';'
#% while read "Val1" "Val2" "Val3" "Val4"; do
auto $Val1 
iface $Val1 inet static
  address $Val2 
  netmask $Val3 
  gateway $Val4 

#% done < "$CSVFILE"

Mando:

$ CSVFILE=test.csv sh -c "$( shtpl interfaces.tpl )"

Resultado:

auto eth0 
iface eth0 inet static
  address 10.1.0.10 
  netmask 255.255.0.0 
  gateway 10.1.0.1 

auto eth1 
iface eth1 inet static
  address 192.168.0.10 
  netmask  255.255.255.0 
  gateway 192.168.0.1

¡Disfrutar!

zstegi
fuente
1

Mejoré la respuesta de FooF para que el usuario no necesite eliminar las comillas dobles manualmente:

#!/bin/bash
template="$(cat $1)"
template=$(sed 's/\([^\\]\)"/\1\\"/g; s/^"/\\"/g' <<< "$template")
eval "echo \"${template}\""
Shaohua Li
fuente
1

Recientemente publiqué un script bash que logra eso usando una sintaxis de plantilla tipo jinja. Se llama cookie . Aquí hay una demostración:

demostración de cookies

Bryan Bugyi
fuente
¿Cómo responde esto a la pregunta?
RalfFriedl
1
@RalfFriedl No estoy seguro de lo que quieres decir. Acabo de releer la pregunta pensando que debo haberme perdido algo, pero no lo veo. De hecho, ¿cómo no responde esto a ninguna parte de la pregunta que se hizo? Es casi como si hubiera creado una cookie con el único propósito de responder a esta pregunta ... lo cual hice, aunque es posible que en ese momento lo hubiera expresado de manera un poco diferente en mi cabeza. :)
Bryan Bugyi
Llego un poco tarde para jugar como mediador, pero tal vez Ralf te estaba pidiendo caritativamente que explicaras cómo las cookies responden la pregunta, en lugar de tratar de implicar de manera poco caritativa que no conoces tu propio proyecto lo suficientemente bien como para juzgar si podría resolver el problema de alguien .
abathur
0

Probablemente llegue tarde a esta fiesta. Sin embargo, me topé con el mismo problema y opté por crear mi propio motor de plantillas BASH en unas pocas líneas de código:

Digamos que tienes esto file.template:

# My template
## Author
 - @NAME@ <@EMAIL@>

Y este rulesarchivo:

NAME=LEOPOLDO WINSTON
EMAIL=leothewinston\@leoserver.com

Ejecutas este comando:

templater rules < file.template

Obtienes esto:

# My template
## Author
 - LEOPOLDO WINSTON <[email protected]>

Puedes instalarlo de la siguiente manera:

 bpkg install vicentebolea/bash-templater

Este es el sitio del proyecto .

Vicente Bolea
fuente
0

Para ampliar la excelente respuesta de @ FooF (las nuevas líneas no se formatearon en los comentarios), utilizando caracteres de control heredoc +, puede permitir caracteres arbitrarios y nombres de archivos:

template() {
    # if [ "$#" -eq 0 ] ; then return; fi # or just error
    eval "cat <<$(printf '\x04\x04\x04');
$(cat $1)
"
}

Esto acepta cualquier carácter no nulo, y solo se trunca temprano si ^Dse encuentran 3 bytes en su propia línea (en realidad nunca). zsh incluso admite terminadores nulos, por printf '\x00\x00\x00'lo que funcionaría. templateincluso funciona para nombres de archivo traicioneros como:

for num in `seq 10`; do
    template 'foo "$ .html' # works
done

Cuidado con las plantillas de shell pueden "expandir" comandos arbitrarios, por ejemplo $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10)). Con gran poder ...

Editar: si realmente no quieres que tus archivos te lancen armas nucleares, solo sudo -u nobody sh(o algún otro usuario seguro) de antemano.

alexchandel
fuente