Laisser un commentaire

Maîtriser les variables d'environnement

Les variables d’environnements sont des variables du système accessibles à tous les programmes. Elles contiennent par exemple l’adresse et les credentials de la base de données, l’adresse du serveur de fichiers… Ces variables rendent aisé l’accès à l’information par différents programmes potentiellement codés dans différents langages, remplaçant ainsi avantageusement les fichiers de config.

Le Twelve-Factor App, dans son chapitre sur la configuration, recommande d’ailleurs l’usage des variables d’environnent plutôt que des fichiers de configuration. Cela permet clairement de séparer la configuration du code de l’application, et facilite la gestion des différents environnements (développement, staging, production…).

Voyons donc comment tirer profit de ce système en apprenant à configurer ces variables sur les serveurs Linux.

Afficher les variables

Certaines variables sont directement définies par le système. Par exemple le fameux PATH ou PWD – que l’on récupère généralement avec la commande pwd. Lorsque l’on veut afficher la valeur d’une variable, on utilise généralement la commande echo suivi du nom de la variable.

Néanmoins, il existe une fonction spécifique à cet usage. Cette dernière permet en outre d’afficher toutes les varibles d’environnent disponibles, il s’agit de printenv. Si vous l’invoquez sans autre paramètre, cette dernière affiche toutes les variables actuellement déclarées. Pour visualiser le contenu d’une variable en particulier, vous lui passez simplement le nom de la variable en argument (sans le “$”).

Les différents type de variables

Vous avez dors et déjà certainement compris qu’il existe deux grands types de variables :

Cependant, il y a plusieurs moyens de définir des variables et toutes n’ont pas la même portée. C’est donc ce dont nous allons parler maintenant.

Les variables locales

Ce ne sont à proprement parler même pas des variables d’environnement. On les créé simplement en déclarant une variable. Par exemple :

buzut=dev

# on l'affiche comme cela
echo $buzut

Cependant, comme elles sont locales, un script ou tout autre programme – qui tourne dans un processus enfant (son environnement propre) – ne pourra pas y accéder car elles ne sont pas exportées. Ainsi, même printenv buzut ne retournera rien.

Les variables locales ne sont utiles que dans des scripts locaux, dans des boucles par exemple.

# cela prend tout son sens dans une boucle
for f in *.jpg; do cwebp $f -o "$f".webp; done

Cependant, cette variable locale peut devenir une variable d’environnement via la commande export. Cette dernière indique justement qu’il faut exporter (rendre disponible) ladite variable aux processus enfants. Ainsi, après export buzut, si l’on teste de nouveau printenv buzut, on aura bien notre valeur de retour.

En outre, certaines commandes intégrées à bash (c’est également le cas pour d’autres shells) peuvent voir ces variables locales. Comme elles sont intégrées à bash, elles peuvent également accéder aux variables locales.

Ainsi, si vous invoquez la commande set ou declare, vous verrez vos variables non exportées ainsi que les fonctions déclarées dans votre .bash_profile.

Les variables d’environnement

Par opposition aux variables locales, nous l’avons vu, il y a les variables d’environnement. Ces dernières sont accessibles en dehors du processus courant.

Cependant, comme toute chose est toujours plus complexe qu’il n’y paraît, ce n’est pas parce que l’on exporte une variable qu’elle est disponible partout. Nous l’avons mentionné, l’export est valable pour l’environnement des processus enfants.

Ainsi, une variable déclarée et exportée dans un shell est disponible dans ce shell, ainsi que dans les processus lancés depuis ce dernier. Ces processus sont en effet des processus enfants et l’environnement courant leur est transmis lors de leur création.

Néanmoins, vous pouvez en faire l’expérience, si vous ouvrez deux terminaux distincts et que vous exportez une variable depuis le shell du terminal 1, celle-ci ne sera pas accessible sur le shell du terminal 2.

Le résultat est le même qu’il s’agisse de deux terminaux ouverts sur la même machine locale, de sessions ssh etc !

De nouveau, il y a des notions de portée pour les variables d’environnement. Nous en distinguons trois. Voyons donc de plus près quelles sont-elles et comment les mettre en place.

Variables d’environnement locales

C’est celles que nous avons vu jusque là. Elles sont déclarées dans l’environnement courant et ne sont valables que pour les commandes et programmes démarrés par cet environnement et ce, jusqu’à la fin de la session courante.

