On Wed, Oct 21, 2026 at 16:45:30 +0200, Franco Martelli wrote:
> # Populate playlist array with unique random values
> while [ "$index" -le "$total" ]; do
> clip="$(/usr/bin/numrandom /1..$total/)"
I would try to avoid calling an external program inside this loop,
especially if the purpose of the external program is just to choose
a random number between 1 and n. That can be done with a non-forking
bash function call.
> if [[ ! ${playlist[@]} =~ "$clip" ]]; then # [ -z
> ${rndarr[clip]} ]; then
> # rndarr[$clip]=1
> playlist[$index]=$clip
> ((index++))
> fi
> done
> It works fine if I use the second array "rndarr[]". I use that array to
> store 1 at the rndarr[$index] occurrence then, after incremented the index,
> I check whether it's empty (no 1 stored) at the new $index value.
>
> My wishes is to avoid to use a second array (rndarr[]) and use playlist[]
> array only to do this, but with the above code I have an endless loop, why?
OK, first note: using an associative array would be the typical way to
store a set of "things that have already been chosen". Your "rndarr"
seems to be an indexed array, which I guess will also work as long
as the "thing chosen" is a non-negative integer.
Trying to do this without a second array means you need to search through
the entire first array every time to see whether the thing is already
in the array. That's an O(n) operation, while looking for an element
in an associative array (or your second indexed array) should be a much
faster O(1) operation.
Second note: [[ ! ${x[@]} =~ "$i" ]] is not even a safe way to check
for the existence of an element "$i" in an array "x". Consider the
case where i = 5, and you've got 15 and 50 already in the array. The
expansion of ${x[@]} will include the text "15 50", and doing an =~ 5
against that text will yield a false positive.
Third note: many people, at this point, will cobble together some code
like this:
IFS=:
[[ ":${x[*]}:" =~ ":$i:" ]] ...
unset IFS
which compares the string :15:50: against the regex :5:. This works,
sort of. But you're expanding the *entire* array as a string on
every single loop iteration, generating a regular expression and
applying it... it's overkill. You can reduce the regular expression
to a substring match:
IFS=:
[[ ":${x[*]}:" = *":$i:"* ]] ...
unset IFS
which is slightly better, but you're still copying the entire array
every single iteration.
Fourth note: if you want to check whether $i is in the array x[],
the proper way is to *use a loop*:
found=0
for element in "${x[@]}"; do
if [[ $element = "$i" ]]; then
found=1
break
fi
done
This avoids copying the entire array, and it allows you to stop early
once you find the element.
Honestly, I would just use the associative array. It'll be much more
efficient than anything else you can do.
Fifth note: you want to know why your loop never terminates, but I
can't give you an answer to that. I'd suggest turning on set -x to
see what the script is doing.
> I checked the remarkable ABS ยน handbook
The most remarkable thing about the ABS is how many flaws it contains.