Je veux canaliser la sortie d’un fichier “template” vers MySQL, le fichier ayant des variables comme ${dbName}
entrecoupé. Quel est l’utilitaire de ligne de commande pour remplacer ces instances et transférer la sortie vers la sortie standard?
Sed !
Étant donné template.txt:
Le nombre est $ {i} Le mot est $ {word}
nous devons juste dire:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Merci à Jonathan Leffler pour le conseil de passer plusieurs arguments -e
à la même invocation sed
.
Voici une solution de yottatsa sur une question similaire qui ne remplace que des variables telles que $ VAR ou $ {VAR}, et est une version abrégée
i=32 word=foo envsubst < template.txt
Bien sûr, si ma parole et mon environnement sont dans votre environnement, alors c'est juste
envsubst < template.txt
Sur mon Mac, il semble avoir été installé dans le cadre de gettext et de MacGPG2
Voici une amélioration de la solution de mogsie sur une question similaire, ma solution ne vous oblige pas à escaler les guillemets doubles, mais Mogsie est une solution unique!
eval "cat < /dev/null
La puissance de ces deux solutions est que vous n’obtenez que quelques types d’extensions de shell qui ne se produisent pas normalement $ ((...)), `...`, et $ (...), bien que la barre oblique inverse soit une caractère d'échappement ici, mais vous n'avez pas à vous soucier du fait que l'parsing a un bogue, et cela fait plusieurs lignes.
Utilisez /bin/sh
. Créez un petit script shell qui définit les variables, puis parsingz le modèle à l’aide du shell lui-même. Comme ça (edit pour gérer correctement les newlines):
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
Je pensais à cela, compte tenu de l’intérêt récent, et je pense que l’outil auquel je pensais à l’origine était m4
, le processeur de macro pour les outils automatiques. Donc, au lieu de la variable spécifiée à l’origine, vous utiliseriez:
$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
voici ma solution avec perl basée sur la réponse précédente, remplace les variables d’environnement:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Si vous êtes ouvert à utiliser Perl , ce serait ma suggestion. Bien qu’il existe probablement des experts sed et / ou AWK qui savent probablement comment faire cela beaucoup plus facilement. Si vous avez un mappage plus complexe avec plus que dbName pour vos remplacements, vous pouvez l’étendre assez facilement, mais vous pourriez tout aussi bien le placer dans un script Perl standard.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Un court script Perl pour faire quelque chose d’un peu plus compliqué (gérer plusieurs clés):
#!/usr/bin/env perl my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' ); undef $/; my $buf = ; $buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace; print $buf;
Si vous nommez le script ci-dessus comme script de remplacement, il pourrait alors être utilisé comme suit:
replace-script < yourfile | mysql
Voici une fonction robuste de Bash qui, malgré l’utilisation d’ eval
, devrait être sûre à utiliser.
Toutes les références de variable ${varName}
dans le texte d’entrée sont développées en fonction des variables du shell appelant.
Rien d’autre n’est développé: ni les références de variables dont les noms ne sont pas inclus dans {...}
(comme $varName
), ni les substitutions de commandes ( $(...)
et la syntaxe héritée `...`
), ni les substitutions arithmétiques ( $((...))
et la syntaxe héritée $[...]
).
Traiter un $
comme un littéral, \
-escape it par exemple: \${HOME}
Notez que l’entrée n’est acceptée que via stdin .
Exemple:
$ expandVarsSsortingct <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded $HOME is "/Users/jdoe"; `date` and $(ls)
Code source de la fonction:
expandVarsSsortingct(){ 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 sortinggger 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 fonction suppose qu'aucun caractère de contrôle 0x1
, 0x2
, 0x3
et 0x4
n'est présent dans l'entrée, car ces caractères. sont utilisés en interne - puisque la fonction traite le texte , cela devrait être une hypothèse sûre.
Créez rendertemplate.sh
:
#!/usr/bin/env bash eval "echo \"$(cat $1)\""
Et template.tmpl
:
Hello, ${WORLD} Goodbye, ${CHEESE}
Rendez le modèle:
$ export WORLD=Foo $ CHEESE=Bar ./rendertemplate.sh template.tmpl Hello, Foo Goodbye, Bar
fichier.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
Je suggère d’utiliser quelque chose comme Sigil : https://github.com/gliderlabs/sigil
Il est compilé en un seul fichier binary, il est donc extrêmement facile à installer sur les systèmes.
Ensuite, vous pouvez faire un simple liner comme suit:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
C’est beaucoup plus sûr eval
et plus facile que d’utiliser regex ou sed
J’ai trouvé ce fil en me demandant la même chose. Cela m’a inspiré à cela (attention aux backticks)
$ echo $MYTEST pass! $ cat FILE hello $MYTEST world $ eval echo `cat FILE` hello pass! world
Beaucoup de choix ici, mais j’ai pensé que je lancerais le mien sur le tas. Il est basé sur Perl, ne cible que les variables de la forme $ {…}, prend le fichier à traiter comme argument et affiche le fichier converti sur stdout:
use Env; Env::import(); while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; } print "$text";
Bien sûr, je ne suis pas vraiment un perl, donc il pourrait facilement y avoir un défaut fatal (ça marche pour moi).
Cela peut être fait en bash lui-même si vous contrôlez le format du fichier de configuration. Il vous suffit de générer (“.”) Le fichier de configuration plutôt que de le sous-stocker. Cela garantit que les variables sont créées dans le contexte du shell en cours (et continuent d’exister) plutôt que dans le sous-shell (où la variable disparaît lorsque le sous-shell est fermé).
$ 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 ssortingng is " $parm_jdbc echo "Username is " $parm_user echo "Password is " $parm_pwd $ bash go.bash JDBC ssortingng is jdbc:db2://box7.co.uk:5000/INSTA Username is pax Password is never_you_mind
Si votre fichier de configuration ne peut pas être un script shell, vous pouvez simplement le comstackr avant de l’exécuter (la compilation dépend de votre format d’entrée).
$ 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-comstackd . config.data-comstackd echo "JDBC ssortingng is " $parm_jdbc echo "Username is " $parm_user echo "Password is " $parm_pwd $ bash go.bash JDBC ssortingng is jdbc:db2://box7.co.uk:5000/INSTA Username is pax Password is never_you_mind
Dans votre cas spécifique, vous pourriez utiliser quelque chose comme:
$ 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
Puis dirigez la sortie de go.bash vers MySQL et le tour est joué, espérons que vous ne détruirez pas votre firebase database :-).