Comment remplacer les espaces réservés $ {} dans un fichier texte?

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 .

Mettre à jour

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

Ancienne réponse

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):

Fichier template.txt:

 the number is ${i} the word is ${word} 

Fichier 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" 

Sortie:

 #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 :-).