96

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?

1
  • The tags should be fixed -- there are no arrays in POSIX shell. Commented Feb 26, 2020 at 9:29

6 Answers 6

140

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Note that that code will still be biased towards the lower array indexes.
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.
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 ))]}
Thank you @loklaan, you are my saviour
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.
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]}

2 Comments

this gives - Mon Mar 13 10:04:55 IST 2017 Whoops! I'm out of memmory!
Additional items can be added to array, e.g. arr[7]="Zip, ding, another line."
6

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"

Comments

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]}

1 Comment

This jot implementation 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.
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**

1 Comment

the first snippet is faulty - $r could be one of 0, 1 or 2 - so you get an empty string in the worst case
-1

For use cases where a bit of python is OK so it becomes a one liner and readable, here is another take:

echo $(python3 -c "import random;print(random.choice(['a','b','c']))")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.