Avertissement: avec l'une de ces solutions, vous devez être conscient que vous faites confiance à l'intégrité des fichiers de données pour être sûrs car ils seront exécutés en tant que code shell dans votre script. Les sécuriser est primordial pour la sécurité de votre script!
Implémentation en ligne simple pour sérialiser une ou plusieurs variables
Oui, à la fois dans bash et zsh, vous pouvez sérialiser le contenu d'une variable d'une manière qui est facile à récupérer à l'aide de la fonction typeset
intégrée et de l' -p
argument. Le format de sortie est tel que vous pouvez simplement source
la sortie pour récupérer vos trucs.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Vous pouvez récupérer vos informations comme ceci plus tard dans votre script ou dans un autre script:
# Load up the serialized data back into the current shell
source serialized_data.sh
Cela fonctionnera pour bash, zsh et ksh, y compris le passage de données entre différents shells. Bash traduira cela dans sa declare
fonction intégrée tandis que zsh l'implémentera avec typeset
mais comme bash a un alias pour que cela fonctionne dans les deux cas car nous utilisons typeset
ici pour la compatibilité avec ksh.
Implémentation généralisée plus complexe à l'aide de fonctions
L'implémentation ci-dessus est vraiment simple, mais si vous l'appelez fréquemment, vous voudrez peut-être vous donner une fonction utilitaire pour le rendre plus facile. De plus, si jamais vous essayez d'inclure les fonctions personnalisées ci-dessus, vous rencontrerez des problèmes avec la portée des variables. Cette version devrait éliminer ces problèmes.
Remarque pour tous ces éléments, afin de maintenir la compatibilité croisée bash / zsh, nous allons corriger les deux cas de typeset
et declare
donc le code devrait fonctionner dans l'un ou les deux shells. Cela ajoute du volume et des dégâts qui pourraient être éliminés si vous ne faisiez cela que pour un shell ou un autre.
Le principal problème lié à l'utilisation de fonctions à cet effet (ou à l'inclusion du code dans d'autres fonctions) est que la typeset
fonction génère du code qui, lorsqu'il est renvoyé dans un script depuis l'intérieur d'une fonction, par défaut, crée une variable locale plutôt que globale.
Cela peut être résolu avec l'un des nombreux hacks. Ma tentative initiale de résoudre ce problème a été d'analyser la sortie du processus de sérialisation sed
pour ajouter l' -g
indicateur afin que le code créé définisse une variable globale lorsqu'il est récupéré.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Notez que l' sed
expression funky doit correspondre uniquement à la première occurrence de 'typeset' ou 'declare' et l'ajouter -g
comme premier argument. Il est nécessaire de ne faire correspondre que la première occurrence parce que, comme Stéphane Chazelas l'a souligné à juste titre dans les commentaires, sinon, il correspondra également aux cas où la chaîne sérialisée contient des retours à la ligne littéraux suivis du mot declare ou typeset.
En plus de corriger mon faux pas d' analyse initial , Stéphane a également suggéré une façon moins cassante de pirater cela qui non seulement contourne les problèmes avec l'analyse des chaînes, mais pourrait être un crochet utile pour ajouter des fonctionnalités supplémentaires en utilisant une fonction wrapper pour redéfinir les actions lors de la récupération des données. Cela suppose que vous ne jouez à aucun autre jeu avec les commandes declare ou typeset, mais cette technique serait plus facile à mettre en œuvre dans une situation où vous incluiez cette fonctionnalité dans le cadre d'une autre fonction de votre choix ou vous ne contrôliez pas les données en cours d'écriture et si le -g
drapeau avait été ajouté ou non . Quelque chose de similaire pourrait également être fait avec les alias, voir la réponse de Gilles pour une implémentation.
Pour rendre le résultat encore plus utile, nous pouvons itérer sur plusieurs variables passées à nos fonctions en supposant que chaque mot du tableau d'arguments est un nom de variable. Le résultat devient quelque chose comme ceci:
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
}
Avec l'une ou l'autre solution, l'utilisation ressemblerait à ceci:
# 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"