web-dev-qa-db-fra.com

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

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?

129
Dana the Sane

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.

154
user

Mise à jour

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

Ancienne réponse

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.

145
plockc

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

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
42
gnud

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"
20
Dana the Sane

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
12
ChaPuZ

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
9
Thomas

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.

9
mklement0

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
7
neu242

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
5
Beau Simensen

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
4
user976433

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

3
spudfkc

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

2
sfitts

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
2
GlueC

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

1
paxdiablo

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.
  • Les chaînes écrites les unes à côté des autres deviennent une chaîne, cette chaîne est:
    • 'echo "' + "$(cat template.txt)" + '"'
  • Puisque la substitution est entre ", "$(cat template.txt)" devient le résultat de cat template.txt.
  • Ainsi, la commande exécutée par sh -c Devient:
    • echo "The number is ${i}\nThe Word is ${Word}",
    • i et Word sont les variables d'environnement spécifiées.
1
Apriori