Попередження: За допомогою будь-якого з цих рішень вам слід пам’ятати, що ви довіряєте цілісності файлів даних, щоб бути безпечними, оскільки вони виконуватимуться як код оболонки у вашому сценарії. Забезпечення їх є першорядним для безпеки вашого сценарію!
Проста вбудована реалізація для серіалізації однієї або декількох змінних
Так, і в 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
виразний вираз повинен відповідати лише першому виникненню або 'typeset', або 'оголосити', і додати -g
як перший аргумент. Потрібно лише узгоджувати перше виникнення, оскільки, як правильно вказував у коментарях Стефан Шазелас , інакше воно також відповідатиме випадкам, коли серіалізований рядок містить буквальні нові рядки з подальшим словом оголошення або набір тексту.
У доповненні до виправлення моєї початкового розбору безтактності , 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"