Templating con Linux in uno script di shell?


26

quello che voglio realizzare è:

1.) Avere un file di configurazione come modello, con variabili come $ versione $ percorso (ad esempio apache config)

2.) Avere uno script shell che "riempie" le variabili del modello e scrive il file generato su disco.

È possibile con uno script di shell. Sarei molto grato se potessi nominare alcuni comandi / strumenti che posso realizzare questo o alcuni buoni collegamenti.

Risposte:


23

Questo è molto possibile. Un modo molto semplice per implementare questo sarebbe che il file modello sia effettivamente lo script e usi variabili di shell come

#! /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

Puoi persino renderlo configurabile sulla riga di comando specificando version=$1e path=$2, quindi puoi eseguirlo come bash script /foo/bar/baz 1.2.3. Il -EOF precedente causa spazi bianchi prima che le linee vengano ignorate, usare plain <<EOFse non si desidera quel comportamento.

Un altro modo per farlo sarebbe usare la ricerca e sostituire la funzionalità di 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

che sostituirà ogni istanza delle stringhe VERSION e PATH. Se ci sono altri motivi per cui tali stringhe si trovano nel file modello, è possibile che la ricerca e la sostituzione siano VERSIONE o% VERSIONE% o qualcosa di meno probabile che venga attivato accidentalmente.


18

Non sono necessari strumenti diversi da /bin/sh. Dato un file modello del modulo

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

o anche con codice shell misto incluso

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)

e un file di configurazione analizzabile shell come

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

è semplice estenderlo 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

Infatti, dato il percorso del file di configurazione nella variabile shell config_filee il percorso del file modello template_file, tutto ciò che devi fare è:

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

Questo è forse più bello di avere uno script di shell completo come file modello (la soluzione di @ mtinberg).

Il programma completo di espansione del modello 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

Ciò genererà l'espansione all'output standard; reindirizzare l'output standard su un file o modificare quanto sopra in modo ovvio per produrre il file di output desiderato.

Avvertenze: l' espansione del file modello non funzionerebbe se il file contenesse virgolette doppie senza escape ( "). Per motivi di sicurezza, dovremmo probabilmente includere alcuni ovvi controlli di integrità o, ancora meglio, eseguire la trasformazione di escape della shell se il file modello viene generato da entità esterna.


1
È bello vedere ancora gente che usa attivamente gli script di shell per fare cose divertenti come questa. Un mio amico ha scritto un orologio in caratteri shell. github.com/tablespoon/fun/blob/master/cli-clock Tempi davvero divertenti.
pulcini,

2
Quando ho trovato questa risposta quando ho cercato di farlo da solo, ho implementato l'approccio piuttosto ingenuo e l'ho pubblicato su Github: github.com/nealian/bash-unsafe-templates
Pyrocater

9

Il modo più semplice per farlo semplicemente nell'interfaccia della riga di comando di Linux è utilizzare le envsubstvariabili di ambiente.

Esempio di file modello apache.tmpl:

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

Esegui envsubste genera il risultato in un nuovo file 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

Produzione:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>

2
Fa parte di GNU gettext e quindi disponibile su tutte le principali piattaforme Unix (incluso macOS X), oltre a Microsoft Windows.
tricasse il

4

Probabilmente dovresti esaminare un sistema di gestione della configurazione come Puppet o Chef . Questi possono facilmente fare ciò che descrivi sopra e molto altro.


1
Grazie. Assolutamente, ho Chef installato e funzionante. Ma aggiunge un sacco di sovraccarico, quando devi scrivere i tuoi libri di cucina. Non conosco il linguaggio di programmazione ruby ​​e la mia conclusione è stata. è più facile farlo con uno script di shell per i casi più semplici (se possibile).
Markus,

Sembra che Puppet e Chef utilizzino entrambi ERB per i modelli, ed è incredibilmente facile iniziare. Data una variabile name, la stringa <%= name %>in un modello verrà sostituita con nameil valore di. Il modo in cui si definisce nameal di fuori del modello differisce tra i due sistemi, ovviamente.
Mike Renfro,

