Предупреждение. С любым из этих решений вы должны знать, что вы доверяете целостности файлов данных, поскольку они будут выполняться как код оболочки в вашем скрипте. Обеспечение их имеет первостепенное значение для безопасности вашего сценария!
Простая встроенная реализация для сериализации одной или нескольких переменных
Да, и в bash, и в zsh вы можете сериализовать содержимое переменной таким способом, который легко получить с помощью typeset
встроенного -p
аргумента и аргумента. Выходной формат таков, что вы можете просто source
вывести свои данные обратно.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Вы можете получить свои вещи обратно, как это позже или в вашем сценарии или в другом сценарии в целом:
# Load up the serialized data back into the current shell
source serialized_data.sh
Это будет работать для bash, zsh и ksh, включая передачу данных между различными оболочками. Bash преобразует это в свою встроенную declare
функцию, в то время как zsh реализует это с помощью, typeset
но поскольку bash имеет псевдоним, чтобы это работало в любом случае, поскольку мы используем его typeset
здесь для совместимости с ksh.
Более сложная обобщенная реализация с использованием функций
Вышеприведенная реализация действительно проста, но если вы будете часто ее вызывать, вы можете захотеть воспользоваться утилитой для ее упрощения. Кроме того, если вы когда-нибудь попробуете включить вышеупомянутое в пользовательские функции, у вас возникнут проблемы с областью видимости переменной. Эта версия должна устранить эти проблемы.
Обратите внимание на все это, чтобы поддерживать перекрестную совместимость bash / zsh, мы будем исправлять оба случая, typeset
и declare
поэтому код должен работать в одной или обеих оболочках. Это добавляет некоторую массу и беспорядок, которые можно было бы устранить, если бы вы делали это только для одной оболочки или другой.
Основная проблема с использованием функций для этого (или включения кода в другие функции) состоит в том, что typeset
функция генерирует код, который при получении обратно в сценарий из функции по умолчанию создает локальную переменную, а не глобальную.
Это можно исправить одним из нескольких хаков. Моя первоначальная попытка исправить это заключалась в том, чтобы проанализировать выходные данные процесса сериализации, sed
чтобы добавить -g
флаг, чтобы созданный код определял глобальную переменную при получении обратно.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Обратите внимание, что sed
выражение funky должно соответствовать только первому вхождению 'typeset' или 'Declare' и добавляться -g
в качестве первого аргумента. Необходимо соответствовать только первому вхождению, потому что, как справедливо указал Стефан Шазелас в комментариях, в противном случае он также будет соответствовать случаям, когда в сериализованной строке содержатся буквенные символы новой строки, за которыми следует слово Declare или typeset.
В дополнении к исправлению моей первоначальному разбора бестактности , Stéphane также предложил менее хрупкий способ взломать это , что не только подножку вопросов с разбором строк , но может быть полезным крючком , чтобы добавить дополнительную функциональность, используя функцию - обертки , чтобы пересмотреть действия при получении данных обратно. Предполагается, что вы не играете ни в какие другие игры с командами объявления или набора текста, но этот метод будет проще реализовать в ситуации, когда вы включаете эту функцию как часть другой собственной функции или Вы не контролировали записываемые данные и -g
добавляли ли они флаг. Нечто подобное можно сделать и с псевдонимами, см . Ответ Жиля для реализации.
Чтобы сделать результат еще более полезным, мы можем перебрать несколько переменных, переданных нашим функциям, предполагая, что каждое слово в массиве аргументов является именем переменной. Результат становится примерно таким:
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
}
При любом решении использование будет выглядеть так:
# 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"