Come sostituire i segnaposto $ {} in un file di testo?
-
03-07-2019 - |
Domanda
Voglio convogliare l'output di un " template " file in MySQL, il file con variabili come $ {dbName}
intervallate. Qual è l'utilità della riga di comando per sostituire queste istanze e scaricare l'output sullo standard output?
Soluzione
Sed !
Dato template.txt:
The number is ${i} The word is ${word}
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ù argomenti -e
allo stesso invocazione sed
.
Altri suggerimenti
Aggiornamento
Ecco una soluzione da yottatsa su una domanda simile che sostituisce solo variabili come $ VAR o $ {VAR } ed è una breve riga di testo
i=32 word=foo envsubst < template.txt
Naturalmente se i e parola sono nel tuo ambiente, allora è solo
envsubst < template.txt
Sul mio Mac sembra che sia stato installato come parte di gettext e da MacGPG2
Vecchia risposta
Ecco un miglioramento della soluzione da mogsie su una domanda simile, la mia soluzione non richiede che tu esegua l'escalation del doppio virgolette, fa mogsie, ma la sua è una fodera!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
La potenza di queste due soluzioni è che si ottengono solo alcuni tipi di espansioni della shell che non si verificano normalmente $ ((...)), `...` e $ (...), sebbene backslash è un carattere di escape qui, ma non devi preoccuparti che l'analisi abbia un bug e fa più righe bene.
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):
File template.txt:
the number is ${i}
the word is ${word}
File 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"
Output:
#sh script.sh
the number is 1
the word is dog
Ci stavo ripensando, visto il recente interesse, e penso che lo strumento a cui stavo inizialmente pensando fosse m4
, il processore macro per autotools. Quindi, invece della variabile che ho specificato in origine, useresti:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
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
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
Ecco una robusta funzione Bash che - nonostante l'uso di eval
- dovrebbe essere sicura da usare.
Tutti i riferimenti alle variabili $ {varName}
nel testo di input vengono espansi in base alle variabili della shell chiamante.
Nient'altro viene espanso: nessuno dei riferimenti di variabili i cui nomi sono non racchiuso in {...}
(come $ varName
), né sostituzioni di comandi ( $ (...)
e sintassi legacy `...`
), né sostituzioni aritmetiche ( $ ( (...))
e sintassi legacy $ [...]
).
Per trattare un $
come un letterale, \
-escape; per esempio:. \ $ {HOME}
Nota 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 '`([
La funzione presuppone che nessun carattere di controllo 0x1
, 0x2
, 0x3
e 0x4
sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.
'\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//
La funzione presuppone che nessun carattere di controllo 0x1
, 0x2
, 0x3
e 0x4
sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.
\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([
La funzione presuppone che nessun carattere di controllo 0x1
, 0x2
, 0x3
e 0x4
sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.
done
}
La funzione presuppone che nessun carattere di controllo 0x1
, 0x2
, 0x3
e 0x4
sia presente nell'input, perché quei caratteri. sono utilizzati internamente, poiché la funzione elabora testo , questo dovrebbe essere un presupposto sicuro.
Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti 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/\$\{ Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti 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):
replace-script < yourfile | mysql
Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:
<*>\}/$replace{ Se sei aperto all'utilizzo di Perl , questo sarebbe il mio suggerimento. Anche se probabilmente ci sono alcuni sed e / o AWK esperti 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):
<*>
Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:
<*>}/g for keys %replace;
print $buf;
Se si nomina lo script sopra come sostituire-script, potrebbe essere utilizzato come segue:
<*> Crea 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
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
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 di eval
e più semplice dell'uso di regex o s<
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
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(<>) { 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:
<*>
Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).
=~ s/(\${\w+})/$1/eeg; $text .= 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:
<*>
Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).
; }
print "$text";
Ovviamente non sono davvero una persona perl, quindi potrebbe esserci facilmente un difetto fatale (funziona per me però).
Può essere fatto in bash stesso se si ha il controllo del formato del file di configurazione. Devi solo procurarti (". & Quot;) il file di configurazione piuttosto che sotto-shell. 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 '^
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 :-).
| 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:
<*>Quindi convoglia l'output di go.bash in MySQL e voilà, speriamo che non distruggerai il tuo database :-).
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
eword
vengono passati come variabili di ambiente ignorate nell'esecuzione dish
. -
sh
esegue il contenuto della stringa che viene passata. - Le stringhe scritte una accanto all'altra diventano una stringa, quella stringa è:
- '
echo "
' + "$ (cat template.txt)
" + '"
'
- '
- Poiché la sostituzione è compresa tra
"
, "$ (cat template.txt)
" diventa l'output dicat template.txt
. - Quindi il comando eseguito da
sh -c
diventa:-
echo " Il numero è $ {i} \ nLa parola è $ {word} "
, - dove
i
eword
sono le variabili di ambiente specificate.
-