Warum ist $ RANDOM nicht in der Ausgabe von 'env' enthalten?

23

Ich weiß env, dass es sich um einen Shell-Befehl handelt, mit dem eine Liste der aktuellen Umgebungsvariablen gedruckt werden kann. Und soweit ich das verstehe, RANDOMhandelt es sich auch um eine Umgebungsvariable.

Warum ist envdie Ausgabe bei meinem Start unter Linux nicht enthalten RANDOM?

mcmxciv
quelle
4
envist kein Shell-Befehl, da er normalerweise nicht in die Shell integriert ist.
Schily
@schily BTW für Bash, declare -xist das Äquivalent in einer Shell eingebaut.
wjandrea

Antworten:

42

RANDOMist keine Umgebungsvariable. Es ist eine Shell-Variable, die von einigen Shells verwaltet wird. Es wird in der Regel nicht standardmäßig exportiert. Aus diesem Grund wird es in der Ausgabe von nicht angezeigt env.

Sobald es einmal zumindest verwendet wird worden, es würde in der Ausgabe erscheinen set, die, für sich allein, führt die Shell - Variablen (und Funktionen) und deren Werte in der aktuellen Shell - Sitzung. Dieses Verhalten ist abhängig von der Schale und mit pdkshauf OpenBSD, RANDOMwürde von der Liste aufgeführt werden , setauch wenn nicht zuvor verwendet hat .


Der Rest dieser Antwort bezieht sich darauf, was zu erwarten ist, wenn RANDOMes exportiert (dh in eine Umgebungsvariable umgewandelt) wurde.

Wenn Sie es mit exportieren export RANDOM, wird es zu einer Umgebungsvariablen, die Verwendung ist jedoch stark eingeschränkt, da der Wert in einem untergeordneten Prozess "zufällig, aber statisch" ist (dh, es handelt sich um eine unveränderliche Zufallszahl). Das genaue Verhalten unterscheidet sich zwischen den Schalen.

Ich verwende pdkshOpenBSD im folgenden Beispiel und erhalte bei jedem awkLauf einen neuen Zufallswert (aber jedes Mal den gleichen Wert innerhalb derselben awkInstanz). Mit bashwürde ich in allen Aufrufen von genau den gleichen Zufallswert erhalten awk.

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

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

In würde bashder exportierte Wert von RANDOMstatisch bleiben, unabhängig von der Verwendung RANDOMin der Shell (wobei jede Verwendung von $RANDOMimmer noch einen neuen Wert ergeben würde).

Dies liegt daran, dass bei jedem Verweis auf die Shell-Variable RANDOM in bashdie Shell auf ihre interne get_random()Funktion zugegriffen wird, um der Variablen einen neuen Zufallswert zu geben, die Shell jedoch die Umgebungsvariable nicht aktualisiert RANDOM. Dies ist ähnlich im Verhalten wie bei anderen dynamischen bashVariablen, wie LINENO, SECONDS, BASHPIDusw.

Um die Umgebungsvariable RANDOMin zu aktualisieren bash, müssten Sie ihr den Wert der Shell-Variablen zuweisen RANDOM und sie erneut exportieren:

export RANDOM="$RANDOM"

Es ist mir unklar, ob dies den zusätzlichen Nebeneffekt haben würde, den Zufallszahlengenerator wieder einzuspielen bashoder nicht (aber eine fundierte Vermutung wäre, dass dies nicht der Fall ist).

Kusalananda
quelle
1
Hat RANDOMnoch ein Wert bevor Sie es verwenden? Ich war immer davon ausgegangen, dass es nur bei einem Anruf besiedelt war.
Terdon
1
Das Bash-Handbuch erwähnt es nicht.
Terdon
1
Obwohl, wenn Sie gerade export RANDOModer tun declare -p RANDOM, scheint es, so bin ich nicht sicher, ob es irgendeine Verwendung ist, dass es nicht existiert, bevor verwiesen wird ...
ilkkachu
1
"Sein Wert in einem untergeordneten Prozess wäre zufällig, aber statisch." Wenn es statisch ist, ist es nicht zufällig , ob es drei Bytes oder sechzehn sind.
l0b0
3
@ l0b0 Es wäre zufällig in dem Sinne, dass Sie es nicht vorhersagen könnten. Wenn Sie es einmal gelesen haben, ist es natürlich nicht mehr zufällig, da es sich nicht ändert (es sei denn, Sie exportieren erneut, wie ich gezeigt habe. In diesem Fall würde die Umgebungsvariable einen neuen zufälligen Wert erhalten). Deshalb habe ich gesagt, es ist zufällig, aber statisch. Ich habe dies im Text jetzt etwas klarer formuliert.
Kusalananda
16

Nicht alle in Ihrer Shell-Sitzung festgelegten Variablen sind Umgebungsvariablen. "Umgebungsvariablen" bezieht sich nur auf die Variablen, die mit der exportintegrierten Funktion in die Umgebung exportiert wurden. Der envBefehl druckt nur solche Umgebungsvariablen . Beispielsweise:

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

