In bash, `$RANDOM` and `$SECONDS` return a different value each time they are read. What do these shell-maintained variables give you, and what should `$RANDOM` not be used for?
answer
- read it and it changes
- fifteen bits is not many
- assigning one of them reseeds or rebases
- seeded means reproducible, not secret
- unset it once and it is ordinary forever
basics
~20 sEach reference to $RANDOM yields a new pseudo-random integer between 0 and 32767, and $SECONDS counts whole seconds since the shell started. $RANDOM is a seeded pseudo-random generator, so it must not be used for anything security-sensitive.
solid answer
~50 sBoth are variables bash generates on the fly rather than ones you set. Reading `$RANDOM` produces a new pseudo-random integer in the range 0 to 32767; assigning to it seeds the generator, which makes sequences reproducible. Reading `$SECONDS` gives the whole seconds elapsed since the shell started, and assigning to it resets the base, so `SECONDS=0; work; echo "took ${SECONDS}s"` is a cheap block timer. The caveat on `$RANDOM` is that it is a small, seedable pseudo-random source — only about 15 bits, so collisions in a loop are likely, and it is not cryptographically secure. Never use it for tokens, passwords or predictable-name temp files; use a proper entropy source or a tool designed for the job. Bash 5.1 added `$SRANDOM`, a 32-bit value drawn from a system entropy source that cannot be seeded. Both variables lose their special behaviour permanently if you `unset` them.
code
bash · 9 lines#!/usr/bin/env bash
# RANDOM is seedable, therefore reproducible, therefore not secret
RANDOM=42; echo "run 1: $RANDOM $RANDOM"
RANDOM=42; echo "run 2: $RANDOM $RANDOM"
# SECONDS as a cheap block timer
SECONDS=0
sleep 2
echo "elapsed: ${SECONDS}s"go deeper
Know that reading $RANDOM gives a new number between 0 and 32767 and $SECONDS gives seconds since the shell started, and that both are produced by bash rather than set by you.
Explain that assigning to RANDOM seeds the generator and assigning to SECONDS rebases the timer, and that unsetting either strips its special behaviour for good. Be able to state the 0-32767 range and why the modulo trick is biased.
Make the security call out loud: a seedable 15-bit PRNG is unsuitable for secrets or unguessable names, and collisions are likely well before you would expect. Name the alternatives — a system entropy source, or a tool that creates the artefact atomically instead of guessing a name.
Treat this as policy rather than trivia: define where randomness in operational scripts is allowed to come from, and require that anything security-bearing be generated by a reviewed mechanism rather than whatever the shell makes convenient.
## Variables the shell computes for you Most shell variables are inert storage: you assign, you read back the same thing. A handful are maintained by bash itself and produce a value at the moment you reference them. `RANDOM` and `SECONDS` are the two you meet most often in scripts. ## RANDOM Every expansion of `$RANDOM` yields a fresh pseudo-random integer in the range 0 through 32767 — roughly 15 bits. ```bash echo $((RANDOM % 6 + 1)) # a die roll ``` Assigning to `RANDOM` seeds the generator, which makes a sequence reproducible — useful for a deterministic test, and a clear signal about what kind of generator this is: ```bash RANDOM=42; echo "$RANDOM $RANDOM" RANDOM=42; echo "$RANDOM $RANDOM" # same two numbers again ``` Two consequences follow from the 15-bit range. First, the *birthday problem*: with only 32768 possible values, a loop generating a few hundred names will very likely produce a duplicate. Anything relying on uniqueness — a filename, a job id, a lock name — is unsafe on that basis alone. Second, `$RANDOM % n` is biased whenever `n` does not divide 32768 evenly, because the low residues get one extra chance. For a die roll nobody cares; for anything measured, it matters. The bigger rule is that `RANDOM` is not cryptographically secure. It is a small seeded PRNG whose output is predictable to anyone who can observe or influence the seed. Never derive a password, a token, a session id, a nonce or a secret temporary path from it. Draw from the system entropy source instead — reading from `/dev/urandom` and encoding the bytes, or using a tool built for the purpose — and for temporary files use the dedicated facility that creates the file atomically rather than inventing a name and hoping. Bash 5.1 added `SRANDOM`: each reference gives a 32-bit value obtained from a system entropy source, and it cannot be seeded by assignment. It is a better generator, but it is recent enough that you cannot assume it exists — macOS still ships bash 3.2 by default, and plenty of long-lived images carry bash 4.x. ## SECONDS `SECONDS` reports the number of whole seconds since the shell started. Assigning to it resets the origin, which is what makes it a convenient timer: ```bash SECONDS=0 run_migration echo "migration took ${SECONDS}s" ``` It is whole seconds only — no sub-second resolution — so for anything short or precise you want a real clock reading instead. It is also wall-clock elapsed time, not CPU time, and it keeps counting while a process is blocked or the machine is asleep. For a build step or a deploy phase that is exactly the number you want to log; for benchmarking it is not. A natural pairing is a bounded wait loop, where `SECONDS` gives the deadline without spawning a process per iteration: ```bash SECONDS=0 until service_is_up; do (( SECONDS > 60 )) && { echo "timed out" >&2; exit 1; } sleep 2 done ``` ## The unset trap Both variables share a rule worth remembering: if you `unset` `RANDOM` or `SECONDS`, the name loses its special properties *permanently* for that shell, even if you later assign to it again. It becomes an ordinary variable holding whatever you put in it. The same is true of the other shell-generated variables such as `LINENO`. In practice this bites when a script does a blanket cleanup loop that unsets a list of names, or when someone uses `SECONDS` as an ordinary counter name in one function and wonders why the timer elsewhere stopped moving. Treat these names as reserved and do not reuse them for your own data. ## Related generated variables `LINENO` expands to the line number in the script where it appears, which is why it shows up inside error handlers and trace messages. `$$` is the process id of the shell, and `$_` holds the last argument of the previous command. Like `RANDOM` and `SECONDS`, these are produced by the shell rather than stored, and like them they are names your own code should not shadow.
- Why is `$((RANDOM % 100))` not a uniform distribution?`RANDOM` yields 0 to 32767, which is 32768 values, and 32768 is not a multiple of 100. The first 68 residues get one more chance than the rest, so low values are slightly over-represented. The bias is tiny here but grows as the modulus approaches the range; the standard fix is rejection sampling, or using a generator with a wider range.
- What does assigning to `SECONDS` actually change?It rebases the counter: bash records the current time as the new origin and subsequent reads report seconds since that assignment, not since shell start. That is what makes `SECONDS=0` a one-line block timer. The value is whole seconds of wall-clock time, so it keeps counting while a process is blocked and offers no sub-second resolution.
- Is `$$` a better source of uniqueness than `$RANDOM` for a name?Not really. A process id is unique among *live* processes but is recycled, is easily guessed by anything on the host, and gives one value per script rather than per name. Neither is a safe basis for a file another user could pre-create. Use the facility that creates the file atomically with a mode you control, and treat the name as an output rather than something you invent.
saying these in an interview costs you the question
- Uses $RANDOM to generate a password or token
- Assumes $RANDOM values never repeat in a loop
- Thinks SECONDS measures CPU time
- Believes $RANDOM is reseeded from real entropy each read
- Reuses SECONDS or RANDOM as an ordinary variable name