Eric Zheng Eric Zheng - 2 months ago 7
Bash Question

Shell - Why would the built-in RANDOM function return 1?

Today, I find that my shell script occasionally exits early. After checking the code, I find out that it is because I have a

set -e
in the head of my shell script and I use the built-in function
RANDOM
to generate a random number.

With
set -e
, if any command in the script return non-zero, the script would exit immediately. So I write a snippet of testing code like this:

set -u
#set -e

for (( i=0; i < 100; i++ ))
do
index=$(expr ${RANDOM} % 16)
echo "RANDOM return $? , index is $index"
done


Pay attention to the commented
set -e
, I do that so as to show 100 output line, in which some
RANDOM return 1
really exists. For example, I get a result like this:

RANDOM return 0 , index is 2
RANDOM return 0 , index is 10
RANDOM return 1 , index is 0
RANDOM return 0 , index is 9
RANDOM return 0 , index is 5
RANDOM return 0 , index is 9
RANDOM return 0 , index is 4
RANDOM return 0 , index is 6
RANDOM return 0 , index is 2
RANDOM return 0 , index is 4
RANDOM return 0 , index is 14
RANDOM return 0 , index is 6
RANDOM return 0 , index is 2
RANDOM return 1 , index is 0
RANDOM return 0 , index is 1
RANDOM return 0 , index is 8
...(more line)


See? RANDOM returns 1 but it generates the random number correctly, and that is why my shell script exits early. After switching to python's random-number-generating code, my script is back to normal.

Why would
RANDOM
return 1?

Answer

$? is the status of the last command you executed. $RANDOM is not a command, so evaluating it doesn't affect $?. ($RANDOM is a "shell parameter" or variable that yields a random integer between 0 and 32767 when it's evaluated.)

The $? value you're seeing is the result of executing the command expr ${RANDOM} % 16.

The expr command returns a status of 1 if the result of evaluating the expression is null or 0, so you'll get a status of 1 whenever $RANDOM % 16 is 0 (6.25% of the time).

% expr 1 % 16 ; echo "\$?=$?"
1
$?=0
% expr 0 % 16 ; echo "\$?=$?"
0
$?=1

If you've done set -e and you want to execute a command that might fail without terminating the shell, you can append something like || true. Since the prefix to || is used as a condition, failure just makes the condition false.

index=$(expr ${RANDOM} % 16) || true
echo "Status of expr is $?, index is $index"