wake-up-neo.net

Bash zwingen, Variablen in einer aus einer Datei geladenen Zeichenfolge zu erweitern

Ich versuche herauszufinden, wie man mit bash (force?) Variablen in einer Zeichenfolge erweitert (die aus einer Datei geladen wurde).

Ich habe eine Datei namens "etwas.txt" mit dem Inhalt: 

hello $FOO world

Ich renne dann

export FOO=42
echo $(cat something.txt)

das gibt zurück: 

   hello $FOO world

$ FOO wurde nicht erweitert, obwohl die Variable gesetzt wurde. Ich kann die Datei nicht bewerten oder quellen, da sie versuchen wird, sie auszuführen (sie ist nicht ausführbar, da sie ist - ich möchte nur den String mit den interpolierten Variablen). 

Irgendwelche Ideen? 

59
Michael Neale

Ich stolperte über die meiner Meinung nach DIE Antwort auf diese Frage: den Befehl envsubst.

envsubst < something.txt

Falls es in Ihrer Distribution noch nicht verfügbar ist, ist es in der

GNU-Paket gettext.

@Rockallite - Ich habe ein kleines Wrapper-Skript geschrieben, um das '\ $'-Problem zu lösen.

(BTW, es gibt ein "Feature" von envsubst, erklärt unter https://unix.stackexchange.com/a/294400/7088 , Um nur einige der Variablen in der Eingabe zu erweitern aber ich bin damit einverstanden, dass die Flucht vor den Ausnahmen viel bequemer ist.)

Hier ist mein Skript:

#! /bin/bash
      ## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to  keep variables from
    being expanded.
  With option   -sl   '\$' keeps the back-slash.
  Default is to replace  '\$' with '$'
"

if [[ $1 = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\\\$/\${EnVsUbDolR}/g' |  EnVsUbDolR=$sl\$  envsubst  "[email protected]"
73
LenW

Viele der Antworten, die eval und echo verwenden, wirken sich auf verschiedene Dinge aus, z. B. mehrere Zeilen, beim Versuch, Shell-Metazeichen zu entgehen, Escapezeichen innerhalb der Vorlage, die nicht für die Erweiterung durch Bash vorgesehen sind usw.

Ich hatte das gleiche Problem und schrieb diese Shell-Funktion, die, soweit ich das beurteilen kann, alles richtig handhabt. Dadurch werden aufgrund der Befehlssubstitutionsregeln von bash immer noch nur Zeilenumbrüche aus der Vorlage entfernt, aber ich habe nie herausgefunden, dass dies ein Problem ist, solange alles andere intakt bleibt.

apply_Shell_expansion() {
    declare file="$1"
    declare data=$(< "$file")
    declare delimiter="__apply_Shell_expansion_delimiter__"
    declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
    eval "$command"
}

Zum Beispiel können Sie es wie folgt mit einem parameters.cfg verwenden, der eigentlich ein Shell-Skript ist, das nur Variablen setzt, und einer template.txt, die eine Vorlage ist, die diese Variablen verwendet:

. parameters.cfg
printf "%s\n" "$(apply_Shell_expansion template.txt)" > result.txt

In der Praxis verwende ich dies als eine Art leichtes Vorlagensystem.

25
wjl

du kannst es versuchen 

echo $(eval echo $(cat something.txt))
16
pizza

Sie möchten nicht jede Zeile drucken, sondern auswerten, damit Bash Variablenersetzungen durchführen kann.

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

Weitere Informationen finden Sie unter help eval oder im Bash-Handbuch.

10
Todd A. Jacobs

Ein anderer Ansatz (was icky scheint, aber ich stelle es trotzdem hier): 

Schreibe den Inhalt von something.txt in eine temporäre Datei, mit einer Echo-Anweisung: 

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

dann wieder in eine Variable einbinden: 

RESULT=$(source temp.out)

und $ RESULT wird alles erweitern. Aber es scheint so falsch zu sein!

4
Michael Neale

Wenn Sie nur die Variablenreferenzen erweitern möchten (ein Ziel, das ich für mich selbst hatte), können Sie Folgendes tun.

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

(Die mit Escapezeichen versehenen Anführungszeichen um $ Inhalt sind hier der Schlüssel)

2
SS781
  1. bash-Methode (einzeilige Variante von Antwort von Michael Neale ), mit Prozess & Befehlssubstitution:

    FOO=42 . <(echo -e echo $(<something.txt))
    

    Ausgabe:

    hello 42 world
    

    Beachten Sie, dass export nicht benötigt wird.

  2. GNUsede _ ​​bewertung Methode, wenn etwas.txt viele Zeilen hat:

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    
2
agc
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-Apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
1
ClearCrescendo
foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)
1
Božský Boch

envsubst ist eine großartige Lösung (siehe Antwort von LenW), wenn der Inhalt, den Sie ersetzen, eine "angemessene" Länge hat. 

In meinem Fall musste ich den Inhalt einer Datei ersetzen, um den Variablennamen zu ersetzen. envsubst erfordert, dass der Inhalt als Umgebungsvariablen exportiert wird. Beim Exportieren von Umgebungsvariablen, die mehr als ein Megabyte umfassen, tritt beim Bash ein Problem auf.

awk Lösung

Cuonglms Lösung aus einer anderen Frage verwenden:

needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle 
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out

Diese Lösung funktioniert auch für große Dateien.

0
Larry K

Folgende Lösung: 

  • ermöglicht das Ersetzen von definierten Variablen 

  • lässt unveränderte Platzhalter für Variablen, die nicht definiert sind. Dies ist besonders bei automatisierten Bereitstellungen hilfreich. 

  • unterstützt das Ersetzen von Variablen in folgenden Formaten:

    ${var_NAME}

    $var_NAME

  • meldet, welche Variablen in der Umgebung nicht definiert sind, und gibt für solche Fälle einen Fehlercode zurück



    TARGET_FILE=someFile.txt;
    ERR_CNT=0;

    for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do     
      VAR_VALUE=${!VARNAME};
      VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
      VAR_VALUE2=${!VARNAME2};

      if [ "xxx" = "xxx$VAR_VALUE2" ]; then
         echo "$VARNAME is undefined ";
         ERR_CNT=$((ERR_CNT+1));
      else
         echo "replacing $VARNAME with $VAR_VALUE2" ;
         sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; 
      fi      
    done

    if [ ${ERR_CNT} -gt 0 ]; then
        echo "Found $ERR_CNT undefined environment variables";
        exit 1 
    fi
0
aprodan