Je veux diriger la sortie d'un fichier "template" vers MySQL, le fichier ayant des variables comme ${dbName}
intercalé. Quel est l'utilitaire de ligne de commande pour remplacer ces instances et vider la sortie dans une sortie standard?
Sed !
Étant donné template.txt:
Le nombre est $ {i} La Parole est $ {Word}
il suffit de dire:
sed -e "s/\${i}/1/" -e "s/\${Word}/dog/" template.txt
Merci à Jonathan Leffler pour le conseil de passer plusieurs -e
_ arguments vers le même sed
invocation.
Voici une solution de yottatsa sur une question similaire ne remplaçant que des variables telles que $ VAR ou $ {VAR}, et constitue un bref résumé à une ligne.
i=32 Word=foo envsubst < template.txt
Bien sûr, si i et Word sont dans votre environnement, alors il est juste
envsubst < template.txt
Sur mon Mac, il semble qu’il ait é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 à écarter les guillemets doubles, c’est le cas de mogsie, mais c’est un one-liner!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Le pouvoir de ces deux solutions réside dans le fait que vous n’obtenez que quelques types d’extensions de shell qui ne se produisent pas normalement $ ((...)), `...` et $ (...), bien que la barre oblique inverse est un caractère d'échappement ici, mais vous n'avez pas à vous inquiéter du fait que l'analyse présente un bogue et que plusieurs lignes sont correctes.
Utilisation /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 si (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
J'y pensais encore, étant donné l'intérêt récent, et je pense que l'outil auquel je pensais à l'origine était m4
, le processeur de macros pour autotools. 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"
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 fichier_parsed.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
Voici une fonction Bash robuste qui, malgré l'utilisation de eval
- devrait être utilisé en toute sécurité.
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é: aucune référence de variable dont le nom n'est n'est pas incluse dans {...}
(Comme $varName
), Ni substitutions de commandes ($(...)
et syntaxe héritée `...`
), Ni substitutions arithmétiques ($((...))
et la syntaxe existante $[...]
).
Pour traiter un $
Comme un littéral, \
- échappe-lui; 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 que pas de caractères de contrôle 0x1
, 0x2
, 0x3
Et 0x4
Ne sont présents 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éer rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Et template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Rendez le template:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
Si vous êtes prêt à utiliser Perl , ce serait ma suggestion. Bien qu'il y ait probablement quelques sed et/ou AWK experts qui savent probablement comment faire cela beaucoup plus facilement. Si vous avez un mappage plus complexe avec plus que simplement dbName pour vos remplacements, vous pouvez facilement l'étendre, mais vous pouvez tout aussi bien l'insérer dans un script Perl standard à ce stade.
Perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Un petit script Perl pour faire quelque chose de légèrement 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 appelez le script ci-dessus en tant que script de remplacement, vous pouvez l'utiliser comme suit:
replace-script < yourfile | mysql
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é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 liner comme suit:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
C’est beaucoup plus sûr que eval
et plus facile que d’utiliser regex ou sed
Beaucoup de choix ici, mais j'ai pensé jeter le mien sur le tas. Il est basé sur Perl. Seules les variables cibles de la forme $ {...} prennent le fichier à traiter en tant qu’argument et sortent 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 de Perl, donc il pourrait facilement y avoir une faille fatale (ça marche pour moi).
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
Cela peut être fait en bash même si vous avez le contrôle du format du fichier de configuration. Il vous suffit de source (".") Le fichier de configuration plutôt que de le sous-traiter. Cela garantit que les variables sont créées dans le contexte du shell actuel (et continuent d'exister) plutôt que dans le sous-shell (où la variable disparaît lorsque le sous-shell se ferme).
$ 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 (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 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étruisez pas votre base de données :-).
Voici un moyen de demander au shell d'effectuer la substitution à votre place, comme si le contenu du fichier était plutôt saisi entre guillemets.
En utilisant l'exemple de template.txt avec le contenu:
The number is ${i}
The Word is ${Word}
La ligne suivante obligera 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 passés en tant que variables d'environnement analysées lors de 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 le résultat 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.