Aviso: Com qualquer uma dessas soluções, você precisa estar ciente de que está confiando que a integridade dos arquivos de dados seja segura, pois eles serão executados como código shell no script. Protegê-los é fundamental para a segurança do seu script!
Implementação inline simples para serializar uma ou mais variáveis
Sim, tanto no bash quanto no zsh você pode serializar o conteúdo de uma variável de maneira fácil de recuperar usando o argumento typeset
builtin e -p
. O formato de saída é tal que você pode simplesmente source
da saída para recuperar suas coisas.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Você pode recuperar suas coisas assim mais tarde no script ou em outro script:
# Load up the serialized data back into the current shell
source serialized_data.sh
Isso funcionará para bash, zsh e ksh, incluindo a passagem de dados entre diferentes shells. O Bash irá traduzir isso para a sua função declare
enquanto o zsh implementa isso com typeset
mas como o bash tem um alias para isto funcionar de qualquer forma pois usamos typeset
aqui para compatibilidade com o ksh.
Implementação generalizada mais complexa usando funções
A implementação acima é realmente simples, mas se você a chamar com frequência, pode querer dar a si mesmo uma função de utilidade para facilitar. Além disso, se você tentar incluir as funções personalizadas em cima, você terá problemas com o escopo da variável. Esta versão deve eliminar esses problemas.
Nota para tudo isso, para manter a compatibilidade cruzada do bash / zsh, corrigiremos os casos de typeset
e declare
, para que o código funcione em um ou nos dois shells. Isso adiciona algum volume e confusão que poderiam ser eliminados se você estivesse fazendo isso apenas para um shell ou outro.
O principal problema com o uso de funções para isso (ou incluindo o código em outras funções) é que a função typeset
gera código que, quando originado de volta em um script de dentro de uma função, padroniza a criação de uma variável local um global.
Isso pode ser corrigido com um dos vários hacks. Minha tentativa inicial de consertar isso foi analisar a saída do processo de serialização por meio de sed
para adicionar o -g
flag para que o código criado defina uma variável global quando originado de volta.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Observe que a expressão funky sed
deve corresponder apenas à primeira ocorrência de 'typeset' ou 'declare' e adicionar -g
como primeiro argumento. É necessário apenas coincidir com a primeira ocorrência porque, como corretamente apontado em Stéphane Chazelas comentários, caso contrário, ele também corresponderá aos casos em que a sequência serializada contém novas linhas literais seguidas pela palavra declare ou typeset.
Além de corrigir minha análise inicial faux pas , Stéphane também sugeriu uma maneira menos frágil de hackear isso, que não apenas corrige as questões com a análise das cadeias de caracteres, mas pode ser um gancho útil para adicionar funcionalidade adicional usando uma função de wrapper para redefinir as ações Quando você obtém os dados de volta. Isso pressupõe que você não está jogando nenhum outro jogo com os comandos declare ou typeset, mas essa técnica seria mais fácil de implementar em uma situação em que você incluía essa funcionalidade como parte de outra função própria ou você não estava no controle dos dados sendo gravados e se ele tinha ou não o sinalizador -g
adicionado. Algo semelhante também pode ser feito com aliases, consulte a resposta de Gilles para uma implementação.
Para tornar o resultado ainda mais útil, podemos iterar várias variáveis passadas para nossas funções assumindo que cada palavra na matriz de argumentos é um nome de variável. O resultado se torna algo assim:
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
}
Com qualquer solução, o uso seria assim:
# 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"