Advertencia: con cualquiera de estas soluciones, debe tener en cuenta que confía en la integridad de los archivos de datos para que estén seguros, ya que se ejecutarán como código shell en su secuencia de comandos. ¡Asegurarlos es primordial para la seguridad de su script!
Implementación en línea simple para serializar una o más variables
Sí, tanto en bash como en zsh puede serializar el contenido de una variable de una manera fácil de recuperar utilizando typeset
el -p
argumento y el argumento incorporado . El formato de salida es tal que puede simplemente source
la salida para recuperar sus cosas.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Puede recuperar sus cosas así más adelante en su secuencia de comandos o en otra secuencia de comandos por completo:
# Load up the serialized data back into the current shell
source serialized_data.sh
Esto funcionará para bash, zsh y ksh, incluido el paso de datos entre diferentes shells. Bash traducirá esto a su declare
función incorporada mientras que zsh implementa esto, typeset
pero como bash tiene un alias para que esto funcione de cualquier manera, ya que lo usamos typeset
aquí para la compatibilidad de ksh.
Implementación generalizada más compleja usando funciones
La implementación anterior es realmente simple, pero si la llama con frecuencia, es posible que desee tener una función de utilidad para que sea más fácil. Además, si alguna vez intenta incluir las funciones personalizadas anteriores, se encontrará con problemas con el alcance variable. Esta versión debería eliminar esos problemas.
Tenga en cuenta que para todo esto, para mantener la compatibilidad cruzada de bash / zsh, arreglaremos ambos casos typeset
y, declare
por lo tanto, el código debería funcionar en uno o ambos shells. Esto agrega algo de volumen y desorden que podrían eliminarse si solo estuvieras haciendo esto para un shell u otro.
El principal problema con el uso de funciones para esto (o incluir el código en otras funciones) es que la typeset
función genera código que, cuando se origina en un script desde dentro de una función, por defecto crea una variable local en lugar de una global.
Esto se puede solucionar con uno de varios hacks. Mi intento inicial de solucionar esto fue analizar la salida del proceso de serialización sed
para agregar el -g
indicador para que el código creado defina una variable global cuando se obtiene de nuevo.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Tenga en cuenta que la sed
expresión funky es solo coincidir con la primera aparición de 'typeset' o 'declare' y agregar -g
como primer argumento. Es necesario que solo coincida con la primera aparición porque, como Stéphane Chazelas señaló correctamente en los comentarios, de lo contrario también coincidirá con los casos en que la cadena serializada contenga nuevas líneas literales seguidas de la palabra declarar o componer.
Además de corregir mis análisis sintáctico iniciales paso en falso , Stéphane también sugirió una manera menos frágil que cortar esto que no sólo los pasos laterales de los problemas con el análisis de las cuerdas, pero podría ser un gancho útil añadir funcionalidad adicional mediante el uso de una función de contenedor para redefinir las acciones tomado al obtener los datos nuevamente. Esto supone que no está jugando ningún otro juego con los comandos declarar o componer, pero esta técnica sería más fácil de implementar en una situación en la que estuviera incluyendo esta funcionalidad como parte de otra función propia o usted no tenía el control de los datos que se escribían y si tenía o no -g
agregado el indicador. Algo similar también se podría hacer con alias, vea la respuesta de Gilles para una implementación.
Para que el resultado sea aún más útil, podemos iterar sobre múltiples variables pasadas a nuestras funciones asumiendo que cada palabra en la matriz de argumentos es un nombre de variable. El resultado se convierte en algo como esto:
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
}
Con cualquiera de las soluciones, el uso se vería así:
# 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"