So ersetzen Sie Shell-Variablen in komplexen Textdateien


84

Ich habe mehrere Textdateien, in die ich Shell-Variablen eingeführt habe (z. B. $ VAR1 oder $ VAR2).

Ich möchte diese Dateien (einzeln) nehmen und in neuen Dateien speichern, in denen alle Variablen ersetzt worden wären.

Zu diesem Zweck habe ich das folgende Shell-Skript verwendet (in StackOverflow gefunden):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

Dies funktioniert sehr gut bei sehr einfachen Dateien.

Bei komplexeren Dateien führt der Befehl "eval" jedoch zu viel aus:

  • Zeilen, die mit "#" beginnen, werden übersprungen

  • Das Parsen von XML-Dateien führt zu unzähligen Fehlern

Gibt es einen besseren Weg, dies zu tun? (im Shell-Skript ... Ich weiß, dass dies zum Beispiel mit Ant leicht gemacht werden kann)

Mit freundlichen Grüßen

Antworten:


196

Auf meinem System stellt sich heraus, dass es einen envsubstBefehl gibt, der Teil des gettext-base-Pakets ist.

Das macht es also einfach:

envsubst < "source.txt" > "destination.txt"

Hinweis: Wenn Sie für beide dieselbe Datei verwenden möchten, müssen Sie so etwas wie moreutils verwenden sponge, wie von Johnny Utahh vorgeschlagen : envsubst < "source.txt" | sponge "source.txt". (Da die Shell-Umleitung die Datei ansonsten vor dem Lesen leer macht.)


4
Toll! Es funktioniert jedoch nur für Umgebungsvariablen. Wie kann ich dafür sorgen, dass es mit den Variablen funktioniert, die in meinem .sh-Skript deklariert sind?
Ben

10
@ Ben: Verwenden Sie 'export' (bevor Sie envsubst aufrufen) für jede Variable, die Sie in envsubst verwenden möchten
bis

7
envsubstist Teil von GNU gettext
Andy

3
@user_mda wohin die Ausgabe geht.
Derobert

4
Achtung: Wenn source.txtjede enthält $Zeichen außerhalb der variablen Expansion, envsubstwird diese auch ersetzen und es gibt keine Möglichkeit , das zu entkommen $. Häufige (hässliche) Problemumgehung ist export DOLLAR="$".
Kos

60

In Bezug auf Antwort 2 haben Sie bei der Erörterung von envsubst gefragt:

Wie kann ich dafür sorgen, dass es mit den Variablen funktioniert, die in meinem .sh-Skript deklariert sind?

Die Antwort ist, dass Sie einfach Ihre Variablen exportieren müssen, bevor Sie aufrufen envsubst.

Sie können die Variablenzeichenfolgen, die Sie in der Eingabe ersetzen möchten, auch mithilfe des envsubst SHELL_FORMATArguments einschränken (um zu vermeiden, dass eine Zeichenfolge in der Eingabe unbeabsichtigt durch einen gemeinsamen Shell-Variablenwert ersetzt wird - z $HOME. B. ).

Zum Beispiel:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'

envsubst "$MYVARS" <source.txt >destination.txt

Werden alle Instanzen ersetzen $VAR1und $VAR2(und nur VAR1und VAR2) in source.txtmit 'somevalue'und 'someothervalue'jeweils.


14
Die einfachen Anführungszeichen ', die zum Setzen verwendet werden, MYVARSsind entscheidend
Mark Lakata

Beachten Sie, dass export, envsubstoder ein Interleaving-Befehl fehlschlagen kann, wenn der zu ersetzende Text groß ist. Referenz.
Bischof

@ram, weil das Weglassen des SHELL_FORMATArguments (dh "$MYVARS") dazu führt, dass alle exportierten Variablen ersetzt werden. Wenn Sie das brauchen, dann machen Sie sich keine Sorgen.
Thiago Figueiro

12

Ich weiß, dass dieses Thema alt ist, aber ich habe eine einfachere Arbeitslösung, ohne die Variablen zu exportieren. Kann ein Oneliner sein, aber ich ziehe es vor, mit dem \Online-Ende zu teilen .

var1='myVar1'\
var2=2\
var3=${var1}\
envsubst '$var1,$var3' < "source.txt" > "destination.txt"

#         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
# define which to replace   input            output

Die Variablen müssen in derselben Zeile definiert werden envsubst, um als Umgebungsvariablen betrachtet zu werden.

Das '$var1,$var3'ist optional, um nur die angegebenen zu ersetzen. Stellen Sie sich eine Eingabedatei vor, ${VARIABLE_USED_BY_JENKINS}die nicht ersetzt werden soll.


