为什么我不能在执行 bash 脚本时加载模块,而只能在获取它时加载模块?

为什么我不能在执行 bash 脚本时加载模块,而只能在获取它时加载模块?

我在用模块控制我系统上的软件包,我已将其python/2.7.2安装为模块。我有简单的 python 可执行文件python_exe.py,我将从一个简单的“驾驶”脚本中调用它runit.shrunit.sh脚本看起来像这样:

#!/bin/bash
module load python/2.7.2
arg1=myarg1
arg2=15
arg3=$5
/path/to/python_exe.py -a $arg1 -b $arg2 -c $arg3

然而,当我刚刚运行时./runit.sh,它向我推销“模块:找不到命令”。然而,当我这样做时source runit.sh,它正确加载了模块。为什么是这样?

答案1

因为该module命令是别名或 shell 函数(参见“包初始化“ 在 模块(1))。当您说 时source runit.sh,就像module直接在交互式 shell 中键入命令一样。但是当您说 时./runit.sh,您正在运行一个新的非交互式 shell。非交互式 shell 通常没有设置标准别名和 shell 函数。

模块(1)说,“模块包和模块当特定于 shell 的初始化脚本传入 shell 时,命令将被初始化。该脚本创建模块命令,作为别名或 shell 函数,...”如果需要module在脚本中运行该命令,请从脚本中找到定义该module命令及其的初始化脚本。source

答案2

看来,系统中 shell 的简单调用不会继承定义的别名(或函数)module,因此 shell 无法找到它(请参阅下面带有摘录的注释)。尝试type module根据提示查看module当前是如何定义的。

本质上与来源就像您从键盘编写脚本的每一行一样。
请注意,一方面您继承了当前 shell 的所有特定历史记录,但另一方面,当前 shell 将受到脚本和module调用的所有方面的影响。

关于获取脚本和执行脚本之间的区别你可以阅读超级用户2009年9月或者2009年12月, 乌班图2011年2月,Unix2011年8月, 堆栈溢出2012年12月或在许多其他地方。

对此,在模块文件 部分有一个警告:

...卸载模块文件时未设置环境变量。因此,可以加载模块文件然后卸载它而不让环境变量返回到之前的状态。

所以在脚本中执行它似乎更明智

为了实现后者,我可以这样想:

  1. 要使用交互式外壳,忽略当前 shell 的具体历史,修改舍邦你的脚本的

    #!/bin/bash -i
    

    交互式 shell 从 tty 上的用户输入读取命令。除此之外,这样的 shell 会在激活时读取启动文件、显示提示并默认启用作业控制......

  2. 相反,如果您更喜欢继承当前 shell 的特定故事,您可以尝试获取它......但是在一个子外壳

    ( source runit.sh )
    
  3. 尝试找到当前的别名/函数,module然后type module修改您的脚本。请注意,某些环境变量不能设置module
    如果需要,您可以在目录中找到初始化脚本$MODULESHOME/init/<shell>


评论
正如所记得的模块问答

子进程(脚本)不能改变父进程的环境。脚本中的模块加载仅影响脚本本身的环境。让脚本更改当前环境的唯一方法是获取将其读入当前进程的脚本。

因此,如果您想避免修改当前环境,我认为最好尝试更改舍邦(1) 或在子 shell 中获取脚本 (2)。我不完全确定案例(3)的可用性。


笔记
手册和描述页摘录模块

module是模块包的用户界面。这module别名或函数执行modulecmd程序并让 shell 评估命令的输出。第一个参数modulecmd指定 shell 的类型。

模块包和module命令当特定于 shell 的初始化脚本传入 shell 时进行初始化。该脚本创建模块命令(作为别名或 shell 函数),创建模块环境变量

相关内容