Quelle est la différence d'utilisation entre les variables shell et les variables d'environnement?

16

En fait, je ne savais pas qu'il existe deux types de variables différentes auxquelles je peux accéder à partir de la ligne de commande. Tout ce que je savais, c'est que je peux déclarer des variables comme:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

ou y accéder avec un signe $, comme:

echo $foo
echo ${bar[1]}

ou en utilisant des variables intégrées, comme:

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

Maintenant, j'entends qu'il y a deux (au moins?) Types de variables: les variables shell et les variables d'environnement.

  • Quel est le but d'avoir deux types différents?
  • Comment savoir de quel type est une variable?
  • Quels sont les usages typiques de chacun?
sharkant
la source

Réponses:

14

Les variables d'environnement sont une liste de name=valuepaires qui existent quel que soit le programme (shell, application, démon…). Ils sont généralement hérités par les processus enfants (créés par une séquence fork/ exec): les processus enfants obtiennent leur propre copie des variables parentes.

Les variables shell n'existent que dans le contexte d'un shell. Ils ne sont hérités que dans les sous-coques (c'est-à-dire lorsque le shell est bifurqué sans execopération). Selon les fonctionnalités du shell, les variables peuvent être non seulement de simples chaînes comme celles de l'environnement, mais également des tableaux, des variables composées, des variables typées comme des nombres entiers ou à virgule flottante, etc.

Lorsqu'un shell démarre, toutes les variables d'environnement qu'il hérite de son parent deviennent également des variables de shell (à moins qu'elles ne soient pas valides en tant que variables de shell et autres cas d'angle comme ceux IFSqui sont réinitialisés par certains shells), mais ces variables héritées sont marquées comme exportées 1 . Cela signifie qu'ils resteront disponibles pour les processus enfants avec la valeur potentiellement mise à jour définie par le shell. C'est également le cas des variables créées sous le shell et marquées comme exportées avec le exportmot - clé.

Les tableaux et autres variables de type complexe ne peuvent être exportés que si leur nom et leur valeur peuvent être convertis en name=valuemodèle, ou lorsqu'un mécanisme spécifique au shell est en place (par exemple: bashexporte des fonctions dans l'environnement et certains shells exotiques, non POSIX comme rcet espeut exporter des tableaux ).

Ainsi, la principale différence entre les variables d'environnement et les variables de shell est leur portée: les variables d'environnement sont globales tandis que les variables de shell non exportées sont locales au script.

Notez également que les shells modernes (au moins kshet bash) prennent en charge une troisième portée de variables de shell. Les variables créées dans les fonctions avec le typesetmot-clé sont locales à cette fonction (la façon dont la fonction est déclarée active / désactive cette fonctionnalité sous kshet le comportement de persistance est différent entre bashet ksh). Voir /unix//a/28349/2594

1 Cela vaut pour les coquilles modernes comme ksh, dash, bashet similaire. Le shell Bourne hérité et les shells de syntaxe non Bourne comme cshont des comportements différents.

jlliagre
la source
1
Tout est hérité par les processus enfants car les enfants sont créés en tant que fork (une copie exacte) de leur parent. Le point avec les variables d'environnement est qu'elles sont transmises à l' execve()appel système et sont donc (généralement) utilisées pour conserver les données lors de l' exécution d'autres commandes (dans le même processus).
Stéphane Chazelas
Toutes les variables d'environnement ne sont pas converties en variables shell. Seuls ceux qui sont valides en tant que nom de variable shell (et à quelques exceptions près comme IFSdans certains shells).
Stéphane Chazelas
Des shells comme rc, espeuvent exporter des tableaux en utilisant un encodage adhoc. bashet rcpeut également exporter des fonctions à l'aide de variables d'environnement (là encore, en utilisant un encodage spécial).
Stéphane Chazelas
Dans ksh93, typesetrestreint la portée uniquement dans les fonctions déclarées avec la function foo { ...; }syntaxe, pas avec la foo() cmdsyntaxe Bourne ( ) (et sa portée statique n'est pas dynamique comme dans d'autres shells).
Stéphane Chazelas
@ StéphaneChazelas Merci d'avoir révisé! Réponse mise à jour pour tenir compte de vos remarques.
jlliagre
17

