Wie ersetzt ich $ {} Platzhalter in einer Textdatei?
-
03-07-2019 - |
Frage
Ich möchte die Ausgabe einer "Vorlage" -Datei in MySQL übergeben, die Datei mit Variablen wie ${dbName}
durchsetzt. Was ist das Dienstprogramm für Befehlszeilen, um diese Instanzen zu ersetzen und die Ausgabe in die Standardausgabe abzugeben?
Lösung
Sed!
Gegebene Vorlage.txt:
The number is ${i} The word is ${word}
Wir müssen nur sagen:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Vielen Dank an Jonathan Leffler für den Tipp, um mehrere zu bestehen -e
Argumente an das gleiche sed
Aufruf.
Andere Tipps
Aktualisieren
Hier ist eine Lösung von Yottatsa Auf eine ähnliche Frage, die nur Ersatz für Variablen wie $ var oder $ {var} ersetzt und ein kurzer Ein-Liner ist
i=32 word=foo envsubst < template.txt
Natürlich, wenn ich und Wort sind in Ihrer Umgebung, dann ist es gerade
envsubst < template.txt
Auf meinem Mac sieht es so aus, als wäre es als Teil von installiert worden GetText und von MACGPG2
Alte Antwort
Hier ist eine Verbesserung der Lösung von Mogsie Bei einer ähnlichen Frage müssen Sie nicht die Doppelzitate von Mogsie begleiten, aber sein ist ein Ein -Liner!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Die Macht dieser beiden Lösungen besteht ist Ein Fluchtcharakter hier, aber Sie müssen sich keine Sorgen machen, dass das Parsen einen Fehler hat und es mehrere Zeilen gut macht.
Verwenden /bin/sh
. Erstellen Sie ein kleines Shell -Skript, das die Variablen festlegt, und analysieren Sie die Vorlage mit der Shell selbst. Lust auf (bearbeiten, um Neulinien korrekt zu handhaben):
Dateivorlage.txt:
the number is ${i}
the word is ${word}
Dateiskript.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"
Ausgabe:
#sh script.sh
the number is 1
the word is dog
Ich habe angesichts des jüngsten Interesses noch einmal darüber nachgedacht und ich denke, dass das Werkzeug, an das ich ursprünglich gedacht habe, war m4
, der Makroprozessor für Autotools. Anstelle der Variablen, die ich ursprünglich angegeben habe, würden Sie verwenden:
$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 Vorlage.txt parsed_file.txt
parsed_file.txt
Variable 1 value: value 1
Variable 2 value: value 2
Hier ist meine Lösung mit Perl basierend auf früherer Antwort ersetzt Umgebungsvariablen:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Hier ist ein robuste Bash -Funktion das - trotzdem verwendet eval
- sollte sicher zu bedienen sein.
Alle ${varName}
Variable Referenzen im Eingabetext werden basierend auf den Variablen der aufrufenden Shell erweitert.
Nichts anderes wird erweitert: Weder variable Referenzen, deren Namen sind nicht Eingeschlossen in {...}
(wie zum Beispiel $varName
), noch Befehlssubstitutionen ($(...)
und Legacy -Syntax `...`
), noch arithmetische Substitutionen ($((...))
und Legacy -Syntax $[...]
).
A $
als wörtlich, \
-Schape es; z.B:\${HOME}
Beachten Sie, dass die Eingabe nur durch akzeptiert wird Stdin.
Beispiel:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Funktionsquellcode:
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
}
Die Funktion geht davon aus 0x1
, 0x2
, 0x3
, und 0x4
Steuerzeichen sind in der Eingabe vorhanden, da diese Zeichen. werden intern verwendet - seit den Funktionsprozessen Text, das sollte eine sichere Annahme sein.
Wenn Sie offen für die Verwendung sind Perl, das wäre mein Vorschlag. Obwohl es wahrscheinlich einige gibt sed und/oder Awk Experten, die wahrscheinlich wissen, wie man das viel einfacher macht. Wenn Sie eine komplexere Zuordnung mit mehr als nur DBNAME für Ihren Ersatz haben, können Sie dies ziemlich leicht erweitern, aber Sie könnten es zu diesem Zeitpunkt genauso gut in ein Standard -Perl -Skript einfügen.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Ein kurzes Perl -Skript, um etwas etwas komplizierteres zu tun (mehrere Schlüssel zu handeln):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Wenn Sie das obige Skript als Ersatzkript nennen, kann es wie folgt verwendet werden:
replace-script < yourfile | mysql
Schaffen rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Und template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Die Vorlage rendern:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
Datei.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
Ich würde vorschlagen, so etwas zu verwenden Sigil: https://github.com/gliderlabs/sigil
Es ist mit einer einzigen Binärdatei zusammengestellt, sodass es extrem einfach auf Systemen installiert ist.
Dann können Sie einen einfachen Einzeiler wie folgt machen:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Das ist viel sicherer als eval
und einfacher als Regex oder sed
Ich habe diesen Thread gefunden, während ich mich dasselbe gefragt habe. Es hat mich dazu inspiriert (vorsichtig mit den Backticks)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
Viele Auswahlmöglichkeiten hier, aber ich dachte, ich würde meinen auf den Haufen werfen. Es basiert Perl, zielt nur an Variablen des Formulars $ {...}, nimmt die Datei als Argument zur Verarbeitung und gibt die konvertierte Datei auf STDOut aus:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Natürlich bin ich nicht wirklich eine Perl -Person, also könnte es leicht einen fatalen Fehler geben (aber für mich funktioniert).
Dies kann in Bash selbst erfolgen, wenn Sie die Kontrolle über das Konfigurationsdateiformat haben. Sie müssen nur die Konfigurationsdatei beziehen ("."), Anstatt sie zu unterschalen. Dies stellt sicher, dass die Variablen im Kontext der aktuellen Schale erstellt werden (und existieren weiterhin) und nicht im Unterschalen (wo die Variable verschwindet, wenn die Unterschale beendet ist).
$ 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
Wenn Ihre Konfigurationsdatei kein Shell -Skript sein kann, können Sie sie einfach vor der Ausführung "kompilieren" (die Kompilierung hängt von Ihrem Eingabeformat ab).
$ 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
In Ihrem speziellen Fall könnten Sie so etwas verwenden wie:
$ 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
Dann machen Sie die Ausgabe von Go.Bash in MySQL und Voila, hoffentlich werden Sie Ihre Datenbank nicht zerstören :-).
Hier ist eine Möglichkeit, die Shell dazu zu bringen, den Substitution für Sie durchzuführen, als ob der Inhalt der Datei stattdessen zwischen Doppelzitaten tippt würde.
Verwenden des Beispiels von template.txt mit Inhalten:
The number is ${i}
The word is ${word}
Die folgende Zeile veranlasst die Shell, den Inhalt von Template zu interpolieren.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Erläuterung:
i
undword
werden als Umgebungsvariablen übergeben,sh
.sh
führt den Inhalt der Zeichenfolge aus, die sie übergeben wird.- Saiten, die nebeneinander geschrieben wurden, werden zu einer Zeichenfolge, diese Zeichenfolge lautet:
- '
echo "
' + "$(cat template.txt)
" + '"
'
- '
- Da ist die Substitution dazwischen
"
, "$(cat template.txt)
"wird zur Ausgabe voncat template.txt
. - Also der Befehl von ausgeführt von
sh -c
wird:echo "The number is ${i}\nThe word is ${word}"
,- wo
i
undword
sind die angegebenen Umgebungsvariablen.