Ostrzeżenie: korzystając z któregokolwiek z tych rozwiązań, musisz mieć świadomość, że ufasz integralności plików danych, aby były bezpieczne, ponieważ zostaną one wykonane jako kod powłoki w skrypcie. Zabezpieczenie ich jest najważniejsze dla bezpieczeństwa twojego skryptu!
Prosta implementacja wbudowana do szeregowania jednej lub więcej zmiennych
Tak, zarówno w bash, jak i zsh można serializować zawartość zmiennej w sposób łatwy do odczytania za pomocą typeset
wbudowanego -p
argumentu. Format wyjściowy jest taki, że możesz po prostu source
wyjść, aby odzyskać swoje rzeczy.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Możesz odzyskać swoje rzeczy w ten sposób później w skrypcie lub w innym skrypcie:
# Load up the serialized data back into the current shell
source serialized_data.sh
Będzie to działać w przypadku bash, zsh i ksh, w tym przekazywania danych między różnymi powłokami. Bash przetłumaczy to na swoją wbudowaną declare
funkcję, podczas gdy zsh implementuje to za pomocą, typeset
ale ponieważ bash ma alias, aby działał tak czy inaczej, ponieważ używamy typeset
tutaj dla kompatybilności z ksh.
Bardziej złożone uogólnione wdrożenie za pomocą funkcji
Powyższa implementacja jest naprawdę prosta, ale jeśli często ją wywołujesz, możesz chcieć udostępnić sobie funkcję narzędziową, aby to ułatwić. Dodatkowo, jeśli kiedykolwiek spróbujesz zawrzeć powyższe funkcje niestandardowe, napotkasz problemy ze zmiennym zasięgiem. Ta wersja powinna wyeliminować te problemy.
Uwaga dla wszystkich z nich, w celu utrzymania bash / zsh przekrój kompatybilność będziemy mocowania zarówno przypadki typeset
, a declare
więc kod powinien działać w jednej lub obu muszli. Dodaje to trochę bałaganu, który można by wyeliminować, gdybyś robił to tylko dla jednej powłoki.
Głównym problemem związanym z używaniem funkcji do tego (lub włączaniem kodu do innych funkcji) jest to, że typeset
funkcja generuje kod, który po przejściu do skryptu z wnętrza funkcji domyślnie tworzy zmienną lokalną, a nie globalną.
Można to naprawić za pomocą jednego z kilku hacków. Pierwszą próbą rozwiązania tego problemu było przeanalizowanie wyniku procesu serializacji w sed
celu dodania -g
flagi, aby utworzony kod definiował zmienną globalną po ponownym przejściu.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Zauważ, że funky sed
wyrażenie ma pasować tylko do pierwszego wystąpienia „składu” lub „zadeklarować” i dodać -g
jako pierwszy argument. Konieczne jest dopasowanie tylko pierwszego wystąpienia, ponieważ, jak słusznie zauważył Stéphane Chazelas w komentarzach, w przeciwnym razie dopasuje również przypadki, w których szeregowany ciąg zawiera dosłowne znaki nowej linii, a po nim słowo deklaruj lub skład.
Oprócz poprawienia mojego początkowego faux pasowania , Stéphane zasugerował również mniej kruchy sposób zhakowania tego, który nie tylko rozwiązuje problemy z parsowaniem łańcuchów, ale może być przydatnym hakiem, aby dodać dodatkową funkcjonalność za pomocą funkcji otoki w celu przedefiniowania działań pobierane przy ponownym pozyskiwaniu danych. Zakłada się, że nie grasz w żadną inną grę za pomocą poleceń deklarowania lub składu, ale tę technikę łatwiej byłoby wdrożyć w sytuacji, w której włączałeś tę funkcję jako część innej własnej lub nie kontrolowałeś zapisywanych danych i tego, czy -g
dodano flagę. Coś podobnego można również zrobić z aliasami, zobacz implementację Gillesa .
Aby wynik był jeszcze bardziej użyteczny, możemy iterować wiele zmiennych przekazywanych do naszych funkcji, zakładając, że każde słowo w tablicy argumentów jest nazwą zmiennej. Wynik staje się mniej więcej taki:
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
}
W przypadku obu rozwiązań użycie wyglądałoby tak:
# 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"