Discutons du système de recherche d’un programme à exécuter sous UNIX/Linux, et comment mettre en place un programme muni d’un nom voulu.
Recherche d’un programme à exécuter: la variable PATH
Le shell sous UNIX/Linux est le programme typiquement utilisé pour lancer un l’exécution d’un programme. Il s’agit d’un interpréteur de langage de programmation dont les routines peuvent être simplement définies comme des programmes externes.
Par conséquent, le shell doit avoir un mécanisme pour identifier où se trouvent ces routines, ces commandes qui ont une sémantique sur un système donné. La convention est qu’un petit nombre de répertoires sur le système peuvent contenir des programmes exécutables. La plupart des systèmes utilisent les suivants, au minimum:
/bin
/sbin
/usr/bin
/usr/sbin
/usr/local/bin
/opt/bin
La liste de ces répertoires pouvant contenir un programme est stockée dans la
variable d’environnement PATH
du shell: chaque répertoire y est séparé des
autres par le caractère deux-points (:
). Par exemple, chez moi:
$ echo $PATH
/home/hamelin/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
Tout programme présent dans l’un des répertoires listés dans la variable
PATH
peut être invoqué depuis le shell, sans décoration. Lorsque
l’invocation d’un programme commence par un caractère approprié, le shell
capture le nom et le recherche dans chacun des répertoires de PATH
, à tour
de rôle et dans l’ordre; il exécute le premier programme qu’il trouve qui
correspond à ce nom.
Naturellement, on peut changer la valeur de PATH
. Le fichier de
configuration à modifier dépend du shell. Dans le cas du shell bash
, la
définition de PATH
du système se trouve dans /etc/profile
; on peut se
faire une version modifiée pour soi en ajoutant une redéfinition de PATH
dans l’un des fichiers .bashrc
ou .profile
dans son répertoire de base.
Typiquement, on ajoute des répertoires au début ou à la fin de la valeur de
base, ainsi:
export PATH="<repertoires fouilles avant>:$PATH:<repertoires fouilles apres>"
Exécuter un programme hors des répertoires de PATH
Il est également possible d’exécuter un programme se trouvant hors des
répertoires listés dans PATH
. Il faut alors indiquer au shell où se trouve
se programme. Ainsi, pour la plupart des shells, si le premier mot d’une
ligne de commande contient une barre oblique (/
), le shell considère qu’on
donne le chemin d’accès vers le programme à exécuter. Si ce chemin commence
par /
, il est absolu; sinon, il est relatif au répertoire courant du
shell. Certains shells forcent d’ailleurs ces chemins relatifs à spécifier
d’abord le répertoire courant, de sorte que la ligne de commande doive
commencer par un point (.
). Quelques exemples:
$ /usr/lib/programme
$ ./programme_dans_le_repertoire_courant
$ ./programme/a/moi
$ programme/a/moi # Ne fonctionne pas sur tous les shells.
Le hashbang
Si c’est le shell qui détermine où trouver un programme à exécuter, c’est le noyau du système d’exploitation qui détermine comment l’exécuter. Les noyaux POSIX (UNIX, Linux, etc.) admettent deux types de programmes directement exécutables:
- Les binaires (ELF, Mach-O, etc.)
- Les scripts
Les premiers sont des programmes compilés sous forme binaire, typiquement à
l’aide d’un compilateur et/ou d’un assembleur. Les seconds sont des programmes
encodés sous la forme de fichiers texte, qui sont interprétés.
L’interpréteur doit se trouver quelque part sur le système. On indique où
trouver l’interpréteur à l’aide de la première ligne du fichier, où le chemin
d’accès vers l’interpréteur est précédé des caractères #!
(dièse = hash;
point d’exclamation = bang; #!
= hashbang).
#!/bin/bash # Script de *shell*
#!/usr/bin/python # Programme Python
#!/usr/bin/perl # Programme Perl (beurk)
Que le programme soit un binaire ou un script, pour que le noyau accepte de
l’exécuter, il doit être muni du droit d’exécution. Le compilateur donne ce
droit au binaire qu’il produit; en revanche, lorsqu’on écrit un script, on
doit donner ce droit soi-même. On le fait grâce à la commande chmod
:
$ chmod +x mon_script
$ ./mon_script
Type de fichier et script de lancement
Les scripts sont typiquement construits à l’aide d’un éditeur de texte, qui
procure plusieurs services appropriés au langage pour assister la
programmation (coloration lexicale, auto-indentation…). Les éditeurs qui
valent la peine d’être appris (e.g. Vim, Emacs) peuvent déterminer le type
d’un fichier en utilisant l’extension du nom du fichier (.py
, .rb
,
.sh
…) ou en utilisant la ligne hashbang discutée ci-haut.
Malheureusement, plusieurs éditeurs plus amicaux envers les débutants sont moins intelligents: ils n’arrivent à identifier le type de programme qu’en utilisant l’extension du script. Cela complique la nomenclature d’un script: on désire exécuter un script comme une commande, sans nécessairement taper l’extension. Garder l’extension du script est un problème d’interface usager.
Pour contourner ce problème, on peut utiliser un script de lancement. Il
s’agit d’un script de shell très petit qui permet de mettre en oeuvre
l’interpréteur pour un programme plus complexe, qu’on rédige dans un fichier
muni de l’extension appropriée. Par exemple, j’ai un programme Python que je
dois nommer monprog.py
. Dans le même répertoire, j’ajoute alors le script
suivant, que je sauvegarde sous le nom monprog
:
#!/bin/bash
python monprog.py $@
La variable d’environnement $@
désigne l’ensemble des paramètres de ligne de
commande passés au script monprog
; ils sont donc repassés au programme
Python. Pour permettre l’exécution, je donne le droit d’exécution au script de
lancement:
$ chmod +x monprog
Supposons que le code dans monprog.py
soit:
import sys
print "Hello world!"
for arg in sys.argv[1:]:
print arg
Alors, exécutons ce programme à l’aide du script de lancement:
$ ./monprog asdf 56 qwerty
Hello world!
asdf
56
qwerty