Szablonowanie w systemie Linux w skrypcie powłoki?

26

co chcę osiągnąć to:

1.) Posiadanie pliku konfiguracyjnego jako szablonu ze zmiennymi takimi jak $ version $ path (na przykład config apache)

2.) Posiadanie skryptu powłoki, który „wypełnia” zmienne szablonu i zapisuje wygenerowany plik na dysku.

Czy jest to możliwe dzięki skryptowi powłoki. Byłbym bardzo wdzięczny, jeśli potrafisz wymienić niektóre polecenia / narzędzia, które mogę wykonać, lub dobre linki.

Markus
źródło

Odpowiedzi:

23

To bardzo możliwe. Bardzo prostym sposobem na wdrożenie tego byłoby, aby plik szablonu był faktycznie skryptem i używał zmiennych powłoki, takich jak

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

Możesz nawet skonfigurować to w wierszu poleceń, określając version=$1i path=$2, abyś mógł go uruchomić w podobny sposób bash script /foo/bar/baz 1.2.3. -Przed EOF powoduje spacje przed linie są ignorowane używać zwykłego <<EOFjeśli nie chcesz tego zachowania.

Innym sposobem na to byłoby skorzystanie z funkcji wyszukiwania i zamiany 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

który zastąpiłby każde wystąpienie ciągów VERSION i PATH. Jeśli istnieją inne powody, dla których te ciągi znajdują się w pliku szablonu, możesz wyszukać i zastąpić VERSION lub% VERSION% lub coś, co prawdopodobnie zostanie przypadkowo uruchomione.

Mtinberg
źródło
18

Nie są potrzebne żadne narzędzia inne niż /bin/sh. Biorąc pod uwagę plik szablonu formularza

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

lub nawet z dołączonym mieszanym kodem powłoki

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)

i parsowalny plik konfiguracyjny powłoki jak

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

łatwo jest to rozszerzyć

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

Rzeczywiście, biorąc pod uwagę ścieżkę do pliku konfiguracyjnego w zmiennej powłoki config_filei ścieżkę do pliku szablonu w template_file, wszystko, co musisz zrobić, to:

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

Jest to chyba ładniejsze niż posiadanie pełnego skryptu powłoki jako pliku szablonu (rozwiązanie @ mtinberg).

Kompletny naiwny program ekspandera szablonów:

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

Spowoduje to wygenerowanie rozszerzenia do standardowego wyjścia; wystarczy przekierować standardowe wyjście do pliku lub zmodyfikować powyższe w oczywisty sposób, aby uzyskać pożądany plik wyjściowy.

