How do I specify chance when setting a variable to random item in array?

Currently I do something like this to set a variable to a random item in an array:

``````array=("foo" "bar" "baz")

var=\${array[\$RANDOM % \${#array[@]} ]}
``````

How would I do if I want to set `\$var` to one of these values, but specify the chance that the variable will be set to the value of each item respectively? Say I want a 73,3% chance for `foo`, 26,6% chance for `bar`, and 0,1% chance for `baz`.

• Are you allowed to use only bash builtins and no external programs? – Mark Plotnick Feb 4 at 17:30
• @MarkPlotnick I would prefer bash, but anything that I can run from the command line works – DisplayName Feb 4 at 17:34

One way: set up a parallel array with the corresponding percentage chances; below, I've scaled them to 1000. Then, choose a random number between 1 and 1000 and iterate through the array until you've run out of chances:

``````#!/bin/bash

array=( "foo"  "bar" "baz")
chances=(733    266     1)

choice=\$((1 + (RANDOM % 1000)))
value=

for((index=0; index < \${#array[@]}; index++))
do
choice=\$((choice - \${chances[index]}))
if [[ choice -le 0 ]]
then
value=\${array[index]}
break
fi
done

[[ index -eq \${#array[@]} ]] && value=\${array[index]}
printf '%s\n' "\$value"
``````
• I've also used this method before. It works well. However: it might be worth to calculate the sum dynamically. If it's not actually 1000, you get the wrong results. Also, if RANDOM is limited to 32768, there might be some random bias in the selection. – frostschutz Feb 4 at 17:56
• Great points, frostschutz; I didn't do any error-checking of the sums, since I assumed they were all assigned manually. – Jeff Schaller Feb 4 at 17:59

The shell can't do floating point math, but if we just move the decimal point, we can use `\$RANDOM` and integer math:

``````#!/usr/local/bin/bash
array=("foo" "bar" "baz")
dieroll=\$((\$RANDOM % 1000))

if [[ "\$dieroll" -lt 1 ]]; then
printf "%s\n" "\${array}"
elif [[ "\$dieroll" -lt 266 ]]; then
printf "%s\n" "\${array}"
else
printf "%s\n" "\${array}"
fi
``````

This has the advantages of not having to blow the array up to 1000 entries or needing any `for` loops.