Wenn Sie alle Variablen in Ihrer Sitzung anzeigen möchten, unabhängig davon, ob sie exportiert wurden, können Sie Folgendes verwenden set:

$ set | grep foo=
foo=bar

Das setBuiltin gibt auch Funktionen zurück. Um also nur Variablen anzuzeigen, können Sie Folgendes verwenden:

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

Schließlich ist die RANDOMVariable insofern besonders, als ihr nur dann ein Wert zugewiesen wird, wenn Sie darauf verweisen. Dies wird in bash (1) erwähnt :

RANDOM

    Bei jedem Verweis auf diesen Parameter wird eine zufällige Ganzzahl zwischen 0 und 32767 generiert. Die Folge von Zufallszahlen kann durch Zuweisen eines Wertes zu initialisiert werden RANDOM. Wenn RANDOMnicht gesetzt, verliert es seine besonderen Eigenschaften, auch wenn es anschließend zurückgesetzt wird.

Selbst wenn es eine Umgebungsvariable wäre, wie Sie dachten, würde sie nicht angezeigt werden, envda sie erst beim ersten Aufruf festgelegt wird. Das ist auch der Grund, warum es nicht gezeigt wird in 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
quelle
Das ist eine interessante Entdeckung set | grep RAN. Ich hätte es nicht erwartet. FWIW, ich glaube, dass die Dokumentation dies nicht vorhersagen kann.
G-Man sagt, dass Monica
1
PS Herzlichen Glückwunsch zum Erreichen von 120.000. (Ich nehme an, ich habe dich gerade überredet.)
G-Man sagt, 'Monica wieder einsetzen'
4

In den meisten Shells werden eine Reihe anderer Variablen festgelegt oder von der Shell verwendet, die standardmäßig nicht in untergeordnete Prozesse exportiert werden.

In Bash gibt es einige offensichtlich Bash-spezifische:

$ 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

Dann gibt es mehr Standard diejenigen wie OPTINDund OPTERR(verwendet von getopts) und PS2, PS3(die sekundären Prompts) und sogar eine andere „magische“ Variable: SECONDS(zeigt die Zeit in Sekunden , da die Shell gestartet)

In Bash sehen Sie alle Variablen und deren Exportstatus mit declare -p. Die mit gekennzeichneten -xwerden exportiert, die ohne xnicht. (Einige haben andere Flags, wie izum Beispiel für Ganzzahlen oder rfür Nur-Lese- Flags .)

In Zsh oder ksh93 können Sie verwenden typeset -p, obwohl Zsh die exportierten Variablen durch Ändern typesetin exportin der Ausgabe markiert , anstatt Flags zu verwenden. exportAlleine würde auch alle exportierten Variablen anzeigen, aber das ist ungefähr dasselbe Ergebnis, das Sie durch Ausführen erhalten env.

ilkkachu
quelle
2

Wenn Sie dafür googeln, geben die Dokumente Folgendes an:

$RANDOMist eine interne Bash- Funktion (keine Konstante), die eine pseudozufällige [1] Ganzzahl im Bereich von 0 bis 32767 zurückgibt. Sie sollte nicht zum Generieren eines Verschlüsselungsschlüssels verwendet werden.

Wenn Sie verwenden strace, können Sie sehen, dass die $RANDOM"Variable" direkt an Befehle übergeben wird, als wäre es eine gewöhnliche Shell-Variable oder eine Umgebungsvariable, aber es ist nur eine interne Funktion, die in die Shell eingebaut ist, Bash, die die Erweiterung vornimmt.

$ 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
...

gegen diese reguläre Variable:

$ 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
...

Die Variable wird nicht als Referenz übergeben.

Verweise

slm
quelle
1
Nun, übergibt das nicht den erweiterten Wert von $RANDOModer $SOMEVARüber ein Befehlszeilenargument und nicht als Umgebungsvariable? Sie müssten exportbeide durch die Umgebung leiten.
Ilkkachu
Nein, das würde keinen Unterschied machen. Die Shell erweitert sie trotzdem. Die Art und Weise, wie ich es gezeigt habe, hebt im Grunde die Tatsache hervor, dass die Shell die Erweiterung durchführt.
SLM
2
Die straceAusgabe scheint die von der Shell ausgeführte interne Funktion nicht zu erfassen. In beiden Fällen wurde die Variable bereits in der ersten Zeile der erweitert strace. Ich verstehe nicht, auf welchen Unterschied Sie hinweisen. Was vermisse ich?
Terdon
Zeigen, dass die $RANDOMErweiterung intern in der Shell erfolgt. Es ist im Grunde eine Bestätigung, dass die Shell den Wert bestimmt und keinen Verweis auf eine Variable übergibt. Die Shell, wenn sie die Befehlszeile zum Ausführen von Parses erweitert $RANDOMund das erweiterte Formular an übergibt echo.
SLM
2
Also nichts wie eine Umgebungsvariable .
Toby Speight