corvus
corvus

Reputation: 513

$RANDOM being constant when returning it from a function

getRandom () {
  echo $RANDOM
}

getRandomFromGetRandom () {
  echo $(getRandom)
}

Calling getRandom does the expected every time, it returns some random number on each call.

What I'd expect is the same from getRandomFromGetRandom, that a random number being called every time I call getRandomFromGetRandom, but that's not the case. Every time that function is called it returns a constant which changes only when I open another instance of the terminal.

Upvotes: 2

Views: 199

Answers (2)

oguz ismail
oguz ismail

Reputation: 50775

man zshparam says:

The values of RANDOM form an intentionally-repeatable pseudo-random sequence; subshells that reference RANDOM will result in identical pseudo-random values unless the value of RANDOM is referenced or seeded in the parent shell in between subshell invocations.

Since command substitution is performed in a subshell, this explains the repetitive values getRandomFromGetRandom prints. To fix it, reference RANDOM before echoing $(getRandom), like

getRandomFromGetRandom () {
  : $RANDOM
  echo $(getRandom)
}

Or seed it (zsh-only; won't work on yash)

getRandomFromGetRandom () {
  RANDOM= echo $(getRandom)
}

Upvotes: 4

dash-o
dash-o

Reputation: 14452

(Almost) All Random implementations are based on saved state. Calculating the value of the next random number depends only on the state, and will modify the state so that the next call will yield different number. When a process is forked, the state of the parent is copied into the state of the children.

In the posted code, the getRandom is calculated in a sub-shell (because of $(getRandom)). As a result, the code repeated uses the inherit state from the parent, which is never updated. The next call therefore will use the same (parent) state, and will result in the same number.

The first two iterations will look like:

Iteration 1:

  1. Parent initialize parent_state to S0
  2. Parent fork, child_state set to S0 (copied from parent_state)
  3. Child calculate first random using S0, result is R0, modified child_state = S1
  4. Child exit, returns R1 to parent (via stdout, print with echo)

Iteration 2:

  1. Parent fork agin, child set set to S0 (copied from parent_state)
  2. Child calculate second random using S0, result is R0, modified child_state = S1
  3. Child exit, returns R1 to parent (via stdout, print with echo)

The following shows how to get similar logic to work

getRandom () {
  my_random=$RANDOM
}

getRandomFromGetRandom () {
   getRandom
   echo "$my_random"
}

getRandomFromGetRandom
getRandomFromGetRandom 

Upvotes: 2

Related Questions