For i in loops on 2 arrays

Hey ,

i have this script and i have these loops so it can find a match between 2 arrays :

ARRAY_1=(one two three)
ARRAY_2=(A B C)
VAR='B'

for NUMBERS in "${ARRAY_1[@]}"
do
        for LETTERS in "${ARRAY_2[@]}"
        do
        if      [[ $VAR == *"$LETTERS"* ]];then
                VAR='LETTERS'
                break
        fi
        if [[ $VAR == *"$NUMBERS "* ]];then
        VAR='NUMBERS'
        break

        else
        VAR='DEFAULT'

        fi
        done
done

echo "$VAR" 

the thing is that the loops dont break!
after it fines a match just keep looping and gets to default:

+ ARRAY_1=(one two three)
+ ARRAY_2=(A B C)
+ VAR=B
+ for NUMBERS in '"${ARRAY_1[@]}"'
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ B == *\A* ]]
+ [[ B == *\o\n\e* ]]
+ VAR=DEFAULT
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ DEFAULT == *\B* ]]
+ [[ DEFAULT == *\o\n\e* ]]
+ VAR=DEFAULT
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ DEFAULT == *\C* ]]
+ [[ DEFAULT == *\o\n\e* ]]
+ VAR=DEFAULT
+ for NUMBERS in '"${ARRAY_1[@]}"'
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ DEFAULT == *\A* ]]
+ VAR=LETTERS
+ break
+ for NUMBERS in '"${ARRAY_1[@]}"'
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ LETTERS == *\A* ]]
+ [[ LETTERS == *\t\h\r\e\e* ]]
+ VAR=DEFAULT
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ DEFAULT == *\B* ]]
+ [[ DEFAULT == *\t\h\r\e\e* ]]
+ VAR=DEFAULT
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ DEFAULT == *\C* ]]
+ [[ DEFAULT == *\t\h\r\e\e* ]]
+ VAR=DEFAULT
+ echo DEFAULT
DEFAULT

why ?!

i want it to work this way :

+ for NUMBERS in '"${ARRAY_1[@]}"'
+ for LETTERS in '"${ARRAY_2[@]}"'
+ [[ B == *\A* ]]
+ [[ B == *\o\n\e* ]]
+ [[ B == *\B ]]
+ [[ B == *\T\W\O* ]]
+ [[ B == *\C* ]]
+ [[ B ==  *\t\h\r\e\e* ]]

It would seem to be because you have nested loops instead of having sequential loops. And, break with no operand only breaks out of the nearest enclosing loop. To break out of two nested loops you would need to use break 2 .

Furthermore, the code you have (with nested loops) isn't testing A, B, C, one, two, three; it is testing A, B, C, one, A, B, C, two, A, B, C, three which doesn't seem necessary.

But, even if you fix that, I'm not sure why you're creating arrays and using loops. It looks like a simple case statement would be a better approach:

VAR=${1:-B}
case "$VAR" in
	(*A*|*B*|*C*)
		VAR=LETTERS;;
	(*one*|*two*|*three*)
		VAR=NUMBERS;;
	(*)	VAR=DEFAULT
esac
echo "$VAR"

If you insist on doing it with loops and arrays you would need to make the loops sequential instead of nested:

VAR=${1:-B}
found=0

ARRAY_1=(one two three)
ARRAY_2=(A B C)

for LETTERS in "${ARRAY_2[@]}"
do	if [[ $VAR == *"$LETTERS"* ]]
	then	VAR=LETTERS
		found=1
		break
        fi
done
if [[ $found == 0 ]]
then	for NUMBERS in "${ARRAY_1[@]}"
	do	if [[ $VAR == *"$LETTERS"* ]]
		then	VAR=NUMBERS
			found=1
			break
		fi
	done
fi
if [[ $found == 0 ]]
then	VAR=DEFAULT
fi

echo "$VAR"
1 Like

The reason for the undesired behaviour that you complain about is easily seen in the trace log given in your post:
$VAR 's initial contents "B" is compared exactly once - with "A". Not equal - so VAR is assigned "DEFAULT" which is compared against from now, a NEVER will be equal to any of the array elements as given.
Don't use the same variable for (initial) input values and for the results unless you know exactly what you are doing.

Don Cragun showed some way simpler methods to solve your problem - try one of those...