Почему $ RANDOM не включен в вывод 'env'?

23

Я знаю, envчто это команда оболочки, она может быть использована для печати списка текущих переменных среды. И, насколько я понимаю, RANDOMэто тоже переменная окружения.

Итак, почему, когда я запускаю envна Linux, выходные данные не включают RANDOM?

mcmxciv
источник
4
envне является командой оболочки, так как обычно она не встроена в оболочку.
Шили
@schily BTW для Bash, declare -xявляется эквивалентом встроенной оболочки.
wjandrea

Ответы:

42

RANDOMне является переменной среды Это переменная оболочки, поддерживаемая некоторыми оболочками. Обычно он не экспортируется по умолчанию. Вот почему он не отображается на выходе env.

Как только он будет использован хотя бы один раз, он будет отображаться в выходных данных set, которые сами по себе перечисляют переменные оболочки (и функции) и их значения в текущем сеансе оболочки. Это поведение зависит от оболочки и использования pdkshв OpenBSD, RANDOMбудет перечислено, setдаже если ранее не использовался.


Остальная часть этого ответа касается того, что могло бы произойти, если бы он RANDOMбыл экспортирован (то есть превращен в переменную окружения).

Экспорт с помощью export RANDOMэтого сделает переменную окружения, но его использование будет строго ограничено, поскольку его значение в дочернем процессе будет «случайным, но статическим» (то есть это будет неизменное случайное число). Точное поведение отличается между оболочками.

Я использую pdkshOpenBSD в приведенном ниже примере и получаю новое случайное значение при каждом awkзапуске (но одно и то же значение каждый раз в одном и том же awkэкземпляре). Используя bash, я бы получил абсолютно одинаковое случайное значение во всех вызовах awk.

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

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

В bash, экспортированное значение RANDOMбудет оставаться статическим независимо от использования RANDOMв оболочке (где каждое использование по- $RANDOMпрежнему дает новое значение).

Это происходит потому , что каждая ссылка на переменную оболочки RANDOM в bashделает доступ к оболочке его внутренняя get_random()функция , чтобы дать переменной новое случайное значение, но оболочка не обновляет переменную окружения RANDOM . По поведению это похоже на другие динамические bashпеременные, такие как LINENO, SECONDSи BASHPIDт. Д.

Чтобы обновить переменные среды RANDOMв bash, вы должны присвоить ему значение переменной оболочки RANDOM и реэкспорт:

export RANDOM="$RANDOM"

Мне неясно, будет ли это иметь дополнительный побочный эффект повторного заполнения генератора случайных чисел bashили нет (но образованное предположение будет, что это не так).

Кусалананда
источник
1
Есть ли RANDOMдаже значение, прежде чем использовать его? Я всегда предполагал, что это было только заполнено, когда названо.
Тердон
1
Это не так, руководство bash упоминает об этом.
Тердон
1
Хотя, если вы делаете даже export RANDOMили declare -p RANDOM, кажется, так что я не уверен, есть ли какая-то польза от того, что он не существует до того, как на него ссылаются ...
ilkkachu
1
«Его значение в дочернем процессе будет случайным, но статическим». Если он статический, он не случайный , будь то три байта или шестнадцать.
10
3
@ l0b0 Это было бы случайно в том смысле, что вы не смогли бы предсказать это. Очевидно, что после прочтения он больше не является случайным, поскольку он не изменится (если только не будет реэкспорт, как я показал, в этом случае переменная окружения получит новое случайное значение). Вот почему я сказал, что это случайно, но статично. Я разъяснил это в тексте несколько сейчас.
Кусалананда
16

Не все переменные, которые установлены в вашем сеансе оболочки, являются переменными среды. «Переменные среды» относятся только к тем переменным, которые были экспортированы в среду с помощью exportвстроенной функции. env печатает только такие переменные среды . Например:

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

Если вы хотите увидеть все переменные, установленные в вашем сеансе, независимо от того, были ли они экспортированы, вы можете использовать set :

$ set | grep foo=
foo=bar

setВстроенный также возвращает функцию, так , чтобы видеть только переменные, вы можете использовать:

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

Наконец, RANDOMпеременная является особенной в том смысле, что ей присваивается значение только при ссылке на нее Это упоминается в bash (1) :

RANDOM

    Каждый раз, когда на этот параметр ссылаются, генерируется случайное целое число от 0 до 32767. Последовательность случайных чисел может быть инициализирована путем присвоения значенияRANDOM . Если RANDOMне установлено, оно теряет свои специальные свойства, даже если впоследствии оно сбрасывается.

Таким образом, даже если бы это была переменная окружения, как вы думали, она бы не отображалась, envпоскольку не будет установлена ​​до тех пор, пока вы не вызовете ее в первый раз. Это также, почему это не показано в 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
Тердон
источник
Это интересное открытие set | grep RAN. Я бы не ожидал этого. FWIW, я считаю, что это не может быть предсказано документацией.
G-Man говорит: «Восстановите Монику»
1
PS Поздравляю с достижением 120000. (Полагаю, я просто поставил тебя на место.)
G-Man говорит «Восстановить Монику»
4

У большинства оболочек есть ряд других переменных, установленных или используемых оболочкой, которые по умолчанию не экспортируются в дочерние процессы.

В Bash есть некоторые, очевидно, специфичные для 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

Тогда есть более стандартные, такие как OPTINDand OPTERR(используется getopts), and PS2, PS3(вторичные запросы) и даже другая «магическая» переменная:SECONDS (показывает время в секундах с момента запуска оболочки)

В Bash вы можете видеть все переменные и их статус экспорта с помощью declare -p. Те , отмеченные -xэкспортируются, те , без xне. (У некоторых будут другие флаги, например, iдля целого числа или только rдля чтения.)

В Zsh или ksh93 вы можете использовать typeset -p, хотя Zsh помечает экспортируемые переменные, заменяя typesetих exportна выходные, вместо использования флагов. exportсам по себе также будет отображать все экспортированные переменные, но это примерно тот же результат, который вы получите при запуске env.

ilkkachu
источник
2

Если вы гуглите по этому поводу, документы сообщат следующее:

$RANDOMявляется внутренней функцией Bash (не константой), которая возвращает псевдослучайное целое число [1] в диапазоне от 0 до 32767. Она не должна использоваться для генерации ключа шифрования.

Если вы используете, straceвы можете видеть, что $RANDOM«переменная» передается непосредственно командам, как если бы это была обычная переменная оболочки или переменная окружения, но это просто внутренняя функция, встроенная в оболочку, Bash, которая выполняет расширение.

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

против этой регулярной переменной:

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

Переменная не передается в качестве ссылки.

Ссылки

SLM
источник
1
ну, разве это не передача расширенного значения $RANDOMили $SOMEVARчерез аргумент командной строки, а не как переменная окружения? Вы должны exportоба, чтобы передать их через окружающую среду.
ilkkachu
Нет, это не имеет значения. Оболочка расширяет их независимо. То, как я это показал, в основном подчеркивает тот факт, что оболочка выполняет расширение.
SLM
2
straceВыход , кажется, не поймать внутреннюю функцию запуска оболочки. В обоих случаях переменная уже раскрыта в первой строке strace. Я не понимаю, на какую разницу вы указываете. Чего мне не хватает?
Тердон
Показывает, что $RANDOMрасширение выполняется внутри оболочки. По сути, это подтверждение того, что оболочка определяет значение, а не передает ссылку на переменную. Оболочка при расширении командной строки для выполнения анализа $RANDOMи передачи расширенной формы echo.
SLM
2
Итак, ничего подобного переменной окружения .
Тоби Спейт