87

I'm creating a bot in Shell Script:

# Array with expressions
expressions=("Ploink Poink" "I Need Oil" "Some Bytes are Missing!" "Poink Poink" "Piiiip Beeeep!!" "Hello" "Whoops! I'm out of memmory!")

# Seed random generator
RANDOM=$$$(date +%s)

# Loop loop loop loop loop loop ...
while [ 1 ]
do
    # Get random expression...
    selectedexpression=${expressions[$RANDOM % ${#RANDOM[*]}]}

    # Write to Shell
    echo $selectedexpression


    # Wait an half hour
    sleep 1 # It's one second for debugging, dear SOers
done

I want that it prints a random item from the expressions every second. I tried this but it does not work. It only prints the first one (Ploink Poink) every time. Can anyone help me out? Thanks

Ramratan Gupta
  • 1,056
  • 3
  • 17
  • 39

5 Answers5

129

Change the line where you define selectedexpression to

selectedexpression=${expressions[ $RANDOM % ${#expressions[@]} ]}

You want your index into expression to be a random number from 0 to the length of the expression array. This will do that.

Waldir Leoncio
  • 10,853
  • 19
  • 77
  • 107
Jacob Mattison
  • 50,258
  • 9
  • 107
  • 126
  • 11
    Note that that code will still be biased towards the lower array indexes. – Joey Mar 06 '10 at 10:14
  • 16
    True. However, unless your list of expressions is very long, the effect is minor. $RANDOM is a number between 0 and 32767. Say you had 100 items in your list. The first 67 items would have a 328/32767 (.01001) chance, while the last 33 would have a 327/32767 (.00998) chance. For a shorter list the difference would be even less. Still, you make a good point, and the shell RANDOM function is not suitable for situations where you must have truly random numbers, such as cryptography. – Jacob Mattison Mar 07 '10 at 01:20
  • 19
    For anyone that comes to this answer, and applies it in ZSH: Array index in ZSH starts at 1 (for some reason), so change the statement to: `selectedexpression=${expressions[$(($RANDOM % ${#expressions[@]} + 1 ))]}` – loklaan Jan 17 '19 at 08:27
  • Thank you @loklaan, you are my saviour – Lucat Mar 03 '20 at 19:42
  • 1
    To get a uniform distribution you can generate the random numbers by scaling a random floating point number between 0.0 and 1.0. Example for the array `a`: `echo "${a[$(awk '{srand($2); print int(rand()*$1)}' <<< "${#a[@]} $RANDOM")]}"`. Or use a program like `shuf -n1 -i0-...` or `jot -r 1 0 ...` which does the job for you. – Socowi Apr 20 '20 at 11:33
30
arr[0]="Ploink Poink"
arr[1]="I Need Oil"
arr[2]="Some Bytes are Missing!"
arr[3]="Poink Poink"
arr[4]="Piiiip Beeeep!!"
arr[5]="Hello"
arr[6]="Whoops! I'm out of memmory!"
rand=$[$RANDOM % ${#arr[@]}]
echo $(date)
echo ${arr[$rand]}
Mohsen Abasi
  • 2,050
  • 28
  • 30
noobninja
  • 900
  • 12
  • 14
5

Here's another solution that may be a bit more random than Jacob Mattison's solution (hard to say from the jot manpages):

declare -a expressions=('Ploink' 'I Need Oil' 'Some Bytes are Missing' 'Poink Poink' 'Piiiip Beeeep' 'Hello' 'Whoops I am out of memory')
index=$( jot -r 1  0 $((${#expressions[@]} - 1)) )
selected_expression=${expressions[index]}
Powers
  • 18,150
  • 10
  • 103
  • 108
  • 1
    [This `jot` implementation](https://opensource.apple.com/source/shell_cmds/shell_cmds-162/jot/jot.c.auto.html) uses floating point calculations to generate the random number. The system's `random()` function is scaled down to a number between `0.0` and `1.0` which is then scaled up to the range given in the arguments, therefore I consider the distribution to be uniform. – Socowi Apr 20 '20 at 11:14
5

Solution using shuf:

expressions=("Ploink Poink" "I Need Oil" "Some Bytes are Missing!" "Poink Poink" "Piiiip Beeeep!!" "Hello" "Whoops! I'm out of memmory!")
selectedexpression=$(printf "%s\n" "${expressions[@]}" | shuf -n1)
echo $selectedexpression

Or probably better:

select_random() {
    printf "%s\0" "$@" | shuf -z -n1 | tr -d '\0'
}

expressions=("Ploink Poink" "I Need Oil" "Some Bytes are Missing!" "Poink Poink" "Piiiip Beeeep!!" "Hello" "Whoops! I'm out of memmory!")
selectedexpression=$(select_random "${expressions[@]}")
echo "$selectedexpression"
presto8
  • 487
  • 5
  • 8
2

for random

1.

rand=("q" "w") 
r=$(shuf -i 0-${#rand[@]} -n 1)
echo ${rand[$r]}
echo `$((1 + $RAND % 5))` // **for number between 1..5**
Baptiste Mille-Mathias
  • 2,144
  • 4
  • 31
  • 37
Netwons
  • 1,170
  • 11
  • 14