Templating mit Linux in einem Shell-Skript?

26

was ich fertigstellen möchte ist:

1.) Eine Konfigurationsdatei als Vorlage mit Variablen wie $ version $ path (zum Beispiel apache config)

2.) Ein Shell-Skript, das die Variablen der Vorlage "ausfüllt" und die generierte Datei auf die Festplatte schreibt.

Ist das mit einem Shell-Skript möglich. Ich wäre sehr dankbar, wenn Sie einige Befehle / Tools nennen könnten, die ich ausführen kann, oder einige gute Links.

Markus
quelle

Antworten:

23

Das ist sehr gut möglich. Eine sehr einfache Möglichkeit, dies zu implementieren, besteht darin, dass die Vorlagendatei tatsächlich das Skript ist und Shell-Variablen verwendet, wie z

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

Sie können dies sogar in der Befehlszeile konfigurieren, indem Sie version=$1und angeben path=$2, damit Sie es wie folgt ausführen können bash script /foo/bar/baz 1.2.3. Das -Before EOF bewirkt Leerzeichen, bevor die Zeilen ignoriert werden. Verwenden Sie Plain<<EOF wenn Sie dieses Verhalten nicht möchten.

Eine andere Möglichkeit wäre, die Such- und Ersetzungsfunktion von sed zu verwenden

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

Dies würde jede Instanz der Zeichenfolgen VERSION und PATH ersetzen. Wenn es andere Gründe dafür gibt, dass diese Zeichenfolgen in der Vorlagendatei enthalten sind, können Sie entweder VERSION oder% VERSION% suchen und ersetzen, oder es ist weniger wahrscheinlich, dass sie versehentlich ausgelöst werden.

Berg
quelle
18

Kein Werkzeug außer /bin/sh. Gegeben eine Vorlagendatei des Formulars

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

oder sogar mit gemischtem Shell-Code

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)

und eine Shell-analysierbare Konfigurationsdatei wie

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

es ist eine einfache Sache, dies zu erweitern

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

In der Tat müssen Sie angesichts des Pfads zur Konfigurationsdatei in der Shell-Variablen config_fileund des Pfads zur Vorlagendatei in template_filenur Folgendes tun:

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

Dies ist vielleicht hübscher als ein vollständiges Shell-Skript als Vorlagendatei (@ mtinbergs Lösung).

Das vollständige naive Template-Expander-Programm:

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

Dadurch wird die Erweiterung auf die Standardausgabe zurückgesetzt. leiten Sie die Standardausgabe einfach in eine Datei um, oder ändern Sie die obigen Einstellungen auf offensichtliche Weise, um die gewünschte Ausgabedatei zu erstellen.

