Voglio convogliare l'output di un file "template" in MySQL, il file ha variabili come ${dbName}
intervallate. Qual è l'utilità della riga di comando per sostituire queste istanze e scaricare l'output sullo standard output?
Voglio convogliare l'output di un file "template" in MySQL, il file ha variabili come ${dbName}
intervallate. Qual è l'utilità della riga di comando per sostituire queste istanze e scaricare l'output sullo standard output?
Risposte:
Sed !
Dato template.txt:
Il numero è $ {i} La parola è $ {parola}
non ci resta che dire:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Grazie a Jonathan Leffler per la segnalazione di passare più -e
argomenti alla stessa sed
invocazione.
cat
. Tutto ciò che serve è sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
ci si aspetta un testo di escape, che è una seccatura.
Ecco una soluzione di yottatsa su una domanda simile che sostituisce solo variabili come $ VAR o $ {VAR} ed è una breve riga
i=32 word=foo envsubst < template.txt
Naturalmente se io e la parola siamo nel tuo ambiente, allora è giusto
envsubst < template.txt
Sul mio Mac sembra che è stato installato come parte di gettext e da MacGPG2
Ecco un miglioramento alla soluzione di Mogsie su una domanda simile, la mia soluzione non richiede che tu faccia scalare le doppie virgolette, lo fa Mogsie, ma la sua è una fodera!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Il potere di queste due soluzioni è che si ottengono solo alcuni tipi di espansioni della shell che non si verificano normalmente $ ((...)), `...` e $ (...), sebbene la barra rovesciata sia un carattere di escape qui, ma non devi preoccuparti che l'analisi abbia un bug e fa più righe bene.
envsubst
non funziona se i tuoi dintorni non vengono esportati.
envsubst
, come suggerisce il nome, riconosce solo le variabili di ambiente , non le variabili di shell . Vale anche la pena notare che si envsubst
tratta di un'utilità GNU e quindi non preinstallata o disponibile su tutte le piattaforme.
Usa /bin/sh
. Creare uno script di shell piccolo che imposta le variabili, quindi analizzare il modello utilizzando la shell stessa. In questo modo (modifica per gestire correttamente le nuove righe):
the number is ${i}
the word is ${word}
#!/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"
#sh script.sh
the number is 1
the word is dog
bash
Verrà eseguito tutto il comando nell'input. Se il modello è: "le parole sono; rm -rf $ HOME" perderai i file.
read
comando, come scritto, taglia gli spazi bianchi iniziali e finali da ogni riga e 'mangia' \
caratteri., (C) usa questo solo se hai completamente fidarsi o controllare l'input, poiché le sostituzioni di comandi ( `…`
o $(…)
) incorporate nell'input consentono l'esecuzione di comandi arbitrari dovuti all'uso di eval
. Infine, c'è una piccola possibilità che echo
confonde l'inizio di una riga per una delle sue opzioni della riga di comando.
Ci stavo ripensando, visto il recente interesse, e penso che lo strumento a cui stavo pensando originariamente fosse m4
il macro processore per autotools. Quindi, invece della variabile che ho specificato in origine, useresti:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
per questo semplice uso di sostituzione / templating variabile, come indicato in altre risposte. m4
è un ottimo strumento, ma è un preprocessore completo con molte più funzioni e quindi complessità che potrebbe non essere necessaria se si desidera semplicemente sostituire alcune variabili.
Template.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
data.sh
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
parser.sh
#!/usr/bin/env bash
# args
declare file_data=$1
declare file_input=$2
declare file_output=$3
source $file_data
eval "echo \"$(< $file_input)\"" > $file_output
./parser.sh data.sh template.txt parsed_file.txt
parsed_file.txt
Variable 1 value: value 1
Variable 2 value: value 2
`…`
o $(…)
) incorporate nell'input consentono l'esecuzione di comandi arbitrari dovuti all'uso eval
e l'esecuzione diretta del codice shell dovuto all'uso source
. Inoltre, le doppie virgolette nell'input vengono scartate silenziosamente e echo
potrebbero confondere l'inizio di una riga per una delle sue opzioni della riga di comando.
Ecco una robusta funzione Bash che, nonostante l'utilizzo eval
, dovrebbe essere sicura da usare.
Tutti ${varName}
i riferimenti alle variabili nel testo di input vengono espansi in base alle variabili della shell chiamante.
Nient'altro viene espanso: né riferimenti a variabili i cui nomi non sono racchiusi in {...}
(come $varName
), né sostituzioni di comandi ( $(...)
e sintassi legacy `...`
), né sostituzioni aritmetiche ( $((...))
e sintassi legacy $[...]
).
Per trattare un $
come un letterale, \
-scapparlo; per esempio:\${HOME}
Si noti che l'input è accettato solo tramite stdin .
Esempio:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Codice sorgente funzione:
expandVarsStrict(){
local line lineEscaped
while IFS= read -r line || [[ -n $line ]]; do # the `||` clause ensures that the last line is read even if it doesn't end with \n
# Escape ALL chars. that could trigger an expansion..
IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//$'\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
done
}
La funzione assume che nessuna 0x1
, 0x2
, 0x3
, e 0x4
caratteri di controllo sono presenti in ingresso, perché tali caratteri. sono usati internamente - poiché la funzione elabora il testo , questo dovrebbe essere un presupposto sicuro.
eval
è abbastanza sicuro da usare.
"
correttamente!)
${FOO:-bar}
o di generare qualcosa solo se è impostato - ${HOME+Home is ${HOME}}
. Sospetto che con una piccola estensione potrebbe anche restituire codici di uscita per variabili mancanti, ${FOO?Foo is missing}
ma al momento tldp.org/LDP/abs/html/parameter-substitution.html ha un elenco di questi se ciò aiuta
Creare rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
E template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Rendering del modello:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, lo stai eseguendo come codice.
eval "echo \"$(cat $1)\""
Funziona alla grande !
ecco la mia soluzione con perl basata sull'ex risposta, sostituisce le variabili d'ambiente:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni esperti di sed e / o AWK che probabilmente sanno come farlo molto più facilmente. Se hai una mappatura più complessa con più di un semplice dbName per i tuoi sostituti potresti estenderlo abbastanza facilmente, ma potresti anche metterlo in uno script Perl standard a quel punto.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Un breve script Perl per fare qualcosa di leggermente più complicato (gestire più chiavi):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:
replace-script < yourfile | mysql
Ecco un modo per fare in modo che la shell esegua la sostituzione, come se il contenuto del file fosse invece digitato tra virgolette doppie.
Utilizzando l'esempio di template.txt con i contenuti:
The number is ${i}
The word is ${word}
La riga seguente farà interpolare la shell del contenuto di template.txt e scriverà il risultato su standard out.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Spiegazione:
i
e word
vengono passati come variabili di ambiente ignorate nell'esecuzione di sh
.sh
esegue il contenuto della stringa che viene passata.echo "
' + " $(cat template.txt)
" + ' "
'"
", $(cat template.txt)
" diventa l'output dicat template.txt
.sh -c
diventa:
echo "The number is ${i}\nThe word is ${word}"
,i
e word
sono le variabili di ambiente specificate.'$(rm -rf ~)'$(rm -rf ~)
le virgolette letterali nel file modello corrisponderanno a quelle che hai aggiunto prima della sua espansione.
'$(echo a)'$(echo a)
. Produce 'a'a
. La cosa principale che sta accadendo è che viene valutato il primo echo a
all'interno di '
, il che potrebbe non essere quello che ti aspetti dal momento che è in '
, ma è lo stesso comportamento di includere '
in una "
stringa tra virgolette.
"
stringa quotata (incluso $(...)
) è il punto.
${varname}
, non altre, espansioni a rischio di sicurezza più elevato.
echo "
, seguita da una stringa a virgolette doppie con i contetn letterali di template.txt
, seguita da un'altra stringa letterale "
, tutte concatenate in un singolo argomento passato a sh -c
. Hai ragione sul fatto che '
non è possibile abbinare (poiché è stato consumato dalla shell esterna anziché passata a quella interna), ma "
certamente può, quindi un modello contenente Gotcha"; rm -rf ~; echo "
potrebbe essere eseguito.
file.tpl:
The following bash function should only replace ${var1} syntax and ignore
other shell special chars such as `backticks` or $var2 or "double quotes".
If I have missed anything - let me know.
script.sh:
template(){
# usage: template file.tpl
while read -r line ; do
line=${line//\"/\\\"}
line=${line//\`/\\\`}
line=${line//\$/\\\$}
line=${line//\\\${/\${}
eval "echo \"$line\"";
done < ${1}
}
var1="*replaced*"
var2="*not replaced*"
template file.tpl > result.txt
\$(date)
while IFS= read -r line; do
come read
comando, altrimenti spogli gli spazi bianchi iniziali e finali da ogni riga di input. Inoltre, echo
potrebbe confondere l'inizio di una riga per una delle sue opzioni della riga di comando, quindi è meglio usarlo printf '%s\n'
. Infine, è più sicuro fare una doppia citazione ${1}
.
Suggerirei di usare qualcosa come Sigil : https://github.com/gliderlabs/sigil
È compilato su un singolo binario, quindi è estremamente facile da installare sui sistemi.
Quindi puoi fare un semplice one-liner come il seguente:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Questo è molto più sicuro eval
e semplice dell'uso di regex osed
cat
e usare <my-file.conf.template
invece in modo da dare sigil
un vero handle di file invece di un FIFO.
Ho trovato questa discussione mentre mi chiedevo la stessa cosa. Mi ha ispirato a questo (attento con i backtick)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
è$(< file)
eval echo "\"$(cat FILE)\""
ma potrebbe non essere all'altezza se le doppie virgolette nell'input vengono scartate.
`…`
o $(…)
) incorporate nell'input consentono l'esecuzione di comandi arbitrari dovuti all'uso eval
.
Molte scelte qui, ma ho pensato che avrei gettato il mio sul mucchio. È basato su perl, ha come target solo le variabili del modulo $ {...}, accetta il file da elaborare come argomento e genera il file convertito su stdout:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Ovviamente non sono davvero una persona perversa, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).
Env::import();
riga: l'importazione è implicita da use
. Inoltre, suggerisco di non creare prima l'intero output in memoria: basta usare print;
invece che $text .= $_;
all'interno del loop e rilasciare il print
comando post-loop .
Può essere fatto in bash se hai il controllo del formato del file di configurazione. Devi solo procurarti (".") Il file di configurazione piuttosto che eseguirne la subshell. Ciò garantisce che le variabili vengano create nel contesto della shell corrente (e continuino ad esistere) piuttosto che nella subshell (dove la variabile scompare quando la subshell esce).
$ cat config.data
export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
export parm_user=pax
export parm_pwd=never_you_mind
$ cat go.bash
. config.data
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
Se il tuo file di configurazione non può essere uno script di shell, puoi semplicemente 'compilarlo' prima di eseguirlo (la compilazione dipende dal tuo formato di input).
$ cat config.data
parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
parm_user=pax # user name
parm_pwd=never_you_mind # password
$ cat go.bash
cat config.data
| sed 's/#.*$//'
| sed 's/[ \t]*$//'
| sed 's/^[ \t]*//'
| grep -v '^$'
| sed 's/^/export '
>config.data-compiled
. config.data-compiled
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
Nel tuo caso specifico, potresti usare qualcosa come:
$ cat config.data
export p_p1=val1
export p_p2=val2
$ cat go.bash
. ./config.data
echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1
Quindi convoglia l'output di go.bash in MySQL e voilà, speriamo che non distruggerai il tuo database :-).
go.bash
), hai la parte sbagliata dello stick - non fanno parte della soluzione, sono solo un modo per mostrare che le variabili vengono impostato correttamente.
Avrai bisogno di qualcosa di più robusto dei suggerimenti attuali perché mentre funzionano per il tuo (per ora) caso d'uso limitato, non saranno sufficienti per situazioni più complesse.
Hai bisogno di un renderer migliore. Hai bisogno del miglior renderer. Hai bisogno di The Renderest!
Dato template.txt:
Ciao, {{person}}!
Correre:
$ person = Bob ./render template.txt
E vedrai l'output
Ciao Bob!
Scrivilo su un file reindirizzando stdout su un file:
$ person = Bob ./render template.txt> rendering.txt
E se ti capita di eseguire il rendering di uno script con $ {} variabili che non vuoi interpolare, The Renderest ti copre senza che tu debba fare altro!
Continua e ottieni la tua copia su https://github.com/relaxdiego/renderest