Bash Templating: come creare file di configurazione da modelli con Bash?


134

Sto scrivendo uno script per automatizzare la creazione di file di configurazione per Apache e PHP per il mio server web. Non voglio usare alcuna GUI come CPanel o ISPConfig.

Ho alcuni modelli di file di configurazione di Apache e PHP. Lo script Bash deve leggere i modelli, effettuare la sostituzione delle variabili e generare modelli analizzati in alcune cartelle. Qual è il modo migliore per farlo? Posso pensare a diversi modi. Qual è il migliore o potrebbe esserci qualche modo migliore per farlo? Voglio farlo in puro Bash (ad esempio in PHP è facile)

1) Come sostituire i segnaposto $ {} in un file di testo?

Template.txt:

the number is ${i}
the word is ${word}

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"

A proposito, come posso reindirizzare l'output su file esterno qui? Devo sfuggire a qualcosa se le variabili contengono, diciamo, virgolette?

2) Usare cat & sed per sostituire ogni variabile con il suo valore:

Dato template.txt:

The number is ${i}
The word is ${word}

Comando:

cat template.txt | sed -e "s/\${i}/1/" | sed -e "s/\${word}/dog/"

Mi sembra male a causa della necessità di sfuggire a molti simboli diversi e con molte variabili la linea sarà troppo lunga.

Riesci a pensare a qualche altra soluzione elegante e sicura?


Risposte:


61

Puoi usare questo:

perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < template.txt

per sostituire tutte le ${...}stringhe con le variabili di ambiente corrispondenti (non dimenticare di esportarle prima di eseguire questo script).

Per puro bash dovrebbe funzionare (supponendo che le variabili non contengano $ {...} stringhe):

