Pourquoi $ RANDOM n'est-il pas inclus dans la sortie de 'env'?

23

Je sais que envc'est une commande shell, elle peut être utilisée pour imprimer une liste des variables d'environnement actuelles. Et pour autant que je comprends, RANDOMest également une variable d'environnement.

Alors pourquoi, lorsque je lance envsur Linux, la sortie ne comprend-elle pas RANDOM?

mcmxciv
la source
4
envn'est pas une commande shell car elle n'est généralement pas intégrée au shell.
schily
@schily BTW pour Bash, declare -xest l'équivalent dans un shell intégré.
wjandrea

Réponses:

42

RANDOMn'est pas une variable d'environnement. C'est une variable shell maintenue par certains shells. Il n'est généralement pas exporté par défaut. C'est pourquoi il n'apparaît pas dans la sortie de env.

Une fois qu'il a été utilisé au moins une fois, il serait affiché dans la sortie set, qui, par lui - même, énumère les variables shell (et fonctions) et leurs valeurs dans la session shell en cours. Ce comportement dépend du shell et l'utilisation pdkshd'OpenBSD RANDOMserait répertorié setmême s'il n'était pas utilisé auparavant.


Le reste de cette réponse concerne ce qui pourrait se produire s'il RANDOMétait exporté (c'est-à-dire transformé en variable d'environnement).

L'exporter avec en export RANDOMferait une variable d'environnement mais son utilisation serait sévèrement limitée car sa valeur dans un processus enfant serait "aléatoire mais statique" (ce qui signifie que ce serait un nombre aléatoire immuable). Le comportement exact diffère entre les coques.

J'utilise pdkshsur OpenBSD dans l'exemple ci-dessous et j'obtiens une nouvelle valeur aléatoire à chaque awkexécution (mais la même valeur à chaque fois dans la même awkinstance). En utilisant bash, j'obtiendrais exactement la même valeur aléatoire dans toutes les invocations de awk.

$ awk 'BEGIN { print ENVIRON["RANDOM"], ENVIRON["RANDOM"] }'
25444 25444

$ awk 'BEGIN { print ENVIRON["RANDOM"], ENVIRON["RANDOM"] }'
30906 30906

Dans bash, la valeur exportée de RANDOMresterait statique quelle que soit l'utilisation de RANDOMdans le shell (où chaque utilisation de $RANDOMdonnerait toujours une nouvelle valeur).

En effet, chaque référence à la variable shell RANDOM dans bashle shell accède à sa get_random()fonction interne pour donner à la variable une nouvelle valeur aléatoire, mais le shell ne met pas à jour la variable d'environnement RANDOM . Ceci est similaire dans le comportement comme avec d' autres dynamiques bashvariables, telles que LINENO, SECONDS, BASHPIDetc.

Pour mettre à jour la variable d'environnement RANDOMdans bash, vous devez lui affecter la valeur de la variable shell RANDOM et la réexporter:

export RANDOM="$RANDOM"