Ostrzeżenia: Rozszerzenie pliku szablonu nie działałoby, gdyby plik zawierał nieoznaczone podwójne cudzysłowy ( "). Ze względów bezpieczeństwa prawdopodobnie powinniśmy uwzględnić pewne oczywiste kontrole poczytalności, a nawet lepiej wykonać transformację ucieczki powłoki, jeśli plik szablonu jest generowany przez jednostkę zewnętrzną.

FooF
źródło
1
Wspaniale jest nadal widzieć ludzi aktywnie korzystających ze skryptów powłoki do robienia takich zabawnych rzeczy. Mój kumpel napisał zegar w skrypcie powłoki. github.com/tablespoon/fun/blob/master/cli-clock Rzeczywiście czas zabawy.
pisklęta
2
Kiedy znalazłem tę odpowiedź, gdy chciałem to zrobić sam, wdrożyłem
Pyrocater
9

Najłatwiejszym sposobem, aby to zrobić po prostu w systemie Linux CLI, jest użycie envsubstzmiennych środowiskowych.

Przykładowy plik szablonu apache.tmpl:

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

Biegać envsubst i wyślij wynik do nowego pliku 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

Wydajność:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>
Lirt
źródło
2
Jest częścią GNU gettext i dlatego jest dostępna na wszystkich głównych platformach Unix (w tym macOS X), a także w Microsoft Windows.
tricasse
4

Prawdopodobnie powinieneś zajrzeć do systemu zarządzania konfiguracją, takiego jak Puppet lub Chef . Mogą z łatwością zrobić to, co opisano powyżej i wiele więcej.

EEAA
źródło
1
dzięki. Oczywiście mam zainstalowanego i uruchomionego szefa kuchni. Ale to dodaje dużo kosztów ogólnych, gdy trzeba pisać własne książki kucharskie. Nie znam języka programowania ruby ​​i mój wniosek był taki. łatwiej jest to zrobić za pomocą skryptu powłoki dla łatwiejszych przypadków (jeśli to możliwe).
Markus,
Wygląda na to, że zarówno Puppet, jak i Chef używają ERB do tworzenia szablonów, a od samego początku jest to niezwykle łatwe. Biorąc pod uwagę zmienną name, ciąg <%= name %>w szablonie zostanie zastąpiony namewartością. nameOczywiście sposób definiowania poza szablonem różni się między dwoma systemami.
Mike Renfro,
1
Tak, samo szablonowanie (z szefem kuchni) jest absolutnie łatwe. Ale używanie szefa kuchni jako frameworka (i pisanie książek kucharskich) wymaga dużo czasu. Aby wprowadzić dane do szablonów, musisz zrozumieć, gdzie i jak Chef zarządza „łączeniem” źródeł danych i wielu innych rzeczy. Zacząłem pisać własne książki kucharskie, ale skrypt powłoki w moim przypadku byłby 100 razy szybszy ...
Markus
Uruchomienie infrastruktury dla szefa kuchni lub marionetki może być uciążliwe lub możesz spróbować wymyślić, jak je uruchomić bez głowy, co jest zabawną przygodą. Ansible działa dobrze w trybie ciągnięcia lub wypychania po wyjęciu z pudełka, dzięki czemu może znaleźć lepszą równowagę między jego rozszyfrowaniem i samodzielnym skryptowaniem. docs.ansible.com/template_module.html
pisklęta
4

Jeśli chcesz mieć lekkie i rzeczywiste szablony zamiast kodu powłoki, który generuje nowe pliki, zwykle wybierane są sed& awklub perl. Oto jeden link: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

Ja użyłbym prawdziwego języka, takiego jak perl, tcl, python, ruby ​​lub coś innego w tej klasie. Coś zbudowanego do pisania skryptów. Wszystkie mają dobre, proste narzędzia do tworzenia szablonów i mnóstwo przykładów w google.

znak
źródło
4

Używam shtpl za to. (mój prywatny projekt, co oznacza, że ​​nie jest powszechnie używany. Ale może i tak chcesz go przetestować)

Na przykład, chcesz wygenerować / etc / network / interfejsy z pliku csv, możesz to zrobić w następujący sposób:

Zawartość pliku CSV (tutaj 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

Szablon (tutaj interfaces.tpl):

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

#% done < "$CSVFILE"

Dowództwo:

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

Wynik:

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

Cieszyć się!

zstegi
źródło
1

Poprawiłem odpowiedź FooF, aby użytkownik nie musiał ręcznie usuwać cudzysłowów ręcznie:

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

Niedawno opublikowałem skrypt bash, który osiąga właśnie to przy użyciu składni szablonu podobnej do jinja. To się nazywa ciasteczko . Oto demo:

demo ciasteczka

Bryan Bugyi
źródło
Jak to odpowiada na pytanie?
RalfFriedl
1
@RalfFriedl Nie jestem pewien, co masz na myśli. Właśnie ponownie przeczytałem pytanie, myśląc, że musiałem coś przeoczyć, ale nie widzę tego. W rzeczywistości, w jaki sposób nie odpowiada to na żadną część zadanego pytania? To prawie tak, jakbym zbudował plik cookie wyłącznie w celu udzielenia odpowiedzi na to pytanie ... co zrobiłem, chociaż mogłem wtedy sformułować to nieco inaczej w mojej głowie. :)
Bryan Bugyi
Jestem trochę spóźniony, aby zagrać w mediatora, ale być może Ralf po prostu uprzejmie poprosił cię o wyjaśnienie, w jaki sposób cookie odpowiada na pytanie, zamiast próbować sugerować, że nie znasz wystarczająco swojego projektu, aby ocenić, czy może rozwiązać czyjś problem .
abathur
0

Prawdopodobnie spóźniłem się na to przyjęcie. Jednak natknąłem się na ten sam problem i zdecydowałem się na stworzenie własnego silnika szablonów BASH w kilku wierszach kodu:

Powiedzmy, że masz to file.template:

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

A ten rulesplik:

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

Wykonujesz to polecenie:

templater rules < file.template

Dostajesz to:

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

Możesz go zainstalować:

 bpkg install vicentebolea/bash-templater

To jest strona projektu

Vicente Bolea
źródło
0

Aby rozwinąć świetną odpowiedź @ FooF (nowe wiersze nie zostały sformatowane w komentarzu), używając znaków kontrolnych heredoc +, możesz zezwolić na dowolne znaki i nazwy plików:

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

Akceptuje to dowolny znak inny niż null i obcina się tylko wcześnie, jeśli ^Dnapotkane zostaną 3 bajty na jego własnej linii (realistycznie nigdy). Zsh obsługuje nawet terminatory zerowe, więc printf '\x00\x00\x00'działałoby. templatedziała nawet w przypadku zdradliwych nazw plików, takich jak:

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

Uwaga szablony powłoki mogą „rozszerzać” dowolne polecenia, np $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10)). Z wielką mocą…

Edycja: jeśli naprawdę nie chcesz, aby Twoje pliki uruchamiały w Tobie nukes, po prostu sudo -u nobody sh(lub inny bezpieczny użytkownik) wcześniej.

alexchandel
źródło