The shuf Command
shuf command is a nice little utility. The command randomly selects an item from a list. One example might be selecting a playing card from a deck of 52 cards.
I am using the command in a shell script that randomly selects a number from a sequential list of 48 numbers:
shuf -i 1-$SEQ_LIST -n 1
In my use case
$SEQ_LIST is 48.
Originally the script was not written with efficiency in mind. The script selects a number. In the script there is a test to determine whether that number has been used recently. When that number should be skipped the script loops through the shuffle sequence to select a different number. The list of unwanted numbers grows until finally selecting a new number.
This is inefficient because a number that should be skipped might be randomly selected multiple times. The loop repeats when selecting an unwanted number. Yet the loop worked well enough for my original use case because commonly a new number was selected within a few iterations.
With 48 numbers in the sequence the inefficiency was irrelevant except under one scenario — when the list of unwanted numbers grew. The inefficiency of the loop became obvious because the remaining list of usable numbers grew smaller yet the loop repeatedly selected unwanted numbers. That sometimes created the illusion the random selection was hanging or stalling because the random selection did not select a number from the shrinking list of usable numbers within only a few iterations.
shuf command provides a way to work around this. The command accepts input from any kind of list and not only sequential lists. For that usage the command has an
I updated my shuffle loop to continually update and only use a list of usable numbers:
shuf -e $(seq $SEQ_LIST | egrep -vw "$(echo "$UNWANTED_NUMBERS" | tr ' ' '|')") -n 1
In my use case the
$UNWANTED_NUMBERS variable is the growing list of recently used numbers. The updated command pipes the original sequence of 48 numbers to a temporary exclusion list created by the extended
egrep -vcommand. Because the script maintains the list of unusable numbers in a list separated by spaces, the
tr command converts the spaces to pipe symbol separators. The result is a list of numbers that no longer is sequential, to which the
-e option handles nicely.