Il n'est pas clair pour moi si cela aurait l'effet secondaire supplémentaire de réensemencer le générateur de nombres aléatoires bashou non (mais une supposition éclairée serait que ce n'est pas le cas).

Kusalananda
la source
1
A-t RANDOM- il même une valeur avant de l'utiliser? J'avais toujours supposé qu'il n'était peuplé qu'au moment de l'appel.
terdon
1
Ce n'est pas le cas, le manuel bash le mentionne.
terdon
1
Bien que si vous faites même export RANDOMou declare -p RANDOM, il apparaît, donc je ne suis pas sûr si c'est utile qu'il n'existe pas avant d'être référencé ...
ilkkachu
1
"Sa valeur dans un processus enfant serait aléatoire, mais statique." S'il est statique, il n'est pas aléatoire , qu'il s'agisse de trois octets ou de seize.
l0b0
3
@ l0b0 Ce serait aléatoire dans le sens où vous ne pourriez pas le prédire. De toute évidence, une fois que vous l'avez lu, il n'est plus aléatoire car il ne changera pas (à moins de réexporter comme je l'ai montré, auquel cas la variable d'environnement obtiendrait une nouvelle valeur aléatoire). C'est pourquoi j'ai dit que c'était aléatoire mais statique. J'ai clarifié cela un peu dans le texte maintenant.
Kusalananda
16

Toutes les variables définies dans votre session shell ne sont pas des variables d'environnement. "Variables d'environnement" se réfère uniquement aux variables qui ont été exportées vers l'environnement à l'aide de la fonction exportintégrée. La envcommande imprime uniquement ces variables d' environnement . Par exemple:

$ foo="bar"
$ env | grep foo ## returns nothing
$ export foo
$ env | grep foo ## now, env will print it
foo=bar

Si vous souhaitez voir toutes les variables définies dans votre session, qu'elles aient été exportées ou non, vous pouvez utiliser set:

$ set | grep foo=
foo=bar

La fonction setintégrée renvoie également des fonctions, donc pour voir uniquement les variables, vous pouvez utiliser:

set | grep  '^[^[:space:]]*='

Enfin, la RANDOMvariable a la particularité de ne recevoir une valeur que lorsque vous la référencez. Ceci est mentionné dans bash (1) :

RANDOM

    Chaque fois que ce paramètre est référencé, un entier aléatoire entre 0 et 32767 est généré. La séquence de nombres aléatoires peut être initialisée en attribuant une valeur à RANDOM. S'il RANDOMn'est pas défini, il perd ses propriétés spéciales, même s'il est ensuite réinitialisé.

Donc, même s'il s'agissait d'une variable d'environnement comme vous le pensiez, elle n'aurait pas été affichée envcar elle ne serait définie que la première fois que vous l'avez appelée. C'est aussi pourquoi il n'apparaît pas dans set:

$ set | grep RAN   ## returns nothing, RANDOM is unset
$ echo "$RANDOM"   ## this will assign a value to RANDOM
1234
$ set | grep RAN   ## so now it will also appear in the output of set 
RANDOM=1234
terdon
la source
C'est une découverte intéressante, concernant set | grep RAN. Je ne m'y attendais pas. FWIW, je crois que cela ne peut pas être prédit par la documentation.
G-Man dit `` Réintègre Monica '' le
1
PS Félicitations pour avoir atteint 120 000 personnes. (Je suppose que je viens de vous remettre.)
G-Man dit 'Reinstate Monica'
4

La plupart des shells auront un certain nombre d'autres variables définies ou utilisées par le shell qui ne sont pas exportées par défaut vers les processus enfants.

Dans Bash, il y en a de toute évidence spécifiques à Bash:

$ echo "${!BASH*}"
BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
$ echo $BASH_VERSION
4.4.12(1)-release
$ env|grep -c BASH
0

Ensuite, il y a plus de standards comme OPTINDet OPTERR(utilisé par getopts), et PS2, PS3(les invites secondaires) et même une autre variable "magique": SECONDS(affiche le temps en secondes depuis le démarrage du shell)

Dans Bash, vous pouvez voir toutes les variables et leur statut d'exportation avec declare -p. Ceux marqués d'un -xsont exportés, ceux qui ne le xsont pas. (Certains auront d'autres indicateurs comme ipour entier ou ren lecture seule.)

Dans Zsh ou ksh93, vous pouvez utiliser typeset -p, bien que Zsh marque les variables exportées en changeant typeseten exportdans la sortie, au lieu d'utiliser des indicateurs. exportà lui seul afficherait également toutes les variables exportées, mais c'est à peu près le même résultat que vous obtenez en exécutant env.

ilkkachu
la source
2

Si vous utilisez Google pour cela, les documents indiquent ce qui suit:

$RANDOMest une fonction Bash interne (pas une constante) qui renvoie un entier pseudo-aléatoire [1] compris entre 0 et 32 ​​767. Elle ne doit pas être utilisée pour générer une clé de chiffrement.

Si vous utilisez, stracevous pouvez voir que la $RANDOM"variable" est transmise directement aux commandes comme s'il s'agissait d'une variable shell ordinaire ou d'une variable d'environnement, mais c'est juste une fonction interne qui est intégrée dans le shell, Bash, qui fait l'expansion.

$ strace -t echo "random value: $RANDOM"
04:37:58 execve("/bin/echo", ["echo", "random value: 30795"], [/* 27 vars */]) = 0
04:37:58 brk(NULL)                      = 0x19c1000
04:37:58 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9841351000
...

par rapport à cette variable régulière:

$ strace -t echo "random value: $SOMEVAR"
04:40:19 execve("/bin/echo", ["echo", "random value: helloworld"], [/* 27 vars */]) = 0
04:40:19 brk(NULL)                      = 0x154b000
04:40:19 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f659d2eb000
...

La variable n'est pas transmise comme référence.

Les références

slm
la source
1
eh bien, n'est-ce pas en passant la valeur développée de $RANDOMou $SOMEVARvia un argument de ligne de commande, et non pas comme une variable d'environnement? Vous auriez besoin des exportdeux pour les faire passer dans l'environnement.
ilkkachu
Non, cela ne ferait aucune différence. Le shell les agrandit malgré tout. La façon dont je l'ai montré met essentiellement en évidence le fait que le shell fait l'expansion.
slm
2
La stracesortie ne semble pas intercepter la fonction interne exécutée par le shell. Dans les deux cas, la variable a déjà été développée dans la première ligne du strace. Je ne comprends pas quelle différence vous signalez. Qu'est-ce que je rate?
terdon
Montrant que l' $RANDOMexpansion se fait en interne au shell. C'est essentiellement la confirmation que le shell détermine la valeur et ne transmet pas de référence à une variable. Le shell lorsqu'il étend la ligne de commande pour exécuter des analyses $RANDOMet passe le formulaire développé à echo.
slm
2
Donc, rien de tel qu'une variable d' environnement , alors.
Toby Speight