シェル変数を直列化する方法はありますか?変数があり、$VAR
それをファイルや他の場所に保存してから、後でもう一度読んで同じ値を取得できるようにしたいとします。
これを行うポータブルな方法はありますか? (私はそうは思わない)
Bashまたはzshでこれを行う方法はありますか?
ベストアンサー1
警告する:これらのソリューションでは、データファイルがスクリプト内のシェルコードとして実行されるため、データファイルの整合性を安全に信頼できることを認識する必要があります。これを保護することは、スクリプトのセキュリティにとって非常に重要です!
1つ以上の変数を直列化するための単純なインライン実装
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で機能します。 Bashはこれをdeclare
zshがこれを達成するために使用する組み込み関数に変換しますtypeset
が、bashにはエイリアスがあるため、typeset
kshの互換性のためにここで使用できます。
関数を使用したより複雑な一般的な実装
上記の実装は非常に簡単ですが、頻繁に呼び出す場合はユーティリティ関数を簡単に提供できます。また、上記の内容をユーザー定義関数に含めようとすると、変数範囲の指定に関する問題が発生します。このバージョンでは、これらの問題は削除されます。
bash / zshの互換性を維持するために、両方のケースを修正して、コードが両方または両方で機能するようにしますtypeset
。declare
これにより、1つのシェルまたは別のシェルにのみこれを実行した場合に削除できるわずかな量と混乱が追加されます。
そのために関数を使用すること(または他の関数にコードを含めること)の主な問題は、関数typeset
によって生成されたコードが関数内でスクリプトに返されると、グローバル変数の代わりにローカル変数を生成することです。
この問題は、いくつかのトリックのいずれかで解決することができます。この問題を解決しようとする最初の試みは、生成されたコードが戻り値でグローバル変数を定義するように、シリアライゼーションプロセスの出力を解析するフラグを追加することsed
でした。-g
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
ファンキーsed
式は、最初の引数として追加された「typeset」または「declare」の最初の項目とのみ一致します-g
。最初の項目だけが一致する必要があるため、スティーブン・チャジェラスコメントに正しく指定されています。それ以外の場合、シリアライズされた文字列にリテラル改行文字と単語宣言または組版が含まれている場合も一致します。
元の解析を修正することに加えて失礼、スティーブンド提案文字列解析の問題を解決するだけでなく、データの再インポート時に実行されるアクションをオーバーライドするラッパー関数を使用してアドインを追加するのに役立つフックになる可能性のある脆弱なハッキングです。これは、宣言や組版命令を使用して他のゲームをプレイしないと仮定します。ただし、この関数を他の関数の一部として含めるか、どのデータが作成されるかを制御できない場合は、ロゴが追加されました-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"