Vorsichtsmaßnahmen: Die Erweiterung der Vorlagendatei funktioniert nicht, wenn die Datei Anführungszeichen enthält ( "). Aus Sicherheitsgründen sollten wir wahrscheinlich einige offensichtliche Sicherheitsüberprüfungen einbeziehen oder, noch besser, eine Shell-Escape-Transformation durchführen, wenn die Vorlagendatei von einer externen Entität generiert wird.

FooF
quelle
1
Es ist großartig zu sehen, wie Leute immer noch aktiv Shell-Skripte verwenden, um lustige Sachen wie diese zu machen. Ein Kumpel von mir hat eine Uhr in Shell-Schrift geschrieben. github.com/tablespoon/fun/blob/master/cli-clock In der Tat lustige Zeiten.
Küken
2
Als ich diese Antwort fand, als ich mich selbst darum bemühte, implementierte ich den ziemlich naiven Ansatz und veröffentlichte ihn auf Github: github.com/nealian/bash-unsafe-templates
Pyrocater
9

Der einfachste Weg, dies in Linux CLI zu tun, ist die Verwendung von envsubstUmgebungsvariablen.

Beispiel für eine Vorlagendatei apache.tmpl:

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

envsubstErgebnis ausführen und in neue Datei ausgeben 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

Ausgabe:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>
Lirt
quelle
2
Es ist Teil von GNU gettext und daher auf allen wichtigen Unix-Plattformen (einschließlich macOS X) sowie auf Microsoft Windows verfügbar.
Tricasse
4

Sie sollten sich wahrscheinlich ein Konfigurationsmanagementsystem wie Puppet oder Chef ansehen . Diese können einfach das tun, was Sie oben beschrieben haben und vieles mehr.

EEAA
quelle
1
Vielen Dank. Absolut, ich habe Chef installiert und ausgeführt. Aber es bringt eine Menge Aufwand mit sich, wenn Sie Ihre eigenen Kochbücher schreiben müssen. Ich kenne die Programmiersprache Ruby nicht und mein Fazit war. Es ist einfacher, dies mit einem Shell-Skript für die einfacheren Fälle zu tun (falls möglich).
Markus
Sieht so aus, als würden Puppet und Chef beide ERB für Vorlagen verwenden, und das ist lächerlich einfach. Bei einer Variablen namewird der String <%= name %>in einer Vorlage durch nameden Wert von ersetzt. Wie Sie nameaußerhalb der Vorlage definieren, unterscheidet sich offensichtlich zwischen den beiden Systemen.
Mike Renfro
1
Ja, das Schablonieren (mit Chef) selbst ist absolut einfach. Die Verwendung des Küchenchefs als Framework (und das Schreiben der Kochbücher) erfordert jedoch viel Zeit. Um die Daten in die Vorlagen zu übernehmen, müssen Sie verstehen, wo und wie Chefkoch das "Zusammenführen" von Datenquellen und vielen anderen Dingen verwaltet. Ich habe angefangen, meine eigenen Kochbücher zu schreiben, aber ein Shell-Skript wäre in meinem speziellen Fall 100-mal schneller ...
Markus
Die Infrastruktur für Chef oder Puppet in Gang zu bringen, kann mühsam sein, oder Sie können versuchen, herauszufinden, wie man sie kopflos laufen lässt, was ein lustiges Abenteuer ist. Ansible funktioniert ohne Probleme im Pull- oder Push-Modus, sodass Sie es besser herausfinden und selbst erstellen können. docs.ansible.com/template_module.html
Küken
4

Wenn Sie schlanke und echte Vorlagen anstelle von Shell-Code wünschen, der neue Dateien generiert, sind die üblichen Optionen sed& awkoder perl. Hier ist ein Link: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

Ich würde eine echte Sprache wie Perl, Tcl, Python, Ruby oder etwas anderes in dieser Klasse verwenden. Etwas für Skripte gebaut. Sie alle haben gute, einfache Vorlagenwerkzeuge und jede Menge Beispiele in Google.

Kennzeichen
quelle
4

Ich benutze shtpl . (Privates Projekt von mir, was bedeutet, es ist nicht weit verbreitet. Aber vielleicht möchten Sie es trotzdem testen)

Sie möchten zum Beispiel eine / etc / network / interfaces aus einer csv-Datei generieren und können dies folgendermaßen tun:

CSV-Dateiinhalt (hier 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

Vorlage (hier interfaces.tpl):

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

#% done < "$CSVFILE"

Befehl:

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

Ergebnis:

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

Genießen!

zstegi
quelle
1

Ich habe die Antwort von FooF verbessert, damit der Benutzer doppelte Anführungszeichen nicht manuell entfernen muss:

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

Ich habe kürzlich ein Bash-Skript veröffentlicht, das genau dies mithilfe einer jinja-ähnlichen Vorlagensyntax erreicht. Es heißt Cookie . Hier ist eine Demo:

Cookie-Demo

Bryan Bugyi
quelle
Wie beantwortet dies die Frage?
RalfFriedl
1
@RalfFriedl Ich bin mir nicht sicher, was du meinst. Ich habe die Frage nur noch einmal gelesen und gedacht, ich hätte etwas verpasst, aber ich sehe es nicht. In der Tat, wie funktioniert das nicht beantwortet all Teil der Frage , die gestellt wurde? Es ist fast so, als hätte ich ein Cookie mit dem einzigen Zweck erstellt, diese Frage zu beantworten ... was ich auch getan habe, obwohl ich es damals vielleicht ein bisschen anders formuliert habe. :)
Bryan Bugyi
Ich bin etwas spät dran, um als Mediator zu fungieren , aber vielleicht hat Ralf Sie nur dazu aufgefordert , zu erklären, wie Cookies die Frage beantworten, anstatt zu versuchen, gemeinnützig zu implizieren, dass Sie Ihr eigenes Projekt nicht gut genug kennen, um zu beurteilen, ob es das Problem eines Menschen lösen könnte .
Abathur
0

Ich bin wahrscheinlich zu spät zu dieser Party. Ich stolperte jedoch über das gleiche Problem und entschied mich für die Erstellung meiner eigenen BASH-Template-Engine in ein paar Codezeilen:

Nehmen wir an, Sie haben Folgendes file.template:

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

Und diese rulesDatei:

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

Sie führen diesen Befehl aus:

templater rules < file.template

Du bekommst das:

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

Sie können es installieren, indem Sie:

 bpkg install vicentebolea/bash-templater

Dies ist die Projekt - Website

Vicente Bolea
quelle
0

Um die großartige Antwort von @ FooF zu erweitern (Zeilenumbrüche wurden nicht im Kommentar formatiert), können Sie mithilfe eines heredoc + Steuerzeichens beliebige Zeichen und Dateinamen zulassen :

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

Dies akzeptiert alle Nicht-Null-Zeichen und wird nur vorzeitig abgeschnitten, wenn 3 ^DBytes in einer eigenen Zeile gefunden werden (realistisch nie). zsh unterstützt sogar Nullterminatoren, printf '\x00\x00\x00'würde also funktionieren. templatefunktioniert sogar für verräterische Dateinamen wie:

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

Beachten Sie, dass Shell-Vorlagen beliebige Befehle "erweitern" können, z $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10)) . Mit großer Kraft ...

Bearbeiten: Wenn Sie wirklich nicht möchten, dass Ihre Dateien Nukes auf Sie abfeuern, müssen Sie dies sudo -u nobody shvorher tun (oder einem anderen sicheren Benutzer).

alexchandel
quelle