Pourquoi ne puis-je pas charger des modules pendant l'exécution de mon script bash, mais uniquement lors de son sourcing?

13

J'utilise des modules pour contrôler les packages sur mon système et je les ai python/2.7.2installés en tant que module. J'ai un exécutable python simple python_exe.pyque je vais appeler à partir d'un simple script de «pilotage» runit.sh. runit.shle script ressemble à quelque chose comme:

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

Cependant, quand je viens de courir ./runit.sh, il me vend "module: commande introuvable". source runit.shCependant, lorsque je charge correctement le module. Pourquoi est-ce?

drjrm3
la source

Réponses:

13

Parce que la modulecommande est un alias ou une fonction shell (voir " Initialisation du package " dans le module (1) ). Quand vous dites source runit.sh, c'est comme taper la modulecommande directement dans votre shell interactif. Mais quand vous dites ./runit.sh, vous exécutez un nouveau shell non interactif. Les shells non interactifs n'ont généralement pas les alias et les fonctions de shell standard configurés.

module (1) dit: «Le package Modules et la commande module sont initialisés lorsqu'un script d'initialisation spécifique au shell est fourni dans le shell. Le script crée la commande de module , soit en tant qu'alias, soit en tant que fonction shell,… »Si vous devez exécuter la modulecommande dans un script, recherchez le script d'initialisation qui définit la modulecommande et sourcecela à partir du script.

Scott
la source
Est-ce la différence entre l'utilisation de .bashrc et .bash_profile? Un seul d'entre eux a les routines d'initialisation pour démarrer le système de modules à utiliser.
drjrm3
Je ne sais pas exactement ce que vous demandez. Mais: bash fait ce qui suit par défaut (ces actions peuvent être remplacées par des options): un shell de connexion lit `~ / .bash_profile` mais pas ~/.bashrc, un shell interactif qui n'est pas un shell de connexion (par exemple, ce que vous obtenez si vous tapez bashcomme une commande) lit ~/.bashrcmais pas ~/.bash_profile, et un shell non interactif (par exemple, un exécutant un script) ne lit ni l'un ni l'autre. … (Suite)
Scott
(Suite)… C'est probablement pourquoi Cyrus a suggéré #!/bin/bash -i- parce que l' -ioption rend le shell interactif, et le fera donc lire ~/.bashrc. À mon humble avis, c'est exagéré, car le mode interactif peut venir avec des bagages indésirables (comme écrire à ~/.bash_history). D'un autre côté, si moduleest défini comme un alias (par opposition à une fonction shell), il ne fonctionnera pas dans un shell non interactif sauf si vous le dites shopt -s expand_aliases, alors peut-être que la réponse de Cyrus est la meilleure.
Scott
4

Il semble que la simple invocation du shell dans votre système n'hérite pas de l'alias (ou de la fonction) avec lequel est défini module, donc le shell n'est pas en mesure de le trouver (voir ci-dessous la note avec les extraits). Essayez à type modulepartir de l'invite pour voir comment moduleil est actuellement défini.

Essentiellement avec la source, c'est comme si vous écriviez chaque ligne du script à partir du clavier.
Notez que d'un côté vous héritez de tout l'historique spécifique du shell actuel mais, de l'autre, le shell actuel sera soumis à tous les effets de votre script et de votre moduleinvocation.

À propos des différences entre la source d'un script et son exécution, vous pouvez lire sur SuperUser Sep 2009 ou Dec 2009 , Ubuntu Feb 2011 , Unix Aug 2011 , Stackoverflow Dec 2012 ou dans de nombreux autres endroits.

À cet égard, dans la section Modulefiles , il y a un avertissement :

... Les variables d'environnement ne sont pas définies lors du déchargement d'un fichier de modules. Ainsi, il est possible de charger un modulefile puis de le décharger sans que les variables d'environnement reviennent à leur état antérieur.

Il semble donc plus judicieux de l'exécuter dans un script .

Pour accomplir ce dernier je pense:

  1. Pour utiliser un shell interactif , en négligeant l'historique spécifique du shell actuel, en modifiant le shebang de votre script avec

    #!/bin/bash -i

    Un shell interactif lit les commandes de l'entrée utilisateur sur un tty. Entre autres choses, un tel shell lit les fichiers de démarrage lors de l'activation, affiche une invite et active le contrôle des travaux par défaut ...

  2. Si à la place vous préférez hériter de l'histoire spécifique du shell actuel, vous pouvez essayer de la source ... mais dans un sous - shell

    ( source runit.sh )
  3. Essayez de trouver l'alias / la fonction actuelle de moduleavec type modulepuis modifiez en conséquence votre script. Notez que certaines variables d'environnement ne peuvent pas être définies pour module.
    Si vous le souhaitez, vous pouvez trouver les scripts d'initialisation dans le répertoire $MODULESHOME/init/<shell>.


Commentaire
Comme rappelé dans les Q&R des modules

Un processus enfant (script) ne peut pas modifier l'environnement de processus parent. Une charge de module dans un script affecte uniquement l'environnement du script lui-même. La seule façon dont un script peut changer l'environnement actuel est de trouver le script qui le lit dans le processus en cours.

Donc, si vous voulez éviter de modifier l'environnement actuel, je pense qu'il vaut mieux essayer de changer le shebang (1) ou source le script dans un sous-shell (2). Je ne suis pas complètement sûr de l'utilisabilité du boîtier (3).


Remarque
Extraits des pages de manuel et de description des modules

moduleest une interface utilisateur pour le module Modules. L' modulealias ou la fonction exécute le modulecmdprogramme et demande au shell d'évaluer la sortie de la commande. Le premier argument pour modulecmdspécifier le type de shell.

Le package Modules et la modulecommande sont initialisés lorsqu'un script d'initialisation spécifique au shell est fourni dans le shell . Le script crée la commande module, sous forme d'alias ou de fonction shell, crée des variables d'environnement Modules

Hastur
la source
Mais il n'essaie pas d'affecter l'environnement du processus parent; il essaie seulement de faire exécuter son exécutable Python à partir du script . De plus, votre réponse n'explique pas pourquoi il reçoit le message d'erreur «module: commande introuvable».
Scott
@Scott Merci. Avant de couper par inadvertance la plus grande partie de la réponse et de n'en poster qu'un fragment. Réponse réécrite.
Hastur
1
+1.  ( source runit.sh )est une bonne réponse; Je n'y avais pas pensé. Et une bonne collection de références.
Scott