Aviso: Com qualquer uma dessas soluções, você precisa estar ciente de que está confiando na integridade dos arquivos de dados, pois eles serão executados como código de shell em seu script. Protegê-los é fundamental para a segurança do seu script!
Implementação simples em linha para serializar uma ou mais variáveis
Sim, no bash e no zsh, você pode serializar o conteúdo de uma variável de uma maneira fácil de recuperar usando o typeset
builtin e o -p
argumento. O formato de saída é tal que você pode simplesmente source
a 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 em seu script ou em outro script completamente:
# Load up the serialized data back into the current shell
source serialized_data.sh
Isso funcionará para o bash, zsh e ksh, incluindo a passagem de dados entre diferentes shells. O Bash traduzirá isso para sua declare
função embutida, enquanto o zsh implementa isso com typeset
mas o bash tem um alias para que isso funcione de qualquer maneira, 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ê chamá-la com frequência, pode querer oferecer uma função de utilitário para facilitar. Além disso, se você tentar incluir as funções personalizadas internas acima, encontrará problemas com o escopo da variável. Esta versão deve eliminar esses problemas.
Observe tudo isso: para manter a compatibilidade cruzada do bash / zsh, corrigiremos os dois casos typeset
e, declare
portanto, o código deve funcionar em um ou ambos os 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 typeset
função gera código que, quando originado em um script de dentro de uma função, o padrão é criar uma variável local em vez de global.
Isso pode ser corrigido com um dos vários hacks. Minha tentativa inicial de corrigir isso foi analisar a saída do processo de serialização sed
para adicionar o -g
sinalizador, para que o código criado defina uma variável global quando retornado.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Observe que a sed
expressão funky deve corresponder apenas à primeira ocorrência de 'typeset' ou 'declare' e adicionar -g
como primeiro argumento. É necessário corresponder apenas à primeira ocorrência, porque, como Stéphane Chazelas apontou corretamente nos comentários, também corresponderá aos casos em que a seqüência de caracteres serializada contenha novas linhas literais seguidas pela palavra declarar ou digitar.
Além de corrigir meu falso passo de análise inicial , Stéphane também sugeriu uma maneira menos frágil de hackear isso, que além de solucionar os problemas de análise de seqüências de caracteres, poderia ser um gancho útil para adicionar funcionalidade adicional usando uma função de invólucro para redefinir as ações tomada ao fornecer os dados de volta. Isso pressupõe que você não esteja 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ê estivesse incluindo essa funcionalidade como parte de outra função própria ou você não estava no controle dos dados que estavam sendo gravados e se o -g
sinalizador foi adicionado ou não . 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 seja um nome de variável. O resultado se torna algo como isto:
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 ficaria 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"