Por que $ RANDOM não está incluído na saída de 'env'?

23

Eu sei que envé um comando shell, ele pode ser usado para imprimir uma lista das variáveis ​​de ambiente atuais. E, tanto quanto eu entendo, RANDOMtambém é uma variável de ambiente.

Então, por que, quando inicio envno Linux, a saída não inclui RANDOM?

mcmxciv
fonte
4
envnão é um comando do shell, pois geralmente não é incorporado ao shell.
schily
@schily BTW para Bash, declare -xé o equivalente em um shell embutido.
Wjandrea

Respostas:

42

RANDOMnão é uma variável de ambiente. É uma variável de shell mantida por algumas conchas. Geralmente não é exportado por padrão. É por isso que ele não aparece na saída de env.

Uma vez que foi usado pelo menos uma vez, ele iria aparecer na saída set, o que, por si só, lista as variáveis do shell (e funções) e seus valores na sessão shell atual. Esse comportamento é dependente do shell e usa pdkshno OpenBSD,RANDOM seria listado setmesmo se não fosse usado anteriormente.


O restante desta resposta diz respeito ao que se poderia esperar se RANDOM fosse exportado (ou seja, transformado em uma variável de ambiente).

Exportando-o com export RANDOM tornaria uma variável de ambiente, mas seu uso seria severamente limitado, pois seu valor em um processo filho seria "aleatório, mas estático" (o que significa que seria um número aleatório imutável). O comportamento exato difere entre as conchas.

Estou usando o pdkshOpenBSD no exemplo abaixo e recebo um novo valor aleatório em cada awkexecução (mas o mesmo valor todas as vezes na mesma awkinstância). Usando bash, eu obteria exatamente o mesmo valor aleatório em todas as invocações deawk .

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

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

Em bash, o valor exportado de RANDOMpermaneceria estático, independentemente do uso deRANDOM no shell (onde cada uso de $RANDOMainda daria um novo valor).

Isso ocorre porque cada referência à variável shell RANDOM em bashtorna o acesso shell a sua interno get_random()função para dar a variável um novo valor aleatório, mas o shell não atualiza a variável de ambiente RANDOM . Este é um comportamento semelhante como com outros dinâmicas bashvariáveis, tais como LINENO, SECONDS, BASHPIDetc.

Para atualizar a variável de ambiente RANDOMem bash, você teria que atribuir-lhe o valor da variável de shell RANDOM e re-exportá-lo:

export RANDOM="$RANDOM"

Não está claro para mim se isso teria o efeito colateral adicional de re-semear o gerador de números aleatórios bashou não (mas um palpite fundamentado seria que não existe).

Kusalananda
fonte
1
Tem RANDOMalgum valor antes de usá-lo? Eu sempre assumi que ele só era preenchido quando chamado.
terdon
1
Não é, o manual do bash menciona isso.
terdon
1
Embora se você mesmo export RANDOMou declare -p RANDOM, ao que parece, por isso não tenho certeza se é qualquer uso que não existe antes de ser referenciada ...
ilkkachu
1
"Seu valor em um processo filho seria aleatório, mas estático." Se é estático, não é aleatório , sejam três bytes ou dezesseis.
L0b0 5/07
3
@ l0b0 Seria aleatório no sentido de que você não seria capaz de prever. Obviamente, uma vez que você o leia, não será mais aleatório, pois não será alterado (a menos que seja reexportado como mostrei, nesse caso a variável de ambiente obteria um novo valor aleatório). Por isso eu disse que é aleatório, mas estático. Eu esclareci isso no texto um pouco agora.
Kusalananda
16

Nem todas as variáveis ​​definidas em sua sessão do shell são variáveis ​​de ambiente. "Variáveis ​​de ambiente" refere-se apenas às variáveis ​​que foram exportadas para o ambiente usando o exportbuiltin. O envcomando imprime apenas essas variáveis ​​de ambiente . Por exemplo:

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

Se você deseja ver todas as variáveis ​​definidas em sua sessão, independentemente de terem sido exportadas, você pode usar set:

$ set | grep foo=
foo=bar

O setbuiltin também retorna funções, portanto, para ver apenas variáveis, você pode usar:

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

Por fim, a RANDOMvariável é especial, pois somente recebe um valor quando você a referencia. Isso é mencionado no bash (1) :

RANDOM

    Cada vez que esse parâmetro é referenciado, um número inteiro aleatório entre 0 e 32767 é gerado. A sequência de números aleatórios pode ser inicializada atribuindo um valor a RANDOM. Se RANDOMnão estiver definido, ele perde suas propriedades especiais, mesmo que seja redefinido posteriormente.

Portanto, mesmo que fosse uma variável de ambiente como você pensava, ela não teria sido mostrada, envpois não seria configurada até a primeira vez que você a chamou. É também por isso que não é mostrado em 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
fonte
Essa é uma descoberta interessante set | grep RAN. Eu não esperava isso. FWIW, acredito que não possa ser previsto pela documentação.
G-Man diz 'Reinstate Monica'
1
PS Parabéns por atingir 120.000. (Acho que acabei de colocar você no lugar.)
G-Man diz 'Reinstate Monica'
4

A maioria dos shells terá várias outras variáveis ​​definidas ou usadas pelo shell que não são exportadas para processos filho por padrão.

No Bash, existem alguns obviamente específicos do 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

Depois, existem outros padrões como OPTINDand OPTERR(used by getopts), and PS2, PS3(the prompts secundários) e até outra variável "mágica": SECONDS(mostra o tempo em segundos desde que o shell foi iniciado)

No Bash, você pode ver todas as variáveis ​​e seus status de exportação com declare -p. Os marcados com -xsão exportados, os que não xsão. (Alguns terão outros sinalizadores como ipara inteiro ou rsomente leitura.)

No Zsh ou no ksh93, você pode usar typeset -p, embora o Zsh marque as variáveis ​​exportadas alterando typesetpara exportna saída, em vez de usar sinalizadores. exportpor si só também mostraria todas as variáveis ​​exportadas, mas esse é o mesmo resultado que você obtém executando env.

ilkkachu
fonte
2

Se você pesquisar no Google, os documentos declaram o seguinte:

$RANDOMé uma função Bash interna (não uma constante) que retorna um número inteiro pseudo-aleatório [1] no intervalo de 0 a 32767. Não deve ser usada para gerar uma chave de criptografia.

Se você usar, stracepoderá ver que a $RANDOM"variável" é passada diretamente para os comandos como se fosse uma variável comum do shell ou uma variável de ambiente, mas é apenas uma função interna incorporada ao shell, o Bash, que está fazendo a expansão.

$ 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. esta variável regular:

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

A variável não está sendo transmitida como referência.

Referências

slm
fonte
1
bem, isso não passa o valor expandido de $RANDOMou $SOMEVARatravés de um argumento de linha de comando, e não como uma variável de ambiente? Você precisaria de exportambos para passá-los pelo ambiente.
Ilkkachu
Não, isso não faria diferença. O shell os expande independentemente. A maneira como mostrei é basicamente destacando o fato de que o shell está fazendo a expansão.
Slm
2
A stracesaída não parece capturar a função interna executada pelo shell. Nos dois casos, a variável já foi expandida na primeira linha do strace. Não entendo que diferença você está apontando. o que estou perdendo?
terdon
Mostrando que a $RANDOMexpansão é feita internamente no shell. É basicamente a confirmação de que o shell está determinando o valor e não passando uma referência a uma variável. O shell quando está expandindo a linha de comando para executar analisa $RANDOMe passa o formulário expandido para echo.
Slm
2
Então, nada como uma variável de ambiente , então.
precisa