22

I know env is a shell command, it can be used to print a list of the current environment variables. And as far as I understand, RANDOM is also a environment variable.

So why, when I launch env on Linux, does the output not include RANDOM?

  • 4
    env is not a shell command since it is usually not built into the shell. – schily Jul 5 '18 at 9:24
  • @schily BTW for Bash, declare -x is the equivalent in a shell builtin. – wjandrea Jul 5 '18 at 17:43
42

RANDOM is not an environment variable. It's a shell variable maintained by some shells. It is generally not exported by default. This is why it doesn't show up in the output of env.

Once it's been used at least once, it would show up in the output of set, which, by itself, lists the shell variables (and functions) and their values in the current shell session. This behaviour is dependent on the shell and using pdksh on OpenBSD, RANDOM would be listed by set even if not previously used.


The rest of this answer concerns what could be expected to happen if RANDOM was exported (i.e. turned into an environment variable).

Exporting it with export RANDOM would make it an environment variable but its use would be severely limited as its value in a child process would be "random but static" (meaning it would be an unchanging random number). The exact behaviour differs between shells.

I'm using pdksh on OpenBSD in the example below and I get a new random value in each awk run (but the same value every time within the same awk instance). Using bash, I would get exactly the same random value in all invocations of awk.

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

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

In bash, the exported value of RANDOM would remain static regardless of the use of RANDOM in the shell (where each use of $RANDOM would still give a new value).

This is because each reference to the shell variable RANDOM in bash makes the shell access its internal get_random() function to give the variable a new random value, but the shell does not update the environment variable RANDOM. This is similar in behaviour as with other dynamic bash variables, such as LINENO, SECONDS, BASHPID etc.

To update the environment variable RANDOM in bash, you would have to assign it the value of the shell variable RANDOM and re-export it:

export RANDOM="$RANDOM"

It is unclear to me if this would have the additional side effect of re-seeding the random number generator in bash or not (but an educated guess would be that it doesn't).

  • 1
    Does RANDOM even have a value before you use it? I had always assumed it was only populated when called. – terdon Jul 5 '18 at 8:33
  • 1
    It isn't, the bash manual mentions it. – terdon Jul 5 '18 at 8:45
  • 1
    Though if you do even export RANDOM or declare -p RANDOM, it appears, so I'm not sure if it's any use that it doesn't exist before being referenced... – ilkkachu Jul 5 '18 at 8:56
  • 1
    "Its value in a child process would be random, but static." If it is static, it is not random, whether it's three bytes or sixteen. – l0b0 Jul 5 '18 at 20:53
  • 3
    @l0b0 It would be random in the sense that you wouldn't be able to predict it. Obviously, once you've read it, it's not random any longer since it won't change (unless re-exporting as I showed, in which case the environment variable would get a new random value). This is why I said it's random but static. I've clarified this in the text somewhat now. – Kusalananda Jul 5 '18 at 21:00
16

Not all variables that are set in your shell session are environment variables. "Environment variables" refers only to those variables that have been exported to the environment using the export builtin. The env command only prints such environment variables. For example:

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

If you want to see all variables set in your session, irrespective of whether they have been exported, you can use set:

$ set | grep foo=
foo=bar

The set builtin also returns functions, so to see variables only, you can use:

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

Finally, the RANDOM variable is special in that it is only assigned a value when you reference it. This is mentioned in bash(1):

RANDOM

    Each time this parameter is referenced, a random integer between 0 and 32767 is generated.  The sequence of random numbers may be initialized by assigning a value to RANDOM.  If RANDOM is unset, it loses its special properties, even if it is subsequently reset.

So even if it were an environment variable as you thought, it wouldn't have been shown in env since it wouldn't be set until the first time you called it. That is also why it isn't shown 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
  • That’s an interesting discovery, regarding set | grep RAN.  I wouldn’t have expected it.  FWIW, I believe that it can’t be predicted by the documentation. – G-Man Jul 6 '18 at 4:18
  • 1
    P.S. Congratulations on reaching 120,000. (I guess I just put you over.) – G-Man Jul 6 '18 at 4:22
4

Most shells will have a number of other variables set or used by the shell that aren't exported to child processes by default.

In Bash, there are some obviously Bash-specific ones:

$ 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

Then there's more standard ones like OPTIND and OPTERR (used by getopts), and PS2, PS3 (the secondary prompts) and even another "magic" variable: SECONDS (shows the time in seconds since the shell started)

In Bash, you can see all the variables and their export status with declare -p. The ones marked with -x are exported, the ones without x aren't. (Some will have other flags like i for integer or r for read-only.)

In Zsh or ksh93, you can use typeset -p, though Zsh marks the exported variables by changing typeset to export in the output, instead of using flags. export by itself would also show all the exported variables, but that's about the same result you get by running env.

2

If you google for this, the docs state the following:

$RANDOM is an internal Bash function (not a constant) that returns a pseudorandom [1] integer in the range 0 - 32767. It should not be used to generate an encryption key.

If you use strace you can see that the $RANDOM "variable" is passed in directly to commands as if it were any ordinary shell variable or an environment variable, but it's just a internal function that's built into the shell, Bash, that's doing the 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
...

vs. this regular 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
...

The variable is not being passed on as a reference.

References

  • 1
    well, isn't that passing the expanded value of $RANDOM or $SOMEVAR through a command line argument, and not as an environment variable? You'd need to export both to pass them through the environment. – ilkkachu Jul 5 '18 at 8:48
  • No that would make no difference. The shell expands them regardless. The way I showed it is basically highlighting the fact that the shell is doing the expansion. – slm Jul 5 '18 at 8:50
  • 2
    The strace output doesn't seem to catch the internal function run by the shell. In both cases, the variable has already been expanded in the first line of the strace. I don't understand what difference you are pointing to. What am I missing? – terdon Jul 5 '18 at 8:55
  • Showing that the $RANDOM expansion is done internally to the shell. It's basically confirmation that the shell is determining the value, and not passing a reference to a variable. The shell when it's expanding the command line to execute parses $RANDOM and passes the expanded form to echo. – slm Jul 5 '18 at 8:56
  • 2
    So, nothing like an environment variable, then. – Toby Speight Jul 5 '18 at 9:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.