bash:定义一个文件局部变量,不可被源脚本访问

34

假设我有一个bash脚本文件config.sh。它的作用是被其他脚本调用,定义的变量被用作上层脚本的自定义。

问题在于,如果config.sh有一个临时变量,而其名称与上层脚本的变量冲突,它会破坏上层脚本的变量。

config.sh:

TMP1=abc
CONFIG_INPUT_DIR="$TMP1"/in
CONFIG_OUTPUT_DIR="$TMP1"/out

高层脚本:

TMP1=def
source config.sh
echo $TMP1

最后一个echo打印的是abc,而不是def

解决方案 1

我的当前解决方案是将一个随机字符串附加到临时变量名中,以使它几乎不可能发生冲突。例如:

TMP1_vFc9Uiew=abc
CONFIG_INPUT_DIR="$TMP1_vFc9Uiew"/in
CONFIG_OUTPUT_DIR="$TMP1_vFc9Uiew"/out
unset TMP1_vFc9Uiew

这样做很麻烦,而且让代码难以阅读,除此之外也无法完美解决问题。

Solution 2 使用 local 关键字

经过一些搜索,我了解到了 local 关键字。 但是当我将 TMP1 声明为 local 时,bash会报错:config.sh: line 1: local: can only be used in a function

因此,我的另一个解决方案是将整个配置脚本包装成一个函数:

function config_func_rZ0Yqkpm() {
  local TMP1=abc
  CONFIG_INPUT_DIR="$TMP1"/in
  CONFIG_OUTPUT_DIR="$TMP1"/out
}
config_func_rZ0Yqkpm
unset config_func_rZ0Yqkpm

这个解决方案在可维护性和可读性方面比之前的方案更好,但是与方案1一样存在一些可能会发生冲突的情况。

问题

我想知道更加健壮和智能的解决方案,而且没有任何可能发生冲突的情况。

谢谢。

3个回答

10

我从keychain实用程序中学到的一个技巧是使用一个程序来构建一个可用于导出你的程序变量的source文件。你可以修改你的脚本来echo你想要设置的变量,然后从你的程序输出进行源码处理:

$ echo $FOO

$ source <(echo FOO=bar)
$ echo $FOO
bar
$ 

我使用了echo FOO=bar来模拟更大的脚本;你的程序可能会更复杂。重要的是,你必须修改你的程序,输出你想设置的变量和值,而不仅仅是设置它们。这样可以让你决定哪些变量是公开的,哪些是私有的,代价是增加了另一个shell进程。


ssh-agent 使用类似的技术,使用 -c-s 选项。 - jjlin
@jjlin:谢谢!我以为keychain不是我第一次看到它的地方,但这是我找到文档的第一个地方。:)有趣的是,在ssh-agent手册中就在那里摆着... - sarnold
据我所知,使用这种技术需要调用者始终注意使用源命令。难道没有一种技术可以在不引起这些不便的情况下更改调用者的环境变量吗? - isaias-b

10

你可以避免使用变量,而是在config.sh中使用函数来保存你的值:

get_dirname() { echo "abc"; }
CONFIG_INPUT_DIR="$(get_dirname)/in"
CONFIG_OUTPUT_DIR="$(get_dirname)/out"
unset -f get_dirname

如果您仍然担心函数名称冲突,这并不能真正帮助您。


8
"ssh-agent"方法:
config.sh
#!/bin/bash
TMP=abc
printf "CONFIG_INPUT_DIR=%s/in\n" "$TMP"
printf "CONFIG_OUTPUT_DIR=%s/out\n" "$TMP"

主程序:

TMP1=def
eval "$(config.sh)"
echo "$TMP1"

你需要在每个printf命令的末尾加上“;”,以便通过“;”分隔eval命令。 - Sogartar
我应该引用命令替换,以便保留换行符。正在修复。 - glenn jackman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接