#!/bin/bash
while read -r line ; do
    while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
        LHS=${BASH_REMATCH[1]}
        RHS="$(eval echo "\"$LHS\"")"
        line=${line//$LHS/$RHS}
    done
    echo "$line"
done

. Soluzione che non si blocca se RHS fa riferimento a una variabile che fa riferimento a se stessa:

#!/bin/bash
line="$(cat; echo -n a)"
end_offset=${#line}
while [[ "${line:0:$end_offset}" =~ (.*)(\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})(.*) ]] ; do
    PRE="${BASH_REMATCH[1]}"
    POST="${BASH_REMATCH[4]}${line:$end_offset:${#line}}"
    VARNAME="${BASH_REMATCH[3]}"
    eval 'VARVAL="$'$VARNAME'"'
    line="$PRE$VARVAL$POST"
    end_offset=${#PRE}
done
echo -n "${line:0:-1}"

ATTENZIONE : non conosco un modo per gestire correttamente l'input con NUL in bash o preservare la quantità di nuove righe finali. L'ultima variante è presentata così com'è perché le shell "amano" l'input binario:

  1. read interpreterà le barre rovesciate.
  2. read -r non interpreterà le barre rovesciate, ma lascerà comunque l'ultima riga se non termina con una nuova riga.
  3. "$(…)"spogliamo tutte le nuove righe finali presenti, quindi finisco con ; echo -n ae uso echo -n "${line:0:-1}": questo lascia cadere l'ultimo carattere (che è a) e conserva tutte le nuove righe finali presenti nell'input (incluso no).

3
Vorrei passare [^}]a [A-Za-Z_][A-Za-z0-9_]nella versione bash per evitare che la shell vada oltre la rigorosa sostituzione (ad esempio se ha tentato di elaborare ${some_unused_var-$(rm -rf $HOME)}).
Chris Johnsen,

2
@FractalizeR potresti voler cambiare $&la soluzione perl in "": prima lascia ${...}intatta se non riesce a sostituire, in secondo luogo sostituisce con stringa vuota.
ZyX,

5
NOTA: Apparentemente a c'è stato un cambiamento da bash 3.1 a 3.2 (e versioni successive) in cui le singole virgolette intorno alla regex - trattano il contenuto della regex come una stringa letterale. Quindi la regex sopra dovrebbe essere ... (\ $ \ {[a-zA-Z _] [a-zA-Z_0-9] * \}) stackoverflow.com/questions/304864/…
Blue Waters

2
Per fare in modo che il whileciclo legga l'ultima riga anche se non è terminato da una nuova riga, utilizzare while read -r line || [[ -n $line ]]; do. Inoltre, il tuo readcomando elimina gli spazi bianchi iniziali e finali da ciascuna riga; per evitarlo, usawhile IFS= read -r line || [[ -n $line ]]; do
mklement0

2
Solo per notare un vincolo per chi è alla ricerca di una soluzione completa: queste soluzioni altrimenti utili non consentono di proteggere selettivamente i riferimenti variabili dall'espansione (ad esempio \ eliminandoli).
mklement0

138

Provare envsubst

FOO=foo
BAR=bar
export FOO BAR

envsubst <<EOF
FOO is $FOO
BAR is $BAR
EOF

12
Solo per riferimento, envsubstnon è necessario quando si utilizza un heredoc poiché bash tratta l'eredoc come una stringa letterale a doppia virgoletta e interpola già le variabili in essa. È un'ottima scelta quando vuoi leggere il modello da un altro file. Un buon sostituto per i più ingombranti m4.
Beporter,

2
Sono stato molto piacevolmente sorpreso di conoscere questo comando. Stavo cercando di mettere insieme le funzionalità di envsubst manualmente senza successo. Grazie yottatsa!
Tim Stewart,

4
Nota: envsubstè un'utilità GNU gettext e in realtà non è poi così robusta (poiché gettext è pensato per localizzare i messaggi umani). Soprattutto, non riconosce le sostituzioni $ {VAR} con escape backslash (quindi non è possibile avere un modello che utilizza sostituzioni $ VAR in fase di esecuzione, come uno script di shell o un file conf Nginx). Vedi la mia risposta per una soluzione che gestisce gli escape backslash.
Stuart P. Bentley,

4
@beporter In questo caso, se si desidera passare questo modello a envsubst per qualche motivo, si desidera utilizzare <<"EOF", che non interpola le variabili (i terminatori citati sono come le virgolette singole di heredocs).
Stuart P. Bentley

2
L'ho usato come: cat template.txt | envsubst
truthadjustr

47

envsubst era nuovo per me. Fantastico.

Per la cronaca, usare un heredoc è un ottimo modo per modellare un file conf.

STATUS_URI="/hows-it-goin";  MONITOR_IP="10.10.2.15";

cat >/etc/apache2/conf.d/mod_status.conf <<EOF
<Location ${STATUS_URI}>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from ${MONITOR_IP}
</Location>
EOF

1
lo preferisco meglio di quello envsubstche mi ha salvato dall'aggiunta apt-get install gettext-basenel mio Dockerfile
truthadjustr

La shell come uno script simile a un modello, tuttavia, senza alcuna installazione di libreria esterna né stress da far fronte a espressioni difficili.
千 木 郷

35

Sono d'accordo con l'utilizzo di sed: è lo strumento migliore per la ricerca / sostituzione. Ecco il mio approccio:

$ cat template.txt
the number is ${i}
the dog's name is ${name}

$ cat replace.sed
s/${i}/5/
s/${name}/Fido/

$ sed -f replace.sed template.txt > out.txt

$ cat out.txt
the number is 5
the dog's name is Fido

1
Ciò richiede un file temporaneo per la stringa di sostituzione, giusto? C'è un modo per farlo senza file temporanei?
Vladislav Rastrusny,

@FractalizeR: alcune versioni di sed hanno -iun'opzione (modifica file in atto) che è simile all'opzione perl . Controlla la manpage per la tua sed .
Chris Johnsen,

@FractalizeR Sì, sed -i sostituirà in linea. Se ti senti a tuo agio con Tcl (un altro linguaggio di scripting), dai un'occhiata a questo thread: stackoverflow.com/questions/2818130/…
Hai Vu,

Ho creato il sostituto.sed da un file di proprietà con il seguente comando sed: sed -e 's / ^ / s \ / $ {/ g' -e 's / = /} \ // g' -e 's / $ / \ // g 'the.properties> replace.sed
Jaap D

Il codice di @hai vu crea un programma sed e passa quel programma usando il flag -f di sed. Se lo desideri, puoi invece passare in ciascuna riga del programma sed in sed usando i flag -e. FWIW Mi piace l'idea di usare sed per il templating.
Andrew Allbright

23

Penso che eval funzioni davvero bene. Gestisce modelli con interruzioni di riga, spazi bianchi e ogni sorta di roba bash. Se hai il pieno controllo sui modelli stessi, ovviamente:

$ cat template.txt
variable1 = ${variable1}
variable2 = $variable2
my-ip = \"$(curl -s ifconfig.me)\"

$ echo $variable1
AAA
$ echo $variable2
BBB
$ eval "echo \"$(<template.txt)\"" 2> /dev/null
variable1 = AAA
variable2 = BBB
my-ip = "11.22.33.44"

Questo metodo dovrebbe essere usato con cura, ovviamente, poiché eval può eseguire codice arbitrario. Eseguirlo come root è praticamente fuori discussione. Le citazioni nel modello devono essere evase, altrimenti verranno consumate da eval.

È inoltre possibile utilizzare qui i documenti, se si preferisce cataecho

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

@plockc ha dimostrato una soluzione che evita il problema di evasione della citazione di bash:

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

Modifica: parte rimossa sull'esecuzione come root usando sudo ...

Modifica: aggiunto commento su come le citazioni devono essere evase, aggiunta la soluzione di plockc al mix!


Questo elimina le virgolette che hai nel tuo modello e non sostituirà le virgolette singole, quindi a seconda del formato del modello, potresti portare a bug sottili. Questo è probabilmente applicabile a qualsiasi metodo di template basato su Bash, comunque.
Alex B,

I modelli IMHO basati su Bash sono una follia, dal momento che devi essere un programmatore bash per capire cosa sta facendo il tuo modello! Ma grazie per il commento!
mogsie,

@AlexB: Questo approccio sarà sostituire tra virgolette singole, come sono i personaggi appena letterali all'interno della stringa che racchiude virgolette doppie invece di delimitatori di stringa quando il evalEd echo / catcomanda i processi di loro; provare eval "echo \"'\$HOME'\"".
mklement0

21

Ho una soluzione bash come mogsie ma con heredoc invece di herestring per permetterti di evitare le doppie virgolette

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

4
Questa soluzione supporta l' espansione dei parametri di Bash nel modello. Ai miei preferiti sono richiesti parametri con ${param:?}e annidamento del testo attorno a parametri opzionali. Esempio: non si ${DELAY:+<delay>$DELAY</delay>}espande fino a quando DELAY non è definito e <delay> 17 </delay> quando DELAY = 17.
Eric Bolinger,

2
Oh! E il delimitatore EOF può usare una stringa dinamica, come il PID _EOF_$$.
Eric Bolinger,

1
@ mklement0 Una soluzione alternativa per il trascinamento di nuove righe è quella di utilizzare alcune espansioni come ad esempio una variabile vuota $trailing_newlineo utilizzare $NL5e assicurarsi che vengano espanse come 5 nuove righe.
xebeche,

@xebeche: Sì, ponendo quello che suggeriscono proprio alla fine dentrotemplate.txt avrebbe lavorato al fine di preservare ritorni a capo.
mklement0,

1
Una soluzione elegante, ma nota che la sostituzione del comando eliminerà qualsiasi nuova riga finale dal file di input, sebbene in genere ciò non costituisca un problema. Un altro caso limite: a causa dell'uso di eval, se template.txtcontiene EOFsu una riga a sé stante, terminerà prematuramente il here-doc e quindi interromperà il comando. (Punta del cappello a @xebeche).
mklement0

18

Modifica 6 gennaio 2017

Avevo bisogno di mantenere doppie virgolette nel mio file di configurazione, quindi doppia evasione di doppie virgolette con sed aiuta:

render_template() {
  eval "echo \"$(sed 's/\"/\\\\"/g' $1)\""
}

Non riesco a pensare di continuare a seguire nuove linee, ma le linee vuote nel mezzo vengono mantenute.


Sebbene sia un vecchio argomento, IMO ho scoperto una soluzione più elegante qui: http://pempek.net/articles/2013/07/08/bash-sh-as-template-engine/

#!/bin/sh

# render a template configuration file
# expand variables + preserve formatting
render_template() {
  eval "echo \"$(cat $1)\""
}

user="Gregory"
render_template /path/to/template.txt > path/to/configuration_file

Tutti i crediti a Grégory Pakosz .


Ciò rimuove le doppie virgolette dall'input e, se ci sono più newline finali nel file di input, le sostituisce con una singola.
mklement0

2
Avevo bisogno di altre due barre rovesciate per farlo funzionare, vale a dire eval "echo \"$(sed 's/\"/\\"/g' $1)\""
David Bau,

Sfortunatamente, questo approccio non consente di modellare file php (che contengono $variables).
IStranger

10

Invece di reinventare la ruota, vai con envsubst Può essere usato in quasi tutti gli scenari, ad esempio costruendo file di configurazione da variabili d'ambiente in contenitori docker.

Se su Mac assicurati di avere homebrew , collegalo da gettext:

brew install gettext
brew link --force gettext

./template.cfg

# We put env variables into placeholders here
this_variable_1 = ${SOME_VARIABLE_1}
this_variable_2 = ${SOME_VARIABLE_2}

./.env:

SOME_VARIABLE_1=value_1
SOME_VARIABLE_2=value_2

./configure.sh

#!/bin/bash
cat template.cfg | envsubst > whatever.cfg

Ora basta usarlo:

# make script executable
chmod +x ./configure.sh
# source your variables
. .env
# export your variables
# In practice you may not have to manually export variables 
# if your solution depends on tools that utilise .env file 
# automatically like pipenv etc. 
export SOME_VARIABLE_1 SOME_VARIABLE_2
# Create your config file
./configure.sh

questa sequenza di invocazione envsubstfunziona davvero.
Alex

Per chiunque cerchi, envsubstnon funziona su MacOS, avresti bisogno di installarlo utilizzando homebrew: brew install gettext.
Matt

9

Una versione più lunga ma più solida della risposta accettata:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\})?;substr($1,0,int(length($1)/2)).($2&&length($1)%2?$2:$ENV{$3||$4});eg' template.txt

