I am pretty new to bash scripting and I'm facing a behavior I can't explain. I need to create random plate numbers, however the plate generated from the function I wrote always creates the same plate number as calling it in a loop shown.
generate_plate() {
RANDOM=$$$(date +%s)
first_letter=(A B C D E F )
other_letters=(A B C D E F G H J K L M N P R S T V W X Y Z)
numbers=( {0..9} )
first_random_letter=${first_letter[$(($RANDOM % ${#first_letter[@]} + 1 ))]}
second_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
first_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
second_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
fourth_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
plate="$first_random_letter$second_random_letter$first_number$second_number$third_number$third_random_letter$fourth_random_letter"
echo "$plate"
}
for i in {1..5}; do
targa_random=$(generate_plate)
echo "Plate $i: $targa_random"
done
If instead i call each line of the method in a loop then it will create all different plate numbers.
while [ 1 ]
do
first_letter=(A B C D E F )
other_letters=(A B C D E F G H J K L M N P R S T V W X Y Z)
numbers=( {0..9} )
RANDOM=$$$(date +%s)
first_random_letter=${first_letter[$(($RANDOM % ${#first_letter[@]} + 1 ))]}
second_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
first_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
second_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
fourth_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
plate="$first_random_letter$second_random_letter$first_number$second_number$third_number$third_random_letter$fourth_random_letter"
echo $plate
sleep 1
done
Using seed for RANDOM won't make a difference. Why is it so? what's different between the two approaches? Can you spot what I'm doing wrong? The +1 in the array index is because I'm using the zsh shell which has the array index starting at 1 as I found out here Select a random item from an array in Bash
Many thanks
I'm running bash 5.1.16
and was able to get OP's function + 5-pass for
loop to work with either of these modifications:
RANDOM=$$$(date +%s)
line or ...sleep 1
to the 5-pass for
loopHowever, OP has stated these modifications do not work for their bash 3.2.57
environment (see Charles Duffy's answer for possible explanations) so the question becomes how can we get this function to work in OP's environment?
One quick-n-dirty option would be to use the same variable name in the function and the parent process. This would require 2 changes ... 1) remove the RANDOM=$$$(date +%s)
and echo "$plate"
lines from the function and 2) modify the for
loop as follows:
for i in {1..5}; do
generate_plate
echo "Plate $i: $plate"
done
One downside to this approach is the parent process needs to know the function populates the variable named plate
.
With some code changes we could tell the function what variable to populate. With bash 4.3+
this would be relatively easy with a nameref
but this is not an option in OP's bash 3.2.57
environment so that leaves us with a second option of indirect variable references.
One idea for implementing an indirect variable reference:
generate_plate() {
first_letter=(A B C D E F )
other_letters=(A B C D E F G H J K L M N P R S T V W X Y Z)
numbers=( {0..9} )
first_random_letter=${first_letter[$(($RANDOM % ${#first_letter[@]} + 1 ))]}
second_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
first_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
second_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_number=${numbers[$(($RANDOM % ${#numbers[@]} + 1 ))]}
third_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
fourth_random_letter=${other_letters[$(($RANDOM % ${#other_letters[@]} + 1 ))]}
plate="$first_random_letter$second_random_letter$first_number$second_number$third_number$third_random_letter$fourth_random_letter"
if [[ -n $1 ]] # if a variable name was provided then ...
then
read -r $1 <<< "$plate" # set the variable via indirect reference else ...
else
echo "$plate" # print to stdout
fi
}
Taking for a test drive ...
OP's for
loop updated:
for i in {1..5}; do
generate_plate targa_random # pass the name of the variable to be populated
echo "Plate $i: $targa_random"
done
Plate 1: EW364YB
Plate 2: CJ85EK
Plate 3: P289HZ
Plate 4: EP31CL
Plate 5: CG18BM
Allowing the function to print to stdout:
$ generate_plate
DY462TG
$ generate_plate
FZ418ZC
$ generate_plate
DZ812WH
$ generate_plate
FG34HC