Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to randomly loop over an array (shuffle) in bash [duplicate]

Given an array of elements (servers), how do I shuffle the array to obtain a random new array ?

inarray=("serverA" "serverB" "serverC")

outarray=($(randomize_func ${inarray[@]})

echo ${outarray[@]}
serverB serverC serverA

There is a command shuf (man page) but it does not exist on every linux.

This is my first attempt to post a self-answered question stackoverflow, if you have a better solution, please post it.

like image 741
JoKoT3 Avatar asked Apr 30 '26 16:04

JoKoT3


1 Answers

This is another pure Bash solution:

#! /bin/bash

# Randomly permute the arguments and put them in array 'outarray'
function perm
{
    outarray=( "$@" )

    # The algorithm used is the Fisher-Yates Shuffle
    # (https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle),
    # also known as the Knuth Shuffle.

    # Loop down through 'outarray', swapping the item at the current index
    # with a random item chosen from the array up to (and including) that
    # index
    local idx rand_idx tmp
    for ((idx=$#-1; idx>0 ; idx--)) ; do
        rand_idx=$(( RANDOM % (idx+1) ))
        # Swap if the randomly chosen item is not the current item
        if (( rand_idx != idx )) ; then
            tmp=${outarray[idx]}
            outarray[idx]=${outarray[rand_idx]}
            outarray[rand_idx]=$tmp
        fi
    done
}

inarray=( 'server A' 'server B' 'server C' )

# Declare 'outarray' for use by 'perm'
declare -a outarray

perm "${inarray[@]}"

# Display the contents of 'outarray'
declare -p outarray

It's Shellcheck-clean, and tested with Bash 3 and Bash 4.

The caller gets the results from outarray rather than putting them in outarray because outarray=( $(perm ...) ) doesn't work if any of the items to be shuffled contain whitespace characters, and it may also break if items contain glob metacharacters. There is no nice way to return non-trivial values from Bash functions.

If perm is called from another function then declaring outarray in the caller (e.g. with local -a outarray) will avoid creating (or clobbering) a global variable.

The code could safely be simplified by doing the swap unconditionally, at the cost of doing some pointless swaps of items with themselves.

like image 123
pjh Avatar answered May 02 '26 05:05

pjh