8
  1. Definieren Sie Ihre ENV-Variable
$ export MY_ENV_VAR=congratulation
  1. Erstellen Sie eine Vorlagendatei ( in.txt ) mit folgendem Inhalt
$MY_ENV_VAR

Sie können auch alle anderen von Ihrem System definierten ENV-Variablen wie (unter Linux) $ TERM, $ SHELL, $ HOME ... verwenden.

  1. Führen Sie diesen Befehl aus, um alle env-Variablen in Ihrer in.txt- Datei zu ersetzen und das Ergebnis in out.txt zu schreiben
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. Überprüfen Sie den Inhalt der Datei out.txt
$ cat out.txt

und Sie sollten "Glückwunsch" sehen.


0

Wenn Sie wirklich nur bash (und sed) verwenden möchten, würde ich jede Ihrer Umgebungsvariablen (wie von setim posix-Modus zurückgegeben) durchgehen und daraus eine Reihe von -e 'regex'for sed erstellen , die mit a abgeschlossen sind -e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g', und dann all das an übergeben sed.

Perl würde einen besseren Job machen, Sie haben Zugriff auf die Umgebungsvariablen als Array und können ausführbare Ersetzungen durchführen, sodass Sie nur einmal mit einer Umgebungsvariablen übereinstimmen.


warum würde jemand eine Antwort wie diese ablehnen? -perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
Yordan Georgiev

0

Eigentlich müssen Sie Ihre ändern read, read -rwodurch Backslashes ignoriert werden.

Außerdem sollten Sie Anführungszeichen und Backslashes vermeiden. Damit

while read -r line; do
  line="${line//\\/\\\\}"
  line="${line//\"/\\\"}"
  line="${line//\`/\\\`}"
  eval echo "\"$line\""
done > destination.txt < source.txt

Trotzdem eine schreckliche Möglichkeit, Expansion zu betreiben.


In der Tat ist dies das Risiko, wenn eine Datei "ausgewertet" wird, die möglicherweise schlechten Code enthält
Ben

0

Exportieren Sie alle benötigten Variablen und verwenden Sie dann einen Perl-Onliner

TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')

Dadurch werden alle in TEXT vorhandenen ENV-Variablen durch tatsächliche Werte ersetzt. Zitate bleiben auch erhalten :)


0

Wenn Sie möchten, dass env-Variablen in Ihren Quelldateien ersetzt werden, während alle nicht env-Variablen unverändert bleiben, können Sie den folgenden Befehl verwenden:

envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt

Die Syntax zum Ersetzen nur bestimmter Variablen wird hier erläutert . Der obige Befehl verwendet eine Unter-Shell, um alle definierten Variablen aufzulisten und sie dann an die zu übergebenenvsubst

Wenn also eine definierte env-Variable aufgerufen $NAMEwird und Ihre source.txtDatei folgendermaßen aussieht:

Hello $NAME
Your balance is 123 ($USD)

Das destination.txtwird sein:

Hello Arik
Your balance is 123 ($USD)

Beachten Sie, dass das $NAMEersetzt wird und das $USDunberührt bleibt


0

Rufen Sie die Perl-Binärdatei im Such- und Ersetzungsmodus pro Zeile (the -pi) auf, indem Sie den Perl-Code (the -e) in einfachen Anführungszeichen ausführen , der über die Schlüssel des speziellen %ENVHashs iteriert , der die exportierten Variablennamen als Schlüssel und die exportierten Variablenwerte als enthält Die Werte der Schlüssel und für jede einfache Iteration ersetzen Sie eine Zeichenfolge, die a enthält, $<<key>>durch ihre <<value>>.

 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file

Vorsichtsmaßnahme: In Fällen, in denen zwei oder mehr Vars mit derselben Zeichenfolge beginnen, ist eine zusätzliche Logikbehandlung erforderlich ...


-1

envsubst scheint genau wie etwas, das ich verwenden wollte, aber -v Option hat mich ein bisschen überrascht.

Während envsubst < template.txtes gut funktionierte, funktionierte das gleiche mit Option -vnicht:

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.1 (Maipo)
$ envsubst -V
envsubst (GNU gettext-runtime) 0.18.2
Copyright (C) 2003-2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Bruno Haible.

Wie ich schrieb, funktionierte das nicht:

$ envsubst -v < template.txt
envsubst: missing arguments
$ cat template.txt | envsubst -v
envsubst: missing arguments

Ich musste das tun, damit es funktioniert:

TEXT=`cat template.txt`; envsubst -v "$TEXT"

Vielleicht hilft es jemandem.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.