Peringatan: Dengan salah satu solusi ini, Anda harus menyadari bahwa Anda memercayai integritas file data agar aman karena akan dieksekusi sebagai kode shell dalam skrip Anda. Mengamankan mereka sangat penting untuk keamanan skrip Anda!
Implementasi inline sederhana untuk membuat serial satu atau lebih variabel
Ya, dalam bash dan zsh Anda bisa membuat serial konten dari suatu variabel dengan cara yang mudah diambil menggunakan typeset
builtin dan -p
argumen. Format output sedemikian rupa sehingga Anda dapat dengan mudah source
output untuk mendapatkan barang-barang Anda kembali.
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
Anda bisa mendapatkan barang-barang Anda kembali seperti ini nanti di skrip Anda atau di skrip lain sekaligus:
# Load up the serialized data back into the current shell
source serialized_data.sh
Ini akan bekerja untuk bash, zsh dan ksh termasuk mengirimkan data antara shell yang berbeda. Bash akan menerjemahkan ini ke declare
fungsi bawaannya sementara zsh mengimplementasikan ini dengan typeset
tetapi sebagai bash memiliki alias untuk ini berfungsi baik cara yang kami gunakan di typeset
sini untuk kompatibilitas ksh.
Implementasi umum yang lebih kompleks menggunakan fungsi
Implementasi di atas sangat sederhana, tetapi jika Anda sering memanggilnya, Anda mungkin ingin memberi diri Anda fungsi utilitas untuk membuatnya lebih mudah. Selain itu jika Anda pernah mencoba untuk memasukkan fungsi-fungsi khusus di dalam Anda akan mengalami masalah dengan pelingkupan variabel. Versi ini harus menghilangkan masalah-masalah itu.
Catatan untuk semua ini, untuk menjaga kompatibilitas silang bash / zsh, kami akan memperbaiki kedua kasus typeset
dan declare
sehingga kode harus bekerja di salah satu atau kedua shell. Ini menambahkan sejumlah besar dan kekacauan yang bisa dihilangkan jika Anda hanya melakukan ini untuk satu shell atau yang lain.
Masalah utama dengan menggunakan fungsi untuk ini (atau termasuk kode dalam fungsi lain) adalah bahwa typeset
fungsi menghasilkan kode itu, ketika bersumber kembali ke dalam skrip dari dalam suatu fungsi, default untuk membuat variabel lokal daripada yang global.
Ini dapat diperbaiki dengan satu dari beberapa peretasan. Upaya awal saya untuk memperbaikinya adalah mem-parsing output dari proses serialisasi sed
hingga menambahkan -g
flag sehingga kode yang dibuat mendefinisikan variabel global ketika bersumber kembali.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
Perhatikan bahwa sed
ekspresi funky hanya untuk mencocokkan dengan kemunculan pertama 'typeset' atau 'menyatakan' dan menambahkan -g
sebagai argumen pertama. Hal ini diperlukan untuk hanya mencocokkan kejadian pertama karena, seperti Stéphane Chazelas menunjukkan dengan benar dalam komentar, jika tidak, itu juga akan cocok dengan kasus di mana string serial berisi baris baru literal diikuti dengan kata menyatakan atau mengeset.
Selain mengoreksi awal parsing saya kecerobohan , Stéphane juga menyarankan cara yang kurang rapuh untuk hack ini yang tidak hanya langkah samping masalah dengan parsing string tetapi bisa menjadi hook berguna untuk menambah fungsionalitas tambahan dengan menggunakan fungsi pembungkus untuk mendefinisikan tindakan diambil saat sumber data kembali masuk. Ini mengasumsikan Anda tidak memainkan game lain dengan perintah mendeklarasikan atau mengeset, tetapi teknik ini akan lebih mudah untuk diterapkan dalam situasi di mana Anda memasukkan fungsi ini sebagai bagian dari fungsi lain dari Anda sendiri atau Anda tidak mengendalikan data yang sedang ditulis dan apakah -g
bendera itu ditambahkan atau tidak . Hal serupa juga bisa dilakukan dengan alias, lihat jawaban Gilles untuk implementasi.
Untuk menjadikan hasilnya lebih bermanfaat, kita dapat mengulangi beberapa variabel yang diteruskan ke fungsi kita dengan mengasumsikan bahwa setiap kata dalam array argumen adalah nama variabel. Hasilnya menjadi seperti ini:
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
}
Dengan salah satu solusi, penggunaan akan terlihat seperti ini:
# 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"