Cảnh báo: Với bất kỳ giải pháp nào trong số này, bạn cần lưu ý rằng bạn đang tin tưởng tính toàn vẹn của các tệp dữ liệu để đảm bảo an toàn vì chúng sẽ được thực thi dưới dạng mã shell trong tập lệnh của bạn. Bảo vệ chúng là tối quan trọng đối với bảo mật tập lệnh của bạn!
Thực hiện nội tuyến đơn giản để tuần tự hóa một hoặc nhiều biến
Có, trong cả bash và zsh, bạn có thể tuần tự hóa nội dung của một biến theo cách dễ dàng truy xuất bằng cách sử dụng typeset
nội dung và -p
đối số. Các định dạng đầu ra là như vậy mà bạn có thể chỉ đơn giản source
là đầu ra để lấy lại công cụ của bạn.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Bạn có thể lấy lại nội dung của mình như thế này sau này trong tập lệnh của bạn hoặc trong tập lệnh khác hoàn toàn:
# Load up the serialized data back into the current shell
source serialized_data.sh
Điều này sẽ làm việc cho bash, zsh và ksh bao gồm cả việc truyền dữ liệu giữa các shell khác nhau. Bash sẽ dịch cái này sang declare
hàm dựng sẵn của nó trong khi zsh thực hiện điều này với typeset
nhưng vì bash có bí danh để nó hoạt động theo cách nào đó để chúng tôi sử dụng typeset
ở đây để tương thích ksh.
Triển khai tổng quát phức tạp hơn bằng cách sử dụng các hàm
Việc thực hiện ở trên thực sự đơn giản, nhưng nếu bạn gọi nó thường xuyên, bạn có thể muốn cung cấp cho mình một chức năng tiện ích để làm cho nó dễ dàng hơn. Ngoài ra, nếu bạn từng cố gắng bao gồm các chức năng tùy chỉnh bên trên, bạn sẽ gặp phải các vấn đề với phạm vi thay đổi. Phiên bản này sẽ loại bỏ những vấn đề đó.
Lưu ý cho tất cả những điều này, để duy trì khả năng tương thích chéo bash / zsh, chúng tôi sẽ sửa cả hai trường hợp typeset
và declare
do đó mã phải hoạt động trong một hoặc cả hai shell. Điều này thêm một số lượng lớn và lộn xộn có thể được loại bỏ nếu bạn chỉ làm điều này cho vỏ này hay vỏ khác.
Vấn đề chính với việc sử dụng các hàm cho điều này (hoặc bao gồm mã trong các hàm khác) là typeset
hàm tạo mã, khi được lấy lại thành tập lệnh từ bên trong hàm, mặc định tạo một biến cục bộ thay vì toàn cục.
Điều này có thể được sửa chữa với một trong một số hack. Nỗ lực ban đầu của tôi để khắc phục điều này là phân tích đầu ra của quá trình tuần tự hóa sed
để thêm -g
cờ để mã được tạo xác định một biến toàn cục khi có nguồn gốc trở lại.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Lưu ý rằng sed
biểu thức sôi nổi là chỉ khớp với lần xuất hiện đầu tiên của 'kiểu chữ' hoặc 'khai báo' và thêm -g
làm đối số đầu tiên. Điều cần thiết là chỉ khớp với lần xuất hiện đầu tiên bởi vì, như Stéphane Chazelas đã chỉ ra một cách đúng đắn trong các bình luận, nếu không, nó cũng sẽ khớp với các trường hợp trong đó chuỗi nối tiếp chứa các dòng chữ mới theo sau là từ khai báo hoặc sắp chữ.
Ngoài việc sửa lỗi giả lập phân tích cú pháp ban đầu của tôi , Stéphane cũng đề xuất một cách dễ dàng hơn để hack điều này, không chỉ bên cạnh các vấn đề với phân tích chuỗi mà còn có thể là một móc nối hữu ích để thêm chức năng bổ sung bằng cách sử dụng chức năng trình bao bọc để xác định lại các hành động được thực hiện khi tìm nguồn dữ liệu trở lại. Điều này giả sử bạn không chơi bất kỳ trò chơi nào khác bằng lệnh khai báo hoặc sắp chữ, nhưng kỹ thuật này sẽ dễ thực hiện hơn trong trường hợp bạn đưa chức năng này vào một chức năng khác của chính mình hoặc bạn không kiểm soát được dữ liệu được ghi và có -g
thêm cờ hay không . Một cái gì đó tương tự cũng có thể được thực hiện với các bí danh, xem câu trả lời của Gilles để thực hiện.
Để làm cho kết quả thậm chí hữu ích hơn, chúng ta có thể lặp qua nhiều biến được truyền cho các hàm của mình bằng cách giả sử rằng mỗi từ trong mảng đối số là một tên biến. Kết quả trở thành một cái gì đó như thế này:
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
}
Với một trong hai giải pháp, việc sử dụng sẽ như thế này:
# 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"