Je veux diriger la sortie d'un fichier "modèle" dans MySQL, le fichier ayant des variables comme ${dbName}
intercalées. Quel est l'utilitaire de ligne de commande pour remplacer ces instances et vider la sortie sur la sortie standard?
Je veux diriger la sortie d'un fichier "modèle" dans MySQL, le fichier ayant des variables comme ${dbName}
intercalées. Quel est l'utilitaire de ligne de commande pour remplacer ces instances et vider la sortie sur la sortie standard?
Réponses:
Sed !
Template.txt donné:
Le nombre est $ {i} Le mot est $ {word}
il suffit de dire:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Merci à Jonathan Leffler pour l'astuce de passer plusieurs -e
arguments à la même sed
invocation.
cat
. Tout ce dont vous avez besoin est sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
attendra un texte échappé, ce qui est un problème.
Voici une solution de yottatsa sur une question similaire qui ne remplace que des variables comme $ VAR ou $ {VAR}, et est un bref one-liner
i=32 word=foo envsubst < template.txt
Bien sûr, si je et le mot sont dans votre environnement, alors c'est juste
envsubst < template.txt
Sur mon Mac, il semble qu'il a é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 des guillemets doubles, c'est le cas de mogsie, mais la sienne est une ligne!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /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'analyse a un bogue, et il fait très bien plusieurs lignes.
envsubst
ne fonctionne pas si vos envars ne sont pas exportés.
envsubst
, comme son nom l'indique, ne reconnaît que les variables d' environnement , pas les variables shell . Il convient également de noter qu'il envsubst
s'agit d'un utilitaire GNU , et donc non préinstallé ou disponible sur toutes les plates-formes.
Utilisez /bin/sh
. Créez un petit script shell qui définit les variables, puis analysez le modèle à l'aide du shell lui-même. Comme ça (modifier pour gérer correctement les nouvelles lignes):
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
commandes de l'entrée seront exécutées. Si le modèle est: "les mots sont; rm -rf $ HOME" vous perdrez des fichiers.
read
commande, telle qu'elle est écrite, supprime les espaces de début et de fin de chaque ligne et `` mange '' les \
caractères., (C) ne l'utilise que si vous faites confiance ou contrôlez l'entrée, car les substitutions de commandes ( `…`
ou $(…)
) intégrées dans l'entrée permettent l'exécution de commandes arbitraires en raison de l'utilisation de eval
. Enfin, il y a une petite chance que echo
le début d'une ligne soit confondu avec l'une de ses options de ligne de commande.
J'y réfléchissais à nouveau, 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 macros pour les outils automatiques. Donc, au lieu de la variable que j'ai spécifiée à l'origine, vous utiliseriez:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
pour cette simple utilisation de remplacement de variable / modèle, comme mentionné dans d'autres réponses. m4
est un excellent outil, mais c'est un préprocesseur complet avec beaucoup plus de fonctionnalités et donc une complexité qui peut ne pas être nécessaire si vous souhaitez simplement remplacer certaines variables.
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
`…`
ou $(…)
) intégrées dans l'entrée permettent l'exécution de commandes arbitraires en raison de l'utilisation de eval
, et l'exécution directe du code shell en raison de l'utilisation de source
. De plus, les guillemets doubles dans l'entrée sont discrètement ignorés et echo
peuvent confondre le début d'une ligne avec l'une de ses options de ligne de commande.
Voici une fonction Bash robuste qui, malgré l'utilisationeval
, devrait être sûre à utiliser.
Tout ${varName}
les références de variable 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$[...]
).
Pour traiter a $
comme un littéral, \
-escape it; par exemple:\${HOME}
Notez que l'entrée n'est acceptée que via stdin .
Exemple:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Code source de la fonction:
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 fonction suppose qu'aucun 0x1
, 0x2
, 0x3
, et les 0x4
caractères de contrôle sont présents dans l'entrée, parce que ces caractères. sont utilisés en interne - puisque la fonction traite du texte , cela devrait être une hypothèse sûre.
eval
il est assez sûr à utiliser.
"
correctement!)
${FOO:-bar}
ou de ne sortir quelque chose que si elle est définie - ${HOME+Home is ${HOME}}
. Je soupçonne qu'avec une petite extension, il pourrait également renvoyer des codes de sortie pour les variables manquantes, ${FOO?Foo is missing}
mais tldp.org/LDP/abs/html/parameter-substitution.html n'a pas actuellement une liste de ceux-ci si cela aide
Créer rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Et template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Rendre le modèle:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, vous l'exécutez sous forme de code.
eval "echo \"$(cat $1)\""
Fonctionne très bien !
voici ma solution avec perl basée sur l'ancienne réponse, remplace les variables d'environnement:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Si vous êtes prêt à 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 pouvez tout aussi bien le mettre dans un script Perl standard à ce stade.
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 = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Si vous nommez le script ci-dessus comme replace-script, il pourrait alors être utilisé comme suit:
replace-script < yourfile | mysql
Voici un moyen pour que le shell fasse la substitution à votre place, comme si le contenu du fichier était à la place tapé entre guillemets.
En utilisant l'exemple de template.txt avec le contenu:
The number is ${i}
The word is ${word}
La ligne suivante amènera le shell à interpoler le contenu de template.txt et à écrire le résultat dans la sortie standard.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Explication:
i
et word
sont transmises en tant que variables d'environnement limitées à l'exécution de sh
.sh
exécute le contenu de la chaîne qui lui est transmise.echo "
' + " $(cat template.txt)
" + ' "
'"
, " $(cat template.txt)
" devient la sortie de cat template.txt
.sh -c
devient:
echo "The number is ${i}\nThe word is ${word}"
,i
et word
sont les variables d'environnement spécifiées.'$(rm -rf ~)'$(rm -rf ~)
les guillemets littéraux dans le fichier de modèle correspondent à ceux que vous avez ajoutés avant son expansion.
'$(echo a)'$(echo a)
. Cela produit 'a'a
. La principale chose qui se passe est que le premier echo a
à l'intérieur de '
est évalué, ce qui n'est peut-être pas ce à quoi vous vous attendez puisqu'il est dans '
, mais c'est le même comportement que l'inclusion '
dans une "
chaîne entre guillemets.
"
chaîne entre guillemets (y compris $(...)
) est le point.
${varname}
, pas d'autres extensions à haut risque de sécurité.
echo "
, suivie d'une chaîne entre guillemets doubles avec les contetns littéraux de template.txt
, suivie d'une autre chaîne littérale "
, le tout concaténé en un seul argument passé à sh -c
. Vous avez raison de dire que le '
ne peut pas être mis en correspondance (car il a été consommé par le shell externe plutôt que passé à l'intérieur), mais le "
peut certainement, donc un modèle contenant Gotcha"; rm -rf ~; echo "
pourrait être exécuté.
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
comme read
commande, sinon vous supprimerez les espaces blancs de début et de fin de chaque ligne d'entrée. De plus, echo
pourrait confondre le début d'une ligne avec l'une de ses options de ligne de commande, il est donc préférable d'utiliser printf '%s\n'
. Enfin, il est plus sûr de double-citer ${1}
.
Je suggérerais d'utiliser quelque chose comme Sigil : https://github.com/gliderlabs/sigil
Il est compilé en un seul binaire, il est donc extrêmement facile à installer sur les systèmes.
Ensuite, vous pouvez faire un simple one-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 oused
cat
et utiliser à la <my-file.conf.template
place afin de donner sigil
un vrai handle de fichier au lieu d'un FIFO.
J'ai trouvé ce fil en me demandant la même chose. Cela m'a inspiré (attention aux backticks)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
is$(< file)
eval echo "\"$(cat FILE)\""
mais cela peut toujours être insuffisant dans la mesure où les guillemets doubles dans l'entrée sont ignorés.
`…`
ou $(…)
) intégrées dans l'entrée permettent l'exécution de commandes arbitraires en raison de l'utilisation de eval
.
Beaucoup de choix ici, mais je pensais que je mettrais 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 renvoie 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 une personne perl, donc il pourrait facilement y avoir un défaut fatal (cela fonctionne pour moi cependant).
Env::import();
ligne - l'importation est implicite par use
. De plus, je suggère de ne pas commencer par créer la totalité de la sortie en mémoire: utilisez simplement print;
au lieu de l' $text .= $_;
intérieur de la boucle et supprimez la print
commande post-boucle .
Cela peut être fait dans bash lui-même si vous avez le contrôle du format du fichier de configuration. Vous avez juste besoin de source (".") Le fichier de configuration plutôt que de le sous-shell. Cela garantit que les variables sont créées dans le contexte du shell actuel (et continuent d'exister) plutôt que du sous-shell (où la variable disparaît lorsque le sous-shell se termine).
$ 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
Si votre fichier de configuration ne peut pas être un script shell, vous pouvez simplement le «compiler» avant de l'exécuter ainsi (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-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
Dans votre cas spécifique, vous pouvez 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 dans MySQL et voilà, j'espère que vous ne détruirez pas votre base de données :-).
go.bash
), vous avez le mauvais bout du bâton - ils ne font pas partie de la solution, ils sont juste un moyen de montrer que les variables sont réglé correctement.
Vous voudrez quelque chose de plus robuste que les suggestions actuelles car bien qu'elles fonctionnent pour votre cas d'utilisation limité (pour l'instant), elles ne suffiront pas pour des situations plus complexes.
Vous avez besoin d'un meilleur moteur de rendu. Vous avez besoin du meilleur moteur de rendu. Vous avez besoin de The Renderest!
Template.txt donné:
Salut toi}}!
Courir:
$ person = Bob ./render template.txt
Et vous verrez la sortie
Bonjour bob!
Écrivez-le dans un fichier en redirigeant stdout vers un fichier:
$ person = Bob ./render template.txt> rendu.txt
Et si vous effectuez le rendu d'un script contenant des variables $ {} que vous ne voulez pas interpoler, The Renderest vous couvre sans que vous ayez à faire autre chose!
Continuez et obtenez votre copie sur https://github.com/relaxdiego/renderest