Dlaczego $ RANDOM nie jest zawarty w wynikach „env”?

23

Wiem, że envto polecenie powłoki, może być użyte do wydrukowania listy aktualnych zmiennych środowiskowych. O ile rozumiem, RANDOMjest to również zmienna środowiskowa.

Dlaczego więc, kiedy uruchamiam envw systemie Linux, dane wyjściowe nie obejmują RANDOM?

mcmxciv
źródło
4
envnie jest poleceniem powłoki, ponieważ zwykle nie jest wbudowane w powłokę.
schily
@schily BTW dla Bash, declare -xjest odpowiednikiem wbudowanej powłoki.
wjandrea,

Odpowiedzi:

42

RANDOMnie jest zmienną środowiskową. Jest to zmienna powłoki obsługiwana przez niektóre powłoki. Zwykle nie jest eksportowany domyślnie. Dlatego nie pojawia się na wyjściu env.

Raz to było wykorzystywane co najmniej raz, by pokazać się na wyjściu set, który sam w sobie, listy zmienne powłoki (i funkcje) i ich wartości w bieżącej sesji powłoki. To zachowanie zależy od powłoki i użycia pdkshna OpenBSD, RANDOMbyłoby wymienione na liście, setnawet jeśli nie było wcześniej używane.


Pozostała część odpowiedzi dotyczy tego, czego można się było spodziewać, gdyby RANDOMzostał wyeksportowany (tj. Przekształcony w zmienną środowiskową).

Wyeksportowanie go export RANDOMspowoduje, że będzie zmienną środowiskową, ale jego użycie byłoby poważnie ograniczone, ponieważ jego wartość w procesie potomnym byłaby „losowa, ale statyczna” (co oznacza, że ​​byłaby niezmienną liczbą losową). Dokładne zachowanie różni się w zależności od powłoki.

Korzystam pdkshz OpenBSD w poniższym przykładzie i otrzymuję nową losową wartość przy każdym awkuruchomieniu (ale tę samą wartość za każdym razem w tej samej awkinstancji). Używając bash, uzyskałbym dokładnie tę samą losową wartość we wszystkich wywołaniach awk.

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

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

W bash, wyeksportowana wartość RANDOMpozostałaby statyczna bez względu na użycie RANDOMw powłoce (gdzie każde użycie $RANDOMwciąż dawałoby nową wartość).

To dlatego, że każde odwołanie do zmiennej powłoki RANDOM w bashsprawia, że dostęp do powłoki jej wewnętrzna get_random()funkcja dać zmiennej nową wartość losową, ale powłoka nie aktualizuje zmienną środowiskową RANDOM . Jest to podobne do zachowania jak w przypadku innych dynamicznych bashzmiennych, takich jak LINENO, SECONDS, BASHPIDitp

Aby zaktualizować zmienną środowiskową RANDOMw bash, trzeba by przypisać mu wartość zmiennej powłoki RANDOM i reeksportu go:

export RANDOM="$RANDOM"

Nie jest dla mnie jasne, czy miałoby to dodatkowy efekt uboczny ponownego uruchomienia generatora liczb losowych, bashczy też nie (ale zgadywano by, że tak nie jest).

Kusalananda
źródło
1
Czy w RANDOMogóle ma jakąś wartość przed użyciem? Zawsze zakładałem, że jest wypełniony tylko na wezwanie.
terdon
1
Nie jest tak, wspomina o tym instrukcja bash.
terdon
1
Choć jeśli nawet, export RANDOMlub declare -p RANDOMwydaje się, więc nie jestem pewien, czy to pożytek, że nie istnieje przed odniesieniem ...
ilkkachu
1
„Jego wartość w procesie potomnym byłaby losowa, ale statyczna”. Jeśli jest statyczny, nie jest losowy , niezależnie od tego, czy ma trzy bajty, czy szesnaście.
l0b0
3
@ l0b0 Byłoby losowe w tym sensie, że nie byłbyś w stanie tego przewidzieć. Oczywiście, po przeczytaniu, nie jest już losowy, ponieważ nie zmieni się (chyba że pokazałem ponowny eksport, jak pokazałem, w którym to przypadku zmienna środowiskowa otrzymałaby nową losową wartość). Dlatego powiedziałem, że jest losowy, ale statyczny. Wyjaśniłem to nieco w tekście.
Kusalananda
16

Nie wszystkie zmienne ustawione w sesji powłoki są zmiennymi środowiskowymi. „Zmienne środowiskowe” odnoszą się tylko do tych zmiennych, które zostały wyeksportowane do środowiska przy użyciu exportwbudowanego. envKomenda drukuje tylko takie środowisko zmienne. Na przykład:

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