On peut penser que leur utilité est limité, mais ce n’est pas tout à fait le cas. Environnement courant n’est pas forcement synonyme d’interactif. Ainsi, on peut déclarer un environnement pour une crontask ou dans un script bash qui à son tour lance un programme.

Les unit files de SystemD permettent également de définir des variables d’environnement [en] qui seront disponibles pour le service ainsi managé.

Également fort pratique, bash possède un système de quick assignement. Cela permet de déclarer des variables qui ne seront disponibles que pour le processus enfant lancé. Petit exemple :

# bien connu des devs node.js
NODE_ENV=development node server.js

Dans cet exemple, NODE_ENV n’existe que pour le processus node. Il n’est pas déclaré dans l’environnement courant. Essayez par vous-même pour vous en convaincre !

Variables d’environnement de l’utilisateur

Si l’on veut des variables qui persistent entre reboot et soient accessibles depuis différentes sessions, il faut les déclarer au niveau du user. Cependant, comme leur nom l’implique, elles ne sont disponibles que pour un utilisateur donné.

De ce fait, si vous déclarez une variable au niveau de votre utilisateur toto et que c’est www-data qui lance un processus, le processus en question ne sera pas en mesure d’accéder à cette variable. C’est bien voulu !

Pour automatiquement déclarer une variable qui sera disponible pour un utilisateur donné, on se tourne vers le .bashrc ou le .bash_profile et on n’oublie pas d’exporter cette variable, sans quoi elle ne sera qu’une variable locale.

# depuis /home/buzut/.bash_profile
export buzut=tropfort

Et voilà, maintenant, pour tout utilisateur connecté sous buzut et pour tout programme lancé par cet utilisateur, la variable buzut est disponible.

Vous vous demandez peut-être quelle est la différence entre ces deux fichiers. .bash_profile est chargé lorsqu’il s’agit d’un login shell interactif. En revanche, bash chargera .bashrc lorsqu’il est invoqué en tant que shell interactif non login ou invoqué par un remote shell dæmon (typiquement rshd).

C’est un peu complexe, cependant, ce que vous devez retenir, c’est que sur un serveur distant qui est toujours accédé via ssh, ce sera toujours le .bashrc qui sera chargé.

Si vous souhaitez creuser les différences entre login/non login et interactive/non-interactive shells, vous pouvez lire ce thread sur StackExchange [en]. En outre, si vous voulez en savoir plus sur les priorités et les règles de chargement des différents fichiers, je vous invite à lire cet article [en].

Par ailleurs, si vous ne comprenez pas pourquoi sur macOS, tout se passe dans le .bash_profile, c’est que par défaut, Terminal lance un login shell pour chaque session. Cela peut d’ailleur se modifier dans les préférences.

Variables d’environnement globales

Ces variables sont disponibles pour tous les utilisateurs. Cela peut parfois sembler plus simple à gérer, cependant, vous risquez d’exposer des données sensibles à d’autres utilisateurs du système. En outre, aves les variables utilisateurs, un potentiel mysql_user peut avoir des valeurs différentes pour www-data que pour Node.js.

La solution la plus adaptée pour déclarer une variable globale est d’utiliser le fichier spécifiquement dédié : /etc/environment. Comme il s’agit d’un fichier de variables d’environnement et non d’un script, vous déclarez juste la variable et sa valeur var=value. Toute autre chose risquerait de poser des problèmes, donc pas d’export !

Alternativement, si vous avez besoin d’exécuter quelque chose, pour par exemple une déclaration via la commande set, vous pouvez créer un script dans /etc/profile.d/ ou même utiliser le .bashrc global qui se trouve dans /etc/bash.bashrc.

Fonctions dédiées

Nous avons parlé de la fonction set. Cette dernière est très puissante et nous nous contenterons la plupart du temps de export pour exporter les variables. Sachez juste que set -a permet d’automatiquement exporter toutes les varibles qui seront ensuite créés. Si vous désirez en savoir plus sur set, vous pouvez vous référer à la doc [en].

declare quant à elle, permet d’attribuer des propriétés particulières aux variables déclarées (array, int plutôt que string…). On ne l’utilise pas forcément tous les jours, mais il est bon de connaître son existence. Idem, un petit tour dans la doc [en] s’impose afin de prendre connaissance des différentes options.

Enfin, quel que soit le type de variable, la commande unset permettra de l’effacer purement et simplement.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```