Questo espande tutte le istanze di $VAR o ${VAR} ai loro valori di ambiente (o, se non sono definiti, la stringa vuota).

Sfugge correttamente alle barre rovesciate e accetta un $ sfuggito alla barra rovesciata per inibire la sostituzione (a differenza di envsubst, che, a quanto pare, non lo fa ).

Quindi, se il tuo ambiente è:

FOO=bar
BAZ=kenny
TARGET=backslashes
NOPE=engi

e il tuo modello è:

Two ${TARGET} walk into a \\$FOO. \\\\
\\\$FOO says, "Delete C:\\Windows\\System32, it's a virus."
$BAZ replies, "\${NOPE}s."

il risultato sarebbe:

Two backslashes walk into a \bar. \\
\$FOO says, "Delete C:\Windows\System32, it's a virus."
kenny replies, "${NOPE}s."

Se si desidera evitare solo barre rovesciate prima di $ (è possibile scrivere "C: \ Windows \ System32" in un modello invariato), utilizzare questa versione leggermente modificata:

perl -pe 's;(\\*)(\$([a-zA-Z_][a-zA-Z_0-9]*)|\$\{([a-zA-Z_][a-zA-Z_0-9]*)\});substr($1,0,int(length($1)/2)).(length($1)%2?$2:$ENV{$3||$4});eg' template.txt