Jeśli chcesz zobaczyć wszystkie zmienne ustawione w sesji, niezależnie od tego, czy zostały wyeksportowane, możesz użyć set:

$ set | grep foo=
foo=bar

setWbudowane zwraca również funkcje, tak aby widzieć tylko zmienne, można użyć:

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

Wreszcie, RANDOMzmienna jest wyjątkowa, ponieważ przypisuje się jej wartość tylko wtedy, gdy się do niej odwołujesz. Zostało to wspomniane w bash (1) :

RANDOM

    Za każdym razem, gdy odwołuje się do tego parametru, generowana jest losowa liczba całkowita od 0 do 32767. Sekwencję liczb losowych można zainicjować, przypisując wartość do RANDOM. Jeśli RANDOMjest rozbrojony, traci swoje specjalne właściwości, nawet jeśli jest następnie resetowany.

Więc nawet gdyby była to zmienna środowiskowa, jak się spodziewałeś, nie zostałaby pokazana, envponieważ nie zostałaby ustawiona do pierwszego wywołania. Dlatego też nie jest pokazany w 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
źródło
To interesujące odkrycie set | grep RAN. Nie spodziewałbym się tego. FWIW, uważam, że nie można tego przewidzieć w dokumentacji.
G-Man mówi „Przywróć Monikę”
1
PS Gratulujemy osiągnięcia 120 000 osób. (Chyba właśnie cię położyłem.)
G-Man mówi „Przywróć Monikę”
4

Większość powłok ma ustawione lub używane przez powłokę wiele innych zmiennych, które domyślnie nie są eksportowane do procesów potomnych.

W Bash istnieją pewne, oczywiście, specyficzne dla 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

Potem jest bardziej standardowych jak OPTINDi OPTERR(używany przez getopts) i PS2, PS3(wtórne monity), a nawet inny „magia” zmienna: SECONDS(pokazuje czas w sekundach od uruchomienia powłoki)

W Bash możesz zobaczyć wszystkie zmienne i ich status eksportu za pomocą declare -p. Te oznaczone -xsą eksportowane, a te bez x. (Niektóre będą miały inne flagi, takie jak idla liczb całkowitych lub tylko rdo odczytu).

W zsh lub ksh93 można użyć typeset -p, choć wskazuje na zsh wyeksportowanych zmiennych zmieniając typesetsię exportw produkcji, zamiast używania flagi. exportsam w sobie pokazywałby również wszystkie wyeksportowane zmienne, ale jest to mniej więcej taki sam wynik, jaki można uzyskać, uruchamiając env.

ilkkachu
źródło
2

Jeśli korzystasz z wyszukiwarki Google, dokumenty zawierają następujące informacje:

$RANDOMto wewnętrzna funkcja Bash (nie stała), która zwraca liczbę całkowitą pseudolosową [1] z zakresu 0 - 32767. Nie należy jej używać do generowania klucza szyfrowania.

Jeśli go użyjesz strace, zobaczysz, że $RANDOM„zmienna” jest przekazywana bezpośrednio do poleceń, tak jakby to była zwykła zmienna powłoki lub zmienna środowiskowa, ale to tylko funkcja wewnętrzna wbudowana w powłokę, Bash, która dokonuje rozszerzenia.

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

vs. ta zmienna regularna:

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

Zmienna nie jest przekazywana jako odniesienie.

Referencje

slm
źródło
1
cóż, czy to nie jest przekazywanie rozszerzonej wartości $RANDOMlub $SOMEVARprzez argument wiersza poleceń, a nie jako zmienna środowiskowa? Musisz exportoba, aby przejść przez środowisko.
ilkkachu
Nie, to nie miałoby znaczenia. Powłoka rozszerza je niezależnie. Sposób, w jaki to pokazałem, w zasadzie podkreśla fakt, że powłoka dokonuje rozszerzenia.
slm
2
Wydaje się, że stracedane wyjściowe nie wychwytują funkcji wewnętrznej uruchamianej przez powłokę. W obu przypadkach zmienna została już rozwinięta w pierwszym wierszu strace. Nie rozumiem, na którą różnicę wskazujesz. czego mi brakuje?
terdon
Pokazuje, że $RANDOMrozszerzenie jest wykonywane wewnętrznie w powłoce. Jest to w zasadzie potwierdzenie, że powłoka określa wartość, a nie przekazanie odwołania do zmiennej. Powłoka, gdy rozwija linię poleceń w celu wykonania parsowania $RANDOMi przekazuje rozwiniętą formę do echo.
slm
2
Zatem nic nie przypomina zmiennej środowiskowej .
Toby Speight,