Avvertenza: con una di queste soluzioni, è necessario essere consapevoli della fiducia nell'integrità dei file di dati, poiché verranno eseguiti come codice shell nello script. Proteggerli è fondamentale per la sicurezza del tuo script!
Implementazione in linea semplice per serializzare una o più variabili
Sì, sia in bash che in zsh è possibile serializzare i contenuti di una variabile in un modo che è facile da recuperare usando il typeset
builtin e l' -p
argomento. Il formato di output è tale che puoi semplicemente source
ottenere l'output per recuperare le tue cose.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Puoi recuperare le tue cose in questo modo o più tardi nella tua sceneggiatura o in un'altra sceneggiatura del tutto:
# Load up the serialized data back into the current shell
source serialized_data.sh
Questo funzionerà per bash, zsh e ksh incluso il passaggio di dati tra diverse shell. Bash tradurrà questo nella sua declare
funzione incorporata mentre zsh lo implementa con typeset
ma poiché bash ha un alias per farlo funzionare in entrambi i modi perché usiamo typeset
qui per la compatibilità di ksh.
Implementazione generalizzata più complessa tramite funzioni
L'implementazione di cui sopra è davvero semplice, ma se la chiami frequentemente potresti voler darti una funzione di utilità per renderlo più facile. Inoltre, se si tenta di includere le funzioni personalizzate sopra descritte, si verificheranno problemi con l'ambito variabile. Questa versione dovrebbe eliminare tali problemi.
Nota per tutti questi, al fine di mantenere la compatibilità incrociata bash / zsh sistemeremo entrambi i casi typeset
e declare
quindi il codice dovrebbe funzionare in una o entrambe le shell. Questo aggiunge un po 'di massa e confusione che potrebbero essere eliminati se lo facessi solo per una shell o un'altra.
Il problema principale nell'uso di funzioni per questo (o nell'inclusione del codice in altre funzioni) è che la typeset
funzione genera codice che, quando viene ricondotto in uno script dall'interno di una funzione, viene automaticamente impostato sulla creazione di una variabile locale anziché globale.
Questo può essere risolto con uno dei numerosi hack. Il mio tentativo iniziale di risolvere questo problema è stato analizzare l'output del processo di serializzazione sed
per aggiungere il -g
flag, in modo che il codice creato definisca una variabile globale al momento del ritorno.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Nota che l' sed
espressione funky deve corrispondere solo alla prima occorrenza di 'typeset' o 'declare' e aggiungerla -g
come primo argomento. È necessario corrispondere solo alla prima occorrenza perché, come ha giustamente sottolineato Stéphane Chazelas nei commenti, corrisponderà anche ai casi in cui la stringa serializzata contiene nuove righe letterali seguite dalla parola dichiarare o comporre.
Oltre a correggere il mio falso passo di analisi iniziale , Stéphane ha anche suggerito un modo meno fragile per hackerare ciò che non solo affronta i problemi con l'analisi delle stringhe ma potrebbe essere un gancio utile per aggiungere funzionalità aggiuntive utilizzando una funzione wrapper per ridefinire le azioni acquisito durante il reperimento dei dati. Ciò presuppone che non si stia giocando ad altri giochi con i comandi declare o comporre, ma questa tecnica sarebbe più semplice da implementare in una situazione in cui si includeva questa funzionalità come parte di un'altra funzione propria o non avevi il controllo dei dati scritti e se era stato -g
aggiunto o meno il flag. Qualcosa di simile potrebbe essere fatto anche con gli alias, vedere la risposta di Gilles per un'implementazione.
Per rendere il risultato ancora più utile, possiamo iterare su più variabili passate alle nostre funzioni assumendo che ogni parola nell'array degli argomenti sia un nome di variabile. Il risultato diventa qualcosa del genere:
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 entrambe le soluzioni, l'utilizzo sarebbe simile al seguente:
# 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"