14

I am using modules to control the packages on my system and I have python/2.7.2 installed as a module. I have simple python executable python_exe.py which I am going to call from a simple 'driving' script runit.sh. runit.sh script looks something like:

#!/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

Howver, when I just run ./runit.sh, it sells me "module: command not found". When I source runit.sh, however, it correctly loads the module. Why is this?

drjrm3
  • 2,055

2 Answers2

15

Because the module command is an alias or shell function (see "Package Initialization" in module(1)).  When you say source runit.sh, it’s like typing the module command directly into your interactive shell.  But when you say ./runit.sh, you are running a new, non-interactive shell.  Non-interactive shells generally do not have the standard aliases and shell functions set up.

module(1) says, “The Modules package and the module command are initialized when a shell-specific initialization script is sourced into the shell.  The script creates the module command, either as an alias or shell function, …”  If you need to run the module command in a script, find the initialization script that defines the module command and source it from the script.

  • Is this the difference between using .bashrc and .bash_profile? Only one of them has the initialization routines to start up the module system for use. – drjrm3 Apr 07 '15 at 21:52
  • I’m not exactly sure what you’re asking.  But: bash does the following by default (these actions can be overridden by options): a login shell reads ~/.bash_profile but not ~/.bashrc, an interactive shell that is not a login shell (e.g., what you get if you type bash as a command) reads ~/.bashrc but not ~/.bash_profile, and a non-interactive shell (e.g., one running a script) reads neither.  … (Cont’d) – Scott - Слава Україні Apr 07 '15 at 22:37
  • (Cont’d) …  This is probably why Cyrus suggested #!/bin/bash -i — because the -i option makes the shell interactive, and will therefore cause it to read ~/.bashrc.  IMHO, that’s overkill, because interactive mode might come with unwanted baggage (like writing to ~/.bash_history).  On the other hand, if module is defined as an alias (as opposed to a shell function), it won’t work in a non-interactive shell unless you say shopt -s expand_aliases, so maybe Cyrus’s answer is the best one. – Scott - Слава Україні Apr 07 '15 at 22:39
5

It seems that the simple invocation of the shell in your system does not inherit the alias (or the function) with which is defined module, so the shell is not able to find it (see below the note with the excerpts). Try type module from the prompt to see how module it is currently defined.

Essentially with source is like if you write each line of the script from the keyboard.
Note that on one side you're inheriting all the specific history of the current shell but, on the other one, the current shell will be subjected to all sides effect of your script and module invocation.

About the differences between to source a script and to execute it you can read on SuperUser Sep 2009 or Dec 2009, Ubuntu Feb 2011,Unix Aug 2011, Stackoverflow Dec 2012 or in many other places.

In this regard in the Modulefiles section there's a warning:

... Environment variables are unset when unloading a modulefile. Thus, it is possible to load a modulefile and then unload it without having the environment variables return to their prior state.

So it seems more wise to execute it in a script.

To accomplish the latter I can think:

  1. To use an interactive shell, neglecting the specific history of the present shell, modifying the shebang of your script with

    #!/bin/bash -i
    

    An interactive shell reads commands from user input on a tty. Among other things, such a shell reads startup files on activation, displays a prompt, and enables job control by default...

  2. If instead you prefer to inherit the specific story of the present shell, you can try to source it ... but in a subshell

    ( source runit.sh )
    
  3. Try to find the current alias/function of module with type module then modify in consequence your script. Note some environment variable can be not set for module.
    If you want you can find the initialization scripts in the directory$MODULESHOME/init/<shell>.


Comment
As remembered in the Q&A of modules

A child process (script) can not change the parent process environment. A module load in a script only affects the environment for the script itself. The only way you can have a script change the current environment is to source the script which reads it into the current process.

So if you want to avoid to modify the current environment I think it's better to try to change the shebang (1) or source the script in a subshell (2). I'm not completely sure about the usability of the case(3).


Note
Excerpts from manual and description pages of modules

module is a user interface to the Modules package. The module alias or function executes the modulecmd program and has the shell evaluate the command's output. The first argument to modulecmd specifies the type of shell.

The Modules package and the module command are initialized when a shell-specific initialization script is sourced into the shell. The script creates the module command, either as an alias or shell function, creates Modules environment variables

Hastur
  • 2,355