Warning: With any of these solutions, you need to be aware that you are trusting the integrity of the data files to be safe as they will get executed as shell code in your script. Securing them is paramount to your script's security!
Simple inline implementation for serializing one or more variables
Yes, in both bash and zsh you can serialize the contents of a variable in a way that is easy to retrieve using the declaretypeset builtin and the -p argument. The output format is such that you can simply source the output to get your stuff back.
# You have $VARvariable(s) $FOO and $BAR already with your stuff
declaretypeset -p VARFOO BAR > serialized_VAR./serialized_data.sh
EitherYou can get your stuff back like this either later in your script or in another script altogether, you can get your stuff back like this:
# Load up the serialized data back into $VARthe current shell
source serialized_VARserialized_data.sh
How portable this is beyondThis will work for bash, zsh and ksh including passing data between different shells. Bash will translate this to its builtin declare function while zsh I don't knowimplements this with typeset but as bash has an alias for this to work either way for we use typeset here for ksh compatibility. It works in sh
More complex generalized implementation using functions
The above implementation is really simple, but if you call it frequently you might want to give yourself a utility function to make it easier. Additionally if you ever try to include the above inside custom functions you will run into issues with variable scoping. This version should eliminate those issues.
Note for meall of these, in testing but I don't know whatorder to maintain bash/zsh cross-compatibility we will be fixing both the POSIX standard is oncases of typeset and declare so the code should work in either or both shells. This adds some bulk and mess that could be eliminated if you were only doing this for one shell or another.
You could generalizeThe main problem with using functions for this (or including the code in other functions) is that the typeset function generates code that, when sourced back into a little bit and make pairscript from inside a function, defaults to creating a local variable rather than a global one.
This can be fixed with one of functionsseveral hacks. My initial attempt to save stuff to a folderfix this was parse the output of serialized data and read itthe serialize process through sed to add the -g flag so the created code defines a global variable when sourced back on demandin. Obviously you would need to be aware that the stuff in the serialized directory will get executed as shell code so securing it is paramount to your script's security!
# Did you see the warning? Don't do this if you can't trust the files
# ./serialized_* as they will be executed as part of your script!
serialize() {
declaretypeset -p "$1" | sed -E 's'0,/^(typeset|declare)/&{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Note that the dirty hack with sedfunky sed expression is to addonly match the first occurrence of either 'typeset' or 'declare' and add -g flag to the declare statement. Without this, declare would createas a local variable when run again from inside the other functionfirst argument. Matching 'typeset' as well as 'declare'It is for portability between bash and zsh; you could clean that up if you werenecessary to only running this with one ormatch the other.
first occurrence because, as Stéphane Chazelas suggestedrightly pointed out in comments, otherwise it will also match cases where the serialized string contains literal newlines followed by the word declare or typeset.
In addition to correcting my initial parsing faux pas, Stéphane also suggested a cleanerless brittle way to hack this in comments that avoidsnot only side steps the issues with parsing the strings but could be a potential problem case ifuseful hook to add additional functionality by using a wrapper function to redefine the variableactions taken when sourcing the data you are serializing contains literal newlinesback in. This assumes you are not playing any other games with the declare or typeset commands. Implementing, but this technique would lookbe easier to implement in a situation where you were including this functionality as part of another function of your own or you were not in control of the data being written and whether or not it had the -g flag added. Something similar could also be done with aliases, see Gilles's answer for an implementation.
To make the result even more useful, we can iterate over multiple variables passed to our functions by assuming that each word in the argument array is a variable name. The result becomes something like this:
serialize() {
declarefor var in $@; do
typeset -p "$1""$var" > "./serialized_$1serialized_$var.shsh"
done
}
deserialize() {
declare() { builtin declare -g "$@"; }
typeset() { builtin typeset -g "$@"; }
for var in $@; do
source "./serialized_$1serialized_$var.sh"
done
unset -f declare typeset
}
# Load some test data into variables
FOO=(an array or something)
BAR=$(uptime)
serialize# FOO
serializeSave BAR
unsetit FOOout BAR
#to <snip>our later....
serialized data files
deserializeserialize FOO
deserialize BAR
echo# "FOO:For $FOO\nBAR:testing $BAR"
If keeping the values in a single file is a must, you could do something like so:
touchpurposes ./serializedvars.sh
serialize()unset {
the variables to we sedknow -iEif "/^($1=|(typeset|declare)[^=]*$1)/d"it ./serializedvars.shworked
unset FOO BAR
# declareLoad -p "$1"the |data sedback -Ein 's/^(typeset|declare)/&from -g/'out >>data ./serializedvars.sh
}
files
deserialize() {
sourceFOO <(sedBAR
echo -nE"FOO: "/^($1=|(typeset|declare)[^=]*$1)/p"$FOO\nBAR: ./serializedvars.sh)
}$BAR"
Warning: That implementation will break rather badly for variables that contain literal newlines. In order to get around that problem, the easiest thing to do is to use per-variable files for the serialized data per the example above. The other way would be to always append the data without bothering to delete any previous instances of a variable name from the data file. Only the last instance will "stick" anyway, but this could easily get to be unwieldy as your data file grows.
Again, some of the complexity of the expressions here are to accommodate both bash and zsh output formats from declare -p. One or the other would be notably simpler.