Warnung: Bei jeder dieser Lösungen müssen Sie sich bewusst sein, dass Sie der Integrität der Datendateien vertrauen, um sicher zu sein, da diese in Ihrem Skript als Shell-Code ausgeführt werden. Ihre Sicherheit ist von größter Bedeutung für die Sicherheit Ihres Skripts!
Einfache Inline-Implementierung zum Serialisieren einer oder mehrerer Variablen
Ja, sowohl in bash als auch in zsh können Sie den Inhalt einer Variablen auf eine Weise serialisieren, die mit dem typeset
Builtin und dem -p
Argument leicht abgerufen werden kann . Das Ausgabeformat ist so, dass Sie einfach source
die Ausgabe durchführen können, um Ihre Daten wieder zu erhalten.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Du kannst deine Sachen entweder später in deinem Skript oder in einem anderen Skript zurückbekommen:
# Load up the serialized data back into the current shell
source serialized_data.sh
Dies funktioniert für bash, zsh und ksh, einschließlich der Übergabe von Daten zwischen verschiedenen Shells. Bash übersetzt dies in seine eingebaute declare
Funktion, während zsh dies implementiert, typeset
aber da bash einen Alias hat, funktioniert dies so oder so, wie wir es typeset
hier aus Gründen der ksh-Kompatibilität verwenden.
Komplexere verallgemeinerte Implementierung mit Funktionen
Die obige Implementierung ist sehr einfach. Wenn Sie sie jedoch häufig aufrufen, möchten Sie sich möglicherweise eine Utility-Funktion geben, um sie zu vereinfachen. Wenn Sie jemals versuchen, die oben genannten Funktionen in benutzerdefinierte Funktionen einzubeziehen, treten außerdem Probleme mit dem Variablenumfang auf. Diese Version sollte diese Probleme beseitigen.
Beachten Sie, dass zur Aufrechterhaltung der Bash / Zsh-Kreuzkompatibilität beide Fälle behoben werden typeset
und declare
der Code daher in einer oder beiden Shells funktionieren sollte. Dies fügt etwas Masse und Unordnung hinzu, die beseitigt werden könnten, wenn Sie dies nur für die eine oder andere Shell tun würden.
Das Hauptproblem bei der Verwendung von Funktionen hierfür (oder beim Einbeziehen des Codes in andere Funktionen) besteht darin, dass die typeset
Funktion Code generiert, der standardmäßig eine lokale Variable und keine globale Variable erstellt, wenn er aus einer Funktion in ein Skript zurückgeleitet wird.
Dies kann mit einem von mehreren Hacks behoben werden. Mein anfänglicher Versuch, dies zu beheben, bestand darin, die Ausgabe des Serialisierungsprozesses zu analysieren sed
, um das -g
Flag hinzuzufügen, sodass der erstellte Code eine globale Variable definiert, wenn er zurückgegeben wird.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Beachten Sie, dass der flippige sed
Ausdruck nur mit dem ersten Vorkommen von 'typeset' oder 'declare' übereinstimmen und -g
als erstes Argument hinzufügen soll . Es ist nur erforderlich, das erste Vorkommen abzugleichen, da es , wie Stéphane Chazelas in Kommentaren zutreffend ausgeführt hat, auch Fälle abdeckt, in denen der serialisierte String wörtliche Zeilenumbrüche enthält, denen das Wort declare oder typeset folgt.
Neben meinem ersten Parsing zu korrigieren Fauxpas , Stéphane auch vorgeschlagen , einen weniger spröden Weg , dies zu hacken , dass nicht nur Seite Schritte , um die Probleme mit den Saiten Parsen aber könnte eine nützliche Haken sein , zusätzliche Funktionalität hinzufügen , indem Sie eine Wrapper - Funktion mit den Aktionen neu zu definieren Dies setzt voraus, dass Sie keine anderen Spiele mit den Befehlen declare oder typeset spielen. Diese Technik wäre jedoch einfacher zu implementieren, wenn Sie diese Funktionalität als Teil einer anderen eigenen Funktion oder als Teil einer anderen Funktion einbinden würden Sie hatten keine Kontrolle darüber, welche Daten geschrieben wurden und ob das -g
Flag hinzugefügt wurde oder nicht . Ähnliches könnte auch mit Aliasen geschehen, siehe Gilles 'Antwort für eine Implementierung.
Um das Ergebnis noch nützlicher zu machen, können wir mehrere Variablen durchlaufen, die an unsere Funktionen übergeben wurden, indem wir davon ausgehen, dass jedes Wort im Argumentarray ein Variablenname ist. Das Ergebnis sieht ungefähr so aus:
serialize() {
for var in $@; do
typeset -p "$var" > "./serialized_$var.sh"
done
}
deserialize() {
declare() { builtin declare -g "$@"; }
typeset() { builtin typeset -g "$@"; }
for var in $@; do
source "./serialized_$var.sh"
done
unset -f declare typeset
}
In beiden Fällen sieht die Verwendung folgendermaßen aus:
# Load some test data into variables
FOO=(an array or something)
BAR=$(uptime)
# Save it out to our serialized data files
serialize FOO BAR
# For testing purposes unset the variables to we know if it worked
unset FOO BAR
# Load the data back in from out data files
deserialize FOO BAR
echo "FOO: $FOO\nBAR: $BAR"