1
Sì, il templating (con lo chef) è assolutamente semplice. Ma usare lo chef come Framework (e scrivere i libri di cucina) richiede molto tempo. Per inserire i dati nei modelli è necessario capire dove e come Chef gestisce la "fusione" di origini dati e molte altre cose. Ho iniziato a scrivere i miei libri di cucina, ma nel mio caso speciale uno script di shell sarebbe 100 volte più veloce ...
Markus,

Far funzionare l'infrastruttura per Chef o Puppet può essere una seccatura o puoi provare a capire come gestirli senza testa, che è un'avventura divertente. Ansible funziona bene in modalità pull o push fuori dalla scatola, quindi potrebbe trovare un migliore equilibrio nel capirlo e nel copiarlo da soli. docs.ansible.com/template_module.html
pulcini,

4

Se desideri modelli leggeri e reali anziché un codice shell che generi nuovi file, le solite scelte sono sed& awko perl. Ecco un link: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

Io, userei un vero linguaggio come perl, tcl, python, ruby ​​o qualcos'altro in quella classe. Qualcosa costruito per lo scripting. Tutti hanno buoni, semplici strumenti di template e tonnellate di esempi in google.


4

Uso shtpl per questo. (mio progetto privato, il che significa che non è ampiamente utilizzato. Ma forse vuoi testarlo comunque)

Ad esempio, vuoi generare una / etc / network / interfaces da un file csv, puoi farlo in questo modo:

Contenuto del file CSV (qui 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

Modello (qui interfaces.tpl):

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

#% done < "$CSVFILE"

Comando:

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

Risultato:

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

Godere!


1

Ho migliorato la risposta di FooF in modo che l'utente non debba sottrarre manualmente le doppie virgolette:

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

1

Di recente ho pubblicato uno script bash che realizza proprio questo usando una sintassi di modello simile a jinja. Si chiama cookie . Ecco una demo:

demo dei cookie


Come risponde alla domanda?
RalfFriedl,

1
@RalfFriedl Non sono sicuro di cosa intendi. Ho appena riletto la domanda pensando di aver perso qualcosa, ma non la vedo. In effetti, come fa a non rispondere a nessuna parte della domanda che è stata posta? È quasi come se avessi creato un cookie con il solo scopo di rispondere a questa domanda ... cosa che ho fatto, anche se in quel momento potrei averlo scritto in modo un po 'diverso nella mia testa. :)
Bryan Bugyi,

Sono un po 'in ritardo per giocare a mediatore, ma forse Ralf ti stava semplicemente chiedendo di spiegare in che modo il cookie risponde alla domanda, piuttosto che cercare di implicare in modo inequivocabile che non conosci il tuo progetto abbastanza bene da giudicare se potrebbe risolvere il problema di qualcuno .
abathur

0

Probabilmente sono in ritardo a questa festa. Tuttavia, mi sono imbattuto nello stesso problema e ho optato per la creazione del mio motore di template BASH in poche righe di codice:

Diciamo che hai questo file.template:

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

E questo rulesfile:

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

Esegui questo comando:

templater rules < file.template

Ottieni questo:

# My template
## Author
 - LEOPOLDO WINSTON <leothewinston@leoserver.com>

Puoi installarlo tramite:

 bpkg install vicentebolea/bash-templater

Questo è il sito del progetto


0

Per espandere la grande risposta di @ FooF (le nuove righe non sono state formattate nei commenti), usando un heredoc + caratteri di controllo, puoi consentire caratteri e nomi di file arbitrari :

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

Questo accetta qualsiasi carattere non nullo e si tronca in anticipo solo se ^Dsi incontrano 3 byte sulla propria linea (realisticamente mai). zsh supporta anche terminatori null, quindi printf '\x00\x00\x00'funzionerebbe. templatefunziona anche con nomi di file insidiosi come:

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

Attenzione, i modelli di shell possono "espandere" comandi arbitrari, ad es $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10)). Con grande potenza ...

Modifica: se davvero non vuoi che i tuoi file lancino bombe contro di te, solo sudo -u nobody sh(o qualche altro utente sicuro) in anticipo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.