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

人走茶凉 提交于 2021-01-27 14:18:40

问题


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.


回答1:


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.




回答2:


This is the solution I found (it even works in bash < 4.0).

Shellchecked and edited thanks to comments below.

#!/bin/bash
# random permutation of input
perm() {
    # make the input an array
    local -a items=( "$@" )
    # all the indices of the array
    local -a items_arr=( "${!items[@]}" )
    # create out array
    local -a items_out=()
    # loop while there is at least one index
    while [ ${#items_arr[@]} -gt 0 ]; do
        # pick a random number between 1 and the length of the indices array
        local rand=$(( RANDOM % ${#items_arr[@]} ))
        # get the item index from the array of indices
        local items_idx=${items_arr[$rand]}
        # append that item to the out array
        items_out+=("${items[$items_idx]}")
        ### NOTE array is not reindexed when pop'ing, so we redo an array of 
        ### index at each iteration
        # pop the item
        unset "items[$items_idx]"
        # recreate the array
        items_arr=( "${!items[@]}" )
    done
    echo "${items_out[@]}"
}

perm "server1" "server2" "server3" "server4" "server4" "server5" "server6" "server7" "server8"

It is more than possible that it can be optimized.




回答3:


The sort utility has the ability to shuffle lists randomly.

Try this instead:

servers="serverA serverB serverC serverD"
for s in $servers ; do echo $s ; done | sort -R



回答4:


You should use shuf:

inarray=("serverA" "serverB" "serverC")
IFS=$'\n' outarray=($(printf "%s$IFS" "${inarray[@]}" | shuf))

Or when using array members with newlines and other strange characters, use null delimetered strings:

inarray=("serverA" "serverB" "serverC")
readarray -d '' outarray < <(printf "%s\0" "${inarray[@]}" | shuf -z)


来源:https://stackoverflow.com/questions/53229380/how-to-randomly-loop-over-an-array-shuffle-in-bash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!