Les variables d'environnement
Lire et définir des variables d’environnement dans le shell.
Objectifs
À la fin de cette leçon, vous saurez :
- expliquer ce qu'est l'environnement d'un processus et comment il se transmet ;
- lister et lire les variables d'environnement ;
- créer une variable, la exporter (ou non) et prédire qui la verra ;
- comprendre
PATH, la variable qui explique pourquoi vos commandes « marchent ».
🔗 Pour vous rafraîchir la mémoire : du programme au processus · observer les codes de retour
À quoi ça sert : configurer sans modifier le code
Une variable d'environnement est une paire NOM=valeur que le système attache à chaque processus au moment de son lancement. C'est un canal de configuration : le programme lit ce contexte au démarrage et adapte son comportement — sans que personne n'ait touché à une seule ligne de son code.
Exemples que vous rencontrerez très vite dans ce cursus :
PORT=3001 node server.js→ le serveur écoute sur 3001 ;DATABASE_URL=...→ où trouver la base ;NODE_ENV=production→ mode de fonctionnement.
Le même binaire devient ainsi réutilisable partout : en dev avec des valeurs, en production avec d'autres. Cette idée est si centrale en développement web que vous la retrouverez dans chaque projet professionnel (fichiers .env, secrets, configuration de déploiement).
D'où vient l'environnement ? De l'héritage.
Souvenez-vous du mécanisme de la leçon 2-01 : quand votre shell lance un programme, le noyau crée un nouveau processus. Au moment de cette création, le shell transmet une copie de sa table d'environnement au nouveau processus :
shell (a sa table : HOME, USER, PATH...)
│ lancement de node
▼ copie de la table
node (reçoit SA copie, la consulte via process.env)
Trois conséquences logiques mais contre-intuitives, à bien graver :
- Le processus enfant reçoit une copie : s'il modifie sa propre copie, le parent n'en sait rien.
- Une variable définie dans le parent après un lancement ne remonte pas dans le processus déjà lancé.
- L'héritage descend de génération en génération : shell → node → processus fils de node...
Observer l'environnement
$ env
SHELL=/bin/bash
USER=cours
HOME=/home/cours
PATH=/usr/local/bin:/usr/bin:/bin:...
LANG=fr_FR.UTF-8
...
Chaque ligne est une paire NOM=valeur. Les plus familières :
| Variable | Contient | Utilité |
|---|---|---|
HOME | votre dossier personnel | c'est ce que ~ développe |
USER | votre nom d'utilisateur | ce que lit whoami en interne |
PATH | liste de dossiers séparés par : | où le shell cherche les commandes (voir plus bas) |
SHELL | votre shell | /bin/bash sur la VM |
LANG | langue et encodage | messages et formats locaux |
Pour lire une variable précise :
$ printenv USER
cours
Et depuis le shell lui-même, avec substitution :
$ echo "Bonjour $USER, ton dossier est $HOME"
Bonjour cours, ton dossier est /home/cours
$ echo 'Bonjour $USER'
Bonjour $USER
Comparez les deux dernières lignes : entre guillemets doubles, $NOM est remplacé par sa valeur avant l'exécution ; entre guillemets simples, rien n'est substitué, le texte passe tel quel. Ce détail paraît minuscule ; il explique pourtant la moitié des surprises de script shell des débutants.
Définir une variable : deux niveaux de visibilité
Créons une variable et observons qui la voit :
$ MA_VAR="essai"
$ echo "$MA_VAR"
essai
$ printenv MA_VAR
$
Fait notable : echo (exécuté par le shell, qui connaît ses propres variables) affiche la valeur, mais printenv (un programme distinct, donc un processus enfant) ne voit rien. Explication : sans précaution, MA_VAR reste une variable interne au shell, elle ne fait pas partie de la table transmise aux enfants.
Pour l'ajouter à la table héritable, on l'exporte :
$ export MA_VAR="essai"
$ printenv MA_VAR
essai
$ bash -c 'echo "vu par un sous-shell : $MA_VAR"'
vu par un sous-shell : essai
(bash -c 'commande' lance un nouveau bash exécutant cette commande — un vrai processus enfant, parfait pour tester l'héritage.)
Règle de sobriété professionnelle : on n'exporte que ce qui doit être transmis. Votre shell contient des dizaines de variables internes utiles à lui seul ; l'environnement hérité doit rester une petite table d'intentions.
Variante éclair, pour donner une variable à une seule commande sans toucher à la session :
$ SALUT=coucou bash -c 'echo "$SALUT"'
coucou
$ printenv SALUT
$
La variable a existé uniquement pour la durée de ce processus. C'est exactement la forme utilisée par les vrais outils (PORT=3001 node server.js).
Pour supprimer : unset MA_VAR. Et pour voir si tout est compris : après unset, echo "$MA_VAR" affiche une ligne vide — le $NOM inconnu est remplacé par rien, silencieusement. Piège classique de script : une faute de frappe dans un nom de variable ne provoque aucune erreur, juste une valeur vide.
PATH : pourquoi ls marche depuis n'importe où
Question que peu de gens se posent, tant la réponse semble évidente : quand vous tapez ls, d'où vient le programme ? Vous n'avez jamais écrit /usr/bin/ls. La réponse tient dans PATH :
$ echo "$PATH"
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
C'est une liste ordonnée de dossiers. Quand vous tapez une commande sans chemin (ni / ni ./), le shell cherche un exécutable de ce nom dans chaque dossier de PATH, dans l'ordre :
- chercher
lsdans/usr/local/bin→ absent ; - chercher dans
/usr/bin→ trouvé ! → lancer/usr/bin/ls.
Tout s'éclaire d'un coup :
- pourquoi
which noderépond/usr/bin/node: c'est le premier trouvé dans PATH ; - pourquoi une commande fraîchement installée peut rester introuvable : elle est hors PATH ;
- pourquoi taper
mon-script.shéchoue alors que./mon-script.shmarche : un nom nu déclenche la recherche dans PATH (votre dossier courant n'y est pas, volontairement — mesure de sécurité), tandis que./désigne explicitement « ici ».
Vous verrez PATH resurgir plusieurs fois dans ce cursus (installation de Node, scripts npm) ; retenez dès maintenant sa définition : la carte des tiroirs où le shell cherche vos commandes.
Exercice
- Affichez
$USERpuis vérifiez avecprintenv USER. - Créez
MA_VARnon exportée, affichez-la avececho, puis cherchez-la avecprintenvet dansenv. Expliquez la différence. - Exportez
MA_VAR="test"puis relisez-la dans un sous-shell avecbash -c '...'. - Lancez
SECRET=motdepasse bash -c 'printenv SECRET', puis cherchezSECRETavecprintenv SECRETdans votre shell. Où existe-t-elle ? - Affichez
PATH, localiseznodeavecwhich, puis retrouvez le même dossier dans la liste de PATH. - Question piège : dans un terminal A, lancez
export DEMO=oui. Dans un terminal B ouvert ensuite,printenv DEMOrépond-il quelque chose ?
Résumé
- L'environnement = table
NOM=valeurattachée au processus, copiée vers les enfants au lancement. envliste tout ;printenv NOMlit précisément ;"$NOM"se substitue dans le shell (doubles quotes obligatoires).- Variable interne : visible du seul shell.
export: ajoutée à l'héritage.VAR=x commande: le temps d'une commande. PATHest la liste des dossiers où le shell résout les commandes nues.- Configurer par l'environnement = adapter un même programme sans toucher à son code.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Exercice 2. echo "$MA_VAR" affiche la valeur car echo est traité par le shell, qui connaît ses variables internes. printenv MA_VAR et env | grep MA_VAR ne renvoient rien : ces programmes sont des processus enfants, et ils n'ont reçu que les variables exportées. La variable existe bel et bien, mais confinée au shell.
Exercice 3.
$ export MA_VAR="test"
$ bash -c 'echo "$MA_VAR"'
test
Le sous-bash a reçu une copie de l'environnement du parent, MA_VAR comprise.
Exercice 4.
$ SECRET=motdepasse bash -c 'printenv SECRET'
motdepasse
$ printenv SECRET
$
La variable n'a existé que dans l'environnement du bash -c (et de ses propres enfants potentiels). Votre shell courant ne la connaît pas : c'est la forme « variable éphémère », idéale pour les secrets qu'on ne veut pas laisser traîner dans la session.
Exercice 5.
$ which node
/usr/bin/node
$ echo "$PATH"
/usr/local/bin:/usr/bin:/bin:...
On retrouve /usr/bin comme deuxième tiroir de la liste : le shell y a trouvé node après avoir cherché en vain dans /usr/local/bin.
Exercice 6. Non : les deux terminaux sont deux shells indépendants, chacun avec sa propre table. export ajoute la variable à l'environnement du shell A et de ses futurs enfants seulement. Le shell B, lancé avant ou après, n'a rien reçu — l'environnement se transmet à la naissance d'un processus, il ne se diffuse pas entre frères. Pour rendre une variable disponible dans tous vos futurs terminaux, il faut la placer dans un fichier de démarrage du shell (~/.bashrc), sujet que vous croiserez bientôt.