Variables shell

Les variables shell sont des variables dont la portée se trouve dans la session shell actuelle, par exemple dans une session shell interactive ou un script.

Vous pouvez créer une variable shell en attribuant une valeur à un nom inutilisé:

var="hello"

L'utilisation de variables shell est de garder une trace des données dans la session en cours. Les variables shell ont généralement des noms avec des lettres minuscules.

Variables d'environnement

Une variable d'environnement est une variable shell qui a été exportée. Cela signifie qu'elle sera visible en tant que variable, non seulement dans la session shell qui l'a créée, mais aussi pour tout processus (pas seulement les shells) démarré à partir de cette session.

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

ou

export VAR="hello"

Une fois qu'une variable shell a été exportée, elle reste exportée jusqu'à ce qu'elle ne soit pas définie, ou jusqu'à ce que sa "propriété d'exportation" soit supprimée (avec export -nin bash), il n'est donc généralement pas nécessaire de la réexporter. La suppression d'une variable avec la unsetsupprime (que ce soit une variable d'environnement ou non).

Les tableaux et les hachages associatifs dans bashet d'autres shells ne peuvent pas être exportés pour devenir des variables d'environnement. Les variables d'environnement doivent être de simples variables dont les valeurs sont des chaînes, et elles ont souvent des noms composés de lettres majuscules.

L'utilisation de variables d'environnement permet de garder une trace des données dans la session shell actuelle, mais aussi de permettre à tout processus démarré de prendre partie de ces données. Le cas typique de ceci est la PATHvariable d'environnement, qui peut être définie dans le shell et utilisée plus tard par n'importe quel programme qui veut démarrer des programmes sans spécifier un chemin complet vers eux.

La collection de variables d'environnement dans un processus est souvent appelée "l'environnement du processus". Chaque processus a son propre environnement.

Les variables d'environnement ne peuvent être "transférées", c'est-à-dire qu'un processus enfant ne peut jamais modifier les variables d'environnement dans son processus parent, et autre que la configuration de l'environnement pour un processus enfant au démarrage, un processus parent ne peut pas modifier l'environnement existant d'un processus enfant.

Les variables d'environnement peuvent être répertoriées avec env(sans aucun argument). En dehors de cela, elles apparaissent de la même manière que les variables shell non exportées dans une session shell. C'est un peu spécial pour le shell car la plupart des autres langages de programmation ne mélangent généralement pas les variables "ordinaires" avec les variables d'environnement (voir ci-dessous).

env peut également être utilisé pour définir les valeurs d'une ou plusieurs variables d'environnement dans l'environnement d'un processus sans les définir dans la session en cours:

env CC=clang CXX=clang++ make

Cela commence makeavec la variable d'environnement CCdéfinie sur la valeur clanget CXXdéfinie sur clang++.

Il peut également être utilisé pour nettoyer l'environnement d'un processus:

env -i bash

Cela commence , bashmais ne transfère pas l'environnement actuel au nouveau bashprocessus (il faudra encore avoir des variables d'environnement car elle crée de nouvelles de ses scripts d'initialisation du shell).

Exemple de différence

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

Autres langues

Il existe des fonctions de bibliothèque dans la plupart des langages de programmation qui permettent d'obtenir et de définir les variables d'environnement. Notez que puisque les variables d'environnement sont stockées comme une simple relation clé-valeur, elles ne sont généralement pas des "variables" du langage. Un programme peut récupérer la valeur (qui est toujours une chaîne de caractères) correspondant à une clé (le nom de la variable d'environnement), mais devra ensuite la convertir en un entier ou quel que soit le type de données que la langue attend de la valeur.

En C, les variables d' environnement sont accessibles en utilisant getenv(), setenv(), putenv()et unsetenv(). Les variables créées avec ces routines sont héritées de la même manière par tout processus que le programme C démarre.

