在 bash 或 zsh 中序列化 shell 變數
有沒有辦法序列化一個shell變數?假設我有一個變數
$VAR
,並且我希望能夠將其保存到文件或其他文件中,然後稍後再讀回以獲取相同的值?有沒有一種便攜的方式來做到這一點?(我不這麼認為)
有沒有辦法在 bash 或 zsh 中做到這一點?
**警告:**使用這些解決方案中的任何一個,您都需要知道您相信數據文件的完整性是安全的,因為它們將在您的腳本中作為 shell 程式碼執行。保護它們對腳本的安全至關重要!
用於序列化一個或多個變數的簡單內聯實現
typeset
是的,在 bash 和 zsh 中,您都可以使用內置函式和參數以易於檢索的方式序列化變數的內容-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,包括在不同 shell 之間傳遞數據。Bash 將把它翻譯成它的內置
declare
函式,而 zsh 用它來實現它,typeset
但由於 bash 有一個別名,它可以以任何一種方式工作,因為我們typeset
在這裡使用 ksh 兼容性。使用函式的更複雜的通用實現
上面的實現非常簡單,但是如果你經常呼叫它,你可能想給自己一個實用函式來使它更容易。此外,如果您嘗試將上述內容包含在自定義函式中,您將遇到變數範圍的問題。這個版本應該消除這些問題。
請注意所有這些,為了保持 bash/zsh 的交叉兼容性,我們將修復這兩種情況,
typeset
因此declare
程式碼應該在一個或兩個 shell 中工作。這增加了一些體積和混亂,如果您只為一個或另一個外殼執行此操作,則可以消除這些。為此使用函式(或在其他函式中包含程式碼)的主要問題是,該
typeset
函式生成的程式碼在從函式內部返回到腳本時,預設創建一個局部變數而不是全域變數。這可以通過幾種黑客之一來解決。我最初嘗試解決這個問題是通過
sed
添加-g
標誌來解析序列化過程的輸出,以便創建的程式碼在重新獲取時定義一個全域變數。serialize() { typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh" } deserialize() { source "./serialized_$1.sh" }
請注意,時髦的
sed
表達式僅匹配“排版”或“聲明”的第一次出現並添加-g
為第一個參數。必須只匹配第一次出現,因為正如Stéphane Chazelas在評論中正確指出的那樣,否則它也會匹配序列化字元串包含文字換行符後跟單詞 declare 或 typeset 的情況。除了糾正我最初的解析錯誤之外, Stéphane還提出了一種不那麼脆弱的方法來破解這個問題,它不僅可以解決解析字元串的問題,而且可以成為一個有用的鉤子,通過使用包裝函式重新定義操作來添加額外的功能在重新獲取數據時採取。這假設您沒有使用 declare 或 typeset 命令玩任何其他遊戲,但是在您將此功能作為您自己的另一個功能的一部分或您無法控制正在寫入的數據以及是否
-g
添加了標誌。使用別名也可以做類似的事情,請參閱Gilles 的實現答案。為了使結果更加有用,我們可以通過假設參數數組中的每個單詞都是變數名來遍歷傳遞給我們函式的多個變數。結果變成了這樣:
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"