关于环境变量:在modulefile中加载模块时Env不修改

Env not modify when loading module in modulefile

我想将一个模块加载到一个模块文件中(以解决依赖关系)。

我的模块:

1
2
3
4
5
6
7
8
9
10
11
12
#%Module########################################
##
##  Modulefile
#
proc ModulesHelp { } {
    puts stderr"Env for MyProg"
}
proc addPath {var val} {
    prepend-path $var $val
}
module load MyOtherModule
addPath   PATH   /opt/MyModule/bin

我的其他模块:

1
2
3
4
5
6
7
8
9
10
11
#%Module########################################
##
##  Modulefile
#
proc ModulesHelp { } {
    puts stderr"Env for MyOtherProg"
}
proc addPath {var val} {
    prepend-path $var $val
}
addPath   PATH   /opt/MyOtherModule/bin

当我运行 module load MyModule 时,两个模块似乎都已加载,但环境不正确:

1
2
3
4
5
$module list
Currently Loaded Modulefiles:
  1) MyModule   2) MyOtherModule
$echo $PATH
/opt/MyModule/bin:/usr/bin:/bin

如果我在 module load MyOtherModule 行之后的 MyModule 中添加行 foreach p [array names env] { set tmp $env($p) } 或至少 set tmp $env(PATH) 行,则环境已正确修改。如果我不使用我的函数 addPath 但我直接使用 prepend-path 命令,它也可以正常工作,这有点烦人,因为我当然想在 addPath 函数中做更多的事情。

任何人都知道发生了什么以及我缺少什么?


prepend-path 可能正在做一些"聪明"的事情来管理变量;它到底是什么我不知道也不需要知道,因为我们可以使用通用 Tcl 解决所有问题。为了使你的package工作,使用 uplevel 在适当的范围内评估代码,尽管你需要考虑是使用全局范围(名称 #0)还是调用者的范围(1,这是默认值);当从全局级别调用您的过程 addPath 时,它们是相同的,但在其他方面可能会完全不同,而且我不知道模块系统处理还有什么其他奇怪的地方。

为了演示,试试这个 addPath:

1
2
3
4
5
proc addPath {var val} {
    puts stderr"BEFORE..."
    uplevel 1 [list prepend-path $var $val]
    puts stderr"AFTER..."
}

我们使用 list 来构造要在调用者范围内评估的事物,因为它保证生成无替换的单命令脚本。 (还有有效的列表。)这是在 Tcl 中进行代码生成的全部秘诀:保持简单,使用 list 进行任何需要的引用,当事情变得复杂时调用帮助程序(使用合适的参数),并使用 uplevel 控制评估范围。

(NB: upvar 也很有用——它将局部变量绑定到另一个作用域中的变量——但这不是你在这里推荐使用的。我提到它是因为它可能是如果你做更复杂的事情会很有用...)