D'autres langages peuvent avoir des structures de données spéciales pour accomplir la même chose, comme le %ENVhachage en Perl, ou le ENVIRONtableau associatif dans la plupart des implémentations de awk.

Kusalananda
la source
thx, explication brillamment claire. L'environnement est donc comme un grand champ dans lequel d'autres programmes peuvent vivre et voir chacune des variables d'environnement. Certains programmes ont leurs variables privées, seuls eux-mêmes peuvent les voir, comme le shell. mais il existe un mécanisme pour que les variables privées soient vues par tous appelées "exportation". Si cela est bien compris, la seule chose dont je ne suis pas sûr, c'est s'il peut exister plus d'un environnement en même temps?
sharkant
@sharkant Chaque processus en cours d'exécution a son propre environnement. Cet environnement est hérité du processus qui l'a démarré. Il n'y a jamais de "diaphonie" entre des environnements de processus différents. La seule façon de modifier une variable d'environnement dans un processus est que le processus lui-même le modifie.
Kusalananda
merci d'avoir claryfing ma compréhension. Chaque poisson dans son propre bocal à poissons. Qu'en est-il des processus qui engendrent d'autres processus? Les processus et leurs processus enfants sont-ils tous dans un même environnement ou chacun a-t-il le sien?
sharkant
1
@sharkant Il existe des fonctions de bibliothèque dans la plupart des langues qui permettent d'obtenir et de définir les variables d'environnement. En C, cela se fait avec getenv(), setenv(), putenv()et unsetenv(). Les variables créées avec ces routines sont héritées de la même manière par tout processus que le programme C démarre. D'autres langues peuvent avoir des structures de données spéciales pour la même chose, comme %ENVen Perl.
Kusalananda
1
FWIW: la exec*()famille de fonctions peut également définir l'environnement du processus en cours d'exécution.
Satō Katsura
5

Les variables shell sont difficiles à dupliquer.

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

Les variables d'environnement peuvent cependant être dupliquées; ils ne sont qu'une liste, et une liste peut avoir des entrées en double. Voici envdup.cpour cela.

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

Que nous pouvons compiler et exécuter en disant envdupensuite envpour nous montrer quelles variables d'environnement sont définies ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

Cela n'est peut-être utile que pour trouver des bogues ou autres bizarreries dans la gestion des programmes **environ.

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

On dirait que Python 3.6 ici passe aveuglément le long des doublons (une abstraction qui fuit) alors que Perl 5.24 ne le fait pas. Et les coquilles?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

Mon Dieu, que se passe-t-il si sudone désinfecte que la première entrée d'environnement mais bashs'exécute ensuite avec la seconde? Bonjour PATHou LD_RUN_PATHexploiter. Votre sudo(et tout le reste ?) Est corrigé pour ce trou ? Les failles de sécurité ne sont ni "une différence anecdotique" ni juste "un bug" dans le programme appelant.

branler
la source
1
C'est vrai, mais une différence anecdotique et sans doute un bug du programme définissant la variable en double.
jlliagre
1
Voir rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas
0

Une variable d'environnement est comme une variable shell , mais elle n'est pas spécifique au shell . Tous les processus sur les systèmes Unix ont un stockage variable d'environnement . La principale différence entre les variables d'environnement et de shell est que le système d' exploitation transmet toutes les variables d'environnement de votre shell aux programmes que le shell exécute, tandis que les variables de shell ne sont pas accessibles dans les commandes que vous exécutez.

env –La commande vous permet d'exécuter un autre programme dans un environnement personnalisé sans modifier celui en cours. Lorsqu'il est utilisé sans argument, il affichera une liste des variables d'environnement actuelles. printenv –La commande imprime toutes ou les variables d'environnement spécifiées. set –La commande définit ou supprime les variables shell. Lorsqu'il est utilisé sans argument, il affichera une liste de toutes les variables, y compris les variables d'environnement et de shell, et les fonctions de shell. unset –La commande supprime le shell et les variables d'environnement. export –La commande définit les variables d'environnement

Mehdi sellami
la source