8

Lo avrei fatto in questo modo, probabilmente meno efficiente, ma più facile da leggere / mantenere.

TEMPLATE='/path/to/template.file'
OUTPUT='/path/to/output.file'

while read LINE; do
  echo $LINE |
  sed 's/VARONE/NEWVALA/g' |
  sed 's/VARTWO/NEWVALB/g' |
  sed 's/VARTHR/NEWVALC/g' >> $OUTPUT
done < $TEMPLATE

10
Puoi farlo senza leggere riga per riga e con una sola invocazione sed:sed -e 's/VARONE/NEWVALA/g' -e 's/VARTWO/NEWVALB/g' -e 's/VARTHR/NEWVALC/g' < $TEMPLATE > $OUTPUT
Brandon Bloom,

8

Se si desidera utilizzare i modelli Jinja2 , vedere questo progetto: j2cli .

Supporta:

  • Modelli da file JSON, INI, YAML e flussi di input
  • Templating da variabili d'ambiente

5

Prendendo la risposta da ZyX usando pure bash ma con il nuovo stile regex matching e la sostituzione indiretta dei parametri diventa:

#!/bin/bash
regex='\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}'
while read line; do
    while [[ "$line" =~ $regex ]]; do
        param="${BASH_REMATCH[1]}"
        line=${line//${BASH_REMATCH[0]}/${!param}}
    done
    echo $line
done

5

Se usare Perl è un'opzione e ti accontenti di basare le espansioni solo sulle variabili di ambiente (al contrario di tutte le variabili di shell ), considera la solida risposta di Stuart P. Bentley .

Questa risposta mira a fornire una soluzione solo bash che - nonostante l'uso di eval- dovrebbe essere sicura da usare .

Gli obiettivi sono:

  • Supporta l'espansione di entrambi ${name}e $nameriferimenti variabili.
  • Prevenire tutte le altre espansioni:
    • sostituzioni di comandi ( $(...)e sintassi legacy `...`)
    • sostituzioni aritmetiche ( $((...))e sintassi legacy $[...]).
  • Consenti la soppressione selettiva dell'espansione variabile mediante il prefisso con \( \${name}).
  • Preserva caratteri speciali. nell'input, in particolare "e nelle \istanze.
  • Consenti input tramite argomenti o tramite stdin.

FunzioneexpandVars() :

expandVars() {
  local txtToEval=$* txtToEvalEscaped
  # If no arguments were passed, process stdin input.
  (( $# == 0 )) && IFS= read -r -d '' txtToEval
  # Disable command substitutions and arithmetic expansions to prevent execution
  # of arbitrary commands.
  # Note that selectively allowing $((...)) or $[...] to enable arithmetic
  # expressions is NOT safe, because command substitutions could be embedded in them.
  # If you fully trust or control the input, you can remove the `tr` calls below
  IFS= read -r -d '' txtToEvalEscaped < <(printf %s "$txtToEval" | tr '`([' '\1\2\3')
  # Pass the string to `eval`, escaping embedded double quotes first.
  # `printf %s` ensures that the string is printed without interpretation
  # (after processing by by bash).
  # The `tr` command reconverts the previously escaped chars. back to their
  # literal original.
  eval printf %s "\"${txtToEvalEscaped//\"/\\\"}\"" | tr '\1\2\3' '`(['
}

Esempi:

$ expandVars '\$HOME="$HOME"; `date` and $(ls)'
$HOME="/home/jdoe"; `date` and $(ls)  # only $HOME was expanded

$ printf '\$SHELL=${SHELL}, but "$(( 1 \ 2 ))" will not expand' | expandVars
$SHELL=/bin/bash, but "$(( 1 \ 2 ))" will not expand # only ${SHELL} was expanded
  • Per motivi di prestazioni, la funzione legge tutti gli input di stdin contemporaneamente in memoria, ma è facile adattare la funzione ad un approccio riga per riga.
  • Supporta anche espansioni di variabili non di base come ${HOME:0:10}, purché non contengano comandi incorporati o sostituzioni aritmetiche, come${HOME:0:$(echo 10)}
    • Tali sostituzioni incorporate in realtà interrompono la funzione (poiché tutte $(e le `istanze sono evitate ciecamente).
    • Allo stesso modo, riferimenti variabili non validi come ${HOME(chiusura mancante }) interrompono la funzione.
  • A causa della gestione bash di stringhe tra virgolette, le barre rovesciate vengono gestite come segue:
    • \$name impedisce l'espansione.
    • Un singolo \non seguito da $viene conservato così com'è.
    • Se si desidera rappresentare più \ istanze adiacenti , è necessario raddoppiarle ; per esempio:
      • \\-> \- lo stesso di solo\
      • \\\\ -> \\
    • Caratteri l'ingresso non deve contenere la seguente (raramente usato), che vengono utilizzati per scopi interni: 0x1, 0x2, 0x3.
  • C'è una preoccupazione in gran parte ipotetica che se bash dovesse introdurre una nuova sintassi di espansione, questa funzione potrebbe non impedire tali espansioni - vedi sotto per una soluzione che non usa eval.

Se stai cercando una soluzione più restrittiva che supporti solo le ${name}espansioni - ovvero, con parentesi graffe obbligatorie , ignorando i $nameriferimenti - vedi questa mia risposta .


Ecco una versione migliorata della evalsoluzione solo-bash, libera dalla risposta accettata :

I miglioramenti sono:

  • Supporto per l'espansione di entrambi ${name}e $nameriferimenti variabili.
  • Supporto per \-escape riferimenti a variabili che non dovrebbero essere espansi.
  • A differenza della evalsoluzione basata sopra,
    • le espansioni non di base vengono ignorate
    • i riferimenti alle variabili non validi vengono ignorati (non interrompono lo script)
 IFS= read -d '' -r lines # read all input from stdin at once
 end_offset=${#lines}
 while [[ "${lines:0:end_offset}" =~ (.*)\$(\{([a-zA-Z_][a-zA-Z_0-9]*)\}|([a-zA-Z_][a-zA-Z_0-9]*))(.*) ]] ; do
      pre=${BASH_REMATCH[1]} # everything before the var. reference
      post=${BASH_REMATCH[5]}${lines:end_offset} # everything after
      # extract the var. name; it's in the 3rd capture group, if the name is enclosed in {...}, and the 4th otherwise
      [[ -n ${BASH_REMATCH[3]} ]] && varName=${BASH_REMATCH[3]} || varName=${BASH_REMATCH[4]}
      # Is the var ref. escaped, i.e., prefixed with an odd number of backslashes?
      if [[ $pre =~ \\+$ ]] && (( ${#BASH_REMATCH} % 2 )); then
           : # no change to $lines, leave escaped var. ref. untouched
      else # replace the variable reference with the variable's value using indirect expansion
           lines=${pre}${!varName}${post}
      fi
      end_offset=${#pre}
 done
 printf %s "$lines"

5

Ecco un'altra soluzione pura bash:

  • sta usando heredoc, quindi:
    • la complessità non aumenta a causa della sintassi aggiuntiva richiesta
    • il modello può includere il codice bash
      • ciò ti consente anche di indentare correttamente le cose. Vedi sotto.
  • non usa eval, quindi:
    • nessun problema con il rendering delle righe vuote finali
    • nessun problema con le virgolette nel modello

$ cat code

#!/bin/bash
LISTING=$( ls )

cat_template() {
  echo "cat << EOT"
  cat "$1"
  echo EOT
}

cat_template template | LISTING="$LISTING" bash

$ cat template (con nuove righe finali e virgolette doppie)

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
$( echo "$LISTING" | sed 's/^/        /' )
      <pre>
    </p>
  </body>
</html>

produzione

<html>
  <head>
  </head>
  <body> 
    <p>"directory listing"
      <pre>
        code
        template
      <pre>
    </p>
  </body>
</html>

4

Ecco un'altra soluzione: generare uno script bash con tutte le variabili e il contenuto del file modello, tale script sarebbe simile al seguente:

word=dog           
i=1                
cat << EOF         
the number is ${i} 
the word is ${word}

EOF                

Se alimentiamo questo script in bash, produrrebbe l'output desiderato:

the number is 1
the word is dog

Ecco come generare quello script e alimentare quello script in bash:

(
    # Variables
    echo word=dog
    echo i=1

    # add the template
    echo "cat << EOF"
    cat template.txt
    echo EOF
) | bash

Discussione

  • Le parentesi aprono una sotto-shell, il suo scopo è quello di raggruppare tutto l'output generato
  • All'interno della sotto shell generiamo tutte le dichiarazioni delle variabili
  • Anche nella sotto shell, generiamo il file cat comando con HEREDOC
  • Infine, alimentiamo l'output della sub shell per bash e produciamo l'output desiderato
  • Se si desidera reindirizzare questo output in un file, sostituire l'ultima riga con:

    ) | bash > output.txt

3

Questa pagina descrive una risposta con awk

awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < input.txt > output.txt

Ciò mantiene intatte tutte le virgolette. Grande!
Pepster,

3

Custodia perfetta per shtpl . (progetto mio, quindi non è ampiamente utilizzato e manca di documentazione. Ma ecco la soluzione che offre comunque. Ti consigliamo di testarlo.)

Esegui solo:

$ i=1 word=dog sh -c "$( shtpl template.txt )"

Il risultato è:

the number is 1
the word is dog

Divertiti.


1
Se è una schifezza, viene comunque ridimensionata. E io sto bene con quello. Ma ok, punto preso, che non è chiaramente visibile, che in realtà è il mio progetto. Andando a renderlo più visibile in futuro. Grazie comunque per il tuo commento e il tuo tempo.
zstegi,

Voglio aggiungere, che ieri ho davvero cercato i casi d'uso, dove shtpl sarebbe una soluzione perfetta. Sì, ero annoiato ...
zstegi

3
# Usage: template your_file.conf.template > your_file.conf
template() {
        local IFS line
        while IFS=$'\n\r' read -r line ; do
                line=${line//\\/\\\\}         # escape backslashes
                line=${line//\"/\\\"}         # escape "
                line=${line//\`/\\\`}         # escape `
                line=${line//\$/\\\$}         # escape $
                line=${line//\\\${/\${}       # de-escape ${         - allows variable substitution: ${var} ${var:-default_value} etc
                # to allow arithmetic expansion or command substitution uncomment one of following lines:
#               line=${line//\\\$\(/\$\(}     # de-escape $( and $(( - allows $(( 1 + 2 )) or $( command ) - UNSECURE
#               line=${line//\\\$\(\(/\$\(\(} # de-escape $((        - allows $(( 1 + 2 ))
                eval "echo \"${line}\"";
        done < "$1"
}

Questa è la funzione bash pura regolabile a proprio piacimento, utilizzata in produzione e non deve interrompere alcun input. Se si rompe, fammi sapere.



0

Ecco una funzione bash che preserva gli spazi bianchi:

# Render a file in bash, i.e. expand environment variables. Preserves whitespace.
function render_file () {
    while IFS='' read line; do
        eval echo \""${line}"\"
    done < "${1}"
}

0

Ecco uno perlscript modificato basato su alcune delle altre risposte:

perl -pe 's/([^\\]|^)\$\{([a-zA-Z_][a-zA-Z_0-9]*)\}/$1.$ENV{$2}/eg' -i template

Funzionalità (in base alle mie esigenze, ma dovrebbe essere facile da modificare):

  • Salta le espansioni di parametri con escape (ad esempio \ $ {VAR}).
  • Supporta espansioni di parametri del modulo $ {VAR}, ma non $ VAR.
  • Sostituisce $ {VAR} con una stringa vuota se non è presente VAR envar.
  • Supporta solo az, AZ, 0-9 e caratteri di sottolineatura nel nome (escluse le cifre nella prima posizione).

0

Guarda qui lo script python di sostituzione delle variabili semplici: https://github.com/jeckep/vsubst

È molto semplice da utilizzare:

python subst.py --props secure.properties --src_path ./templates --dst_path ./dist

0

Per dare seguito alla risposta di Plockc in questa pagina, ecco una versione adatta per trattini, per quelli di voi che vogliono evitare i bashismi.

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

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.