我用下面的代码在磁盘上导出bash变量:
export_to_disk() {
### Export variable to disk for later reload
eval "echo export $1="$$1"" > $2
#source $2
}
export_to_disk $mvar file.sh
但是,导出的变量被截断。如何解决?
eval
是一个不必要的安全风险来源,并且强制使用export
意味着您的代码不能正确地保留变量的原始标志(实际上可能会或可能不会导出)。
让shell自己负责序列化变量,以一种相同的shell能够成功反序列化它的方式:
export_to_disk() {
declare -p "$1" >"$2"
}
export_to_disk mvar file.sh # notice mvar not $mvar
…但是更聪明的调用约定是将文件名作为第一个参数,这样所有后面的参数都被当作变量来序列化到该文件中:
export_to_disk() {
local export_to_disk__filename=$1; shift || return
declare -p "$@" >"$export_to_disk__filename"
}
export_to_disk file.sh mvar anothervar thirdvar
也就是说,如果您真的想只支持可以导出的字符串类型变量(失去对数组&c的支持),您可以不使用declare -p
:
export_to_disk() {
local export_to_disk__filename=$1; shift || return
local export_to_disk__varname
for export_to_disk__varname in "$@"; do
printf '%s=%qn; export %qn' "$export_to_disk__varname" "${!export_to_disk__varname}" "$export_to_disk__varname"
done
}
让我们来分析一下:
- 在这个示例和前面的示例中,使用以
export_to_disk__
开头的长变量名来防止冲突:如果我们将其命名为var
这样简单的东西,那么这个程序不能用于保存名为var
的变量。为变量使用我们自己的命名空间使函数更复杂,但也使它更强大。 - 因为我们声明了自己的局部变量,所以我们为自己使用而定义的变量在被调用后不会泄漏到周围的作用域中。
- 因为我们在给变量赋值第一个参数后使用了
shift
,所以当我们稍后迭代剩余的参数时,它将从参数列表中删除,因此不再在"$@"
中。 - 当我们使用
printf %q
时,我们生成了一个求值安全的版本的变量值,即使故意使用不友好或危险的变量名也可以防止安全问题。(因为我们对变量名称使用printf %s
,所以不是真正有效的变量名称可能会导致生成不安全的代码;因此,如果你在源代码中使用这个命令运行,你应该确保你信任这些名称,即使你不相信这些值)。