I am trying to random choose a number of items of an array and then update it to do it again till the last set:
#! /bin/bash
A=({1..27})
T=${#A[@]} #number of items in the array
N=3 #number of items to be chosen
V=$(($T/$N)) #number of times to loop
echo ${A[@]} " >> ${#A[@]}"
for ((n=0;n<$V;n++)); do
A1=()
for I in `shuf --input-range=0-$(( ${#A[*]} - 1 )) | head -${N}`; do #random chooses N items random
S=`echo ${A[$I]}` #the chosen items
#echo $S
A1+=("$S|") #creates an array with the chosen items
A=("${A[@]/$S}") #deletes the the chosen items from array
done
echo ${A[@]} " >> ${#A[@]}"
echo ${A1[@]} " >> ${#A1[@]}"
done
The type of output I am getting with this code is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 >> 27
1 4 5 6 7 8 9 10 11 1 1 14 15 16 17 18 19 1 2 4 5 6 7 >> 27
20| 2| 3| >> 3
4 6 7 8 9 0 1 4 6 7 8 9 2 4 6 7 >> 27
1| | 5| >> 3
6 7 8 9 0 1 6 7 8 9 2 6 7 >> 27
| | 4| >> 3
7 8 9 0 1 7 8 9 2 7 >> 27
6| | | >> 3
7 8 9 0 1 7 8 9 7 >> 27
| | 2| >> 3
7 9 0 1 7 9 7 >> 27
8| | | >> 3
7 9 1 7 9 7 >> 27
| | 0| >> 3
7 9 1 7 9 7 >> 27
| | | >> 3
1 >> 27
9| 7| | >> 3
Any ideas why it works fine in the start and fails in the end??
Solved
There are some improvements that could be made to your script:
- Use lowercase letters
- Quote your expansion (
"${a[@]}").
- Remove items by doing
unset a[j].
- Re-build array with
a=("${a[@]}").
- No need for head as shuf could produce a counted result with
-n.
- Avoid the use of backquotes, use
$(…) instead.
That will reduce the script to:
#!/bin/bash
t=27 #number of items in the array
n=3 #number of items to be chosen
a=( $(seq 1 "$t") )
a1=()
while (( ${#a[@]} >= n )); do
for j in $(shuf -n "$n" --input-range=0-$((${#a[@]}-1)) ); do # choose $n random items.
a1+=("${a[j]}") # append to an array the chosen items.
unset "a[j]" # deletes the the chosen items from array.
done
a=("${a[@]}") # re-build the array.
echo "a ${a[@]} >> ${#a[@]}"
echo "a1 ${a1[@]} >> ${#a1[@]}"
done
This will produce this output:
a 1 2 3 5 6 7 8 9 10 11 12 14 15 16 18 19 20 21 22 23 24 25 26 27 >> 24
a1 17 13 4 >> 3
a 1 2 5 6 7 8 10 11 12 14 15 16 18 19 20 21 22 23 24 26 27 >> 21
a1 17 13 4 25 3 9 >> 6
a 1 2 5 6 7 8 10 11 14 15 16 18 21 22 23 24 26 27 >> 18
a1 17 13 4 25 3 9 12 20 19 >> 9
a 1 2 5 7 8 10 11 14 15 16 21 22 24 26 27 >> 15
a1 17 13 4 25 3 9 12 20 19 23 6 18 >> 12
a 2 7 8 10 11 14 15 16 22 24 26 27 >> 12
a1 17 13 4 25 3 9 12 20 19 23 6 18 1 5 21 >> 15
a 2 8 10 14 15 22 24 26 27 >> 9
a1 17 13 4 25 3 9 12 20 19 23 6 18 1 5 21 7 11 16 >> 18
a 2 10 14 24 26 27 >> 6
a1 17 13 4 25 3 9 12 20 19 23 6 18 1 5 21 7 11 16 22 8 15 >> 21
a 14 26 27 >> 3
a1 17 13 4 25 3 9 12 20 19 23 6 18 1 5 21 7 11 16 22 8 15 2 24 10 >> 24
a >> 0
a1 17 13 4 25 3 9 12 20 19 23 6 18 1 5 21 7 11 16 22 8 15 2 24 10 14 26 27 >> 27
But the same array a1 could be built just in one step with shuf:
#!/bin/bash
t=27 # number of items in the array
n=3 # number of items to be chosen
a1=( $(shuf --input-range=1-"$t") )
printf '%3s ' "${a1[@]}"; echo
Which will print:
$ ./script.sh
15 23 1 9 24 2 21 11 12 10 19 25 27 13 5 26 4 7 14 3 22 20 17 18 16 6 8
Or, if the result must be in $n elements per line:
#!/bin/bash
t=27 # number of items in the array
n=3 # number of items to be chosen
a1=( $(shuf --input-range=1-"$t") )
while ((i+n<=t)); do
printf '%3s ' "${a1[@]:i:n}"; echo
((i+=n))
done
Printing:
$ ./script.sh
7 19 16
4 20 26
11 23 2
13 6 15
22 12 25
18 14 10
21 8 9
5 24 27
1 3 17
Looks like a simpler solution.
There are a few wrong things in your script, especially the way you (think you) delete the elements from the array. You're not deleting any elements, you're only replacing their values by the empty string in every field!. That's why your array has 27 elements all the way through, and after the first iteration, all the 2's and 3's are removed from each field. Here's a more idiomatic script:
#! /bin/bash
a=( {1..27} )
n=3 #number of items to be chosen
while ((${#a[@]}>=n)); do
a1=()
for ((i=0;i> %s\n' "${a[*]}" "${#a[@]}"
# print chosen elements
printf 'a1: %s >> %s\n' "${a1[*]}" "${#a1[@]}"
done