0

I was trying to check if an array contained a value and I found Check if a Bash array contains a value

It did not work for me, and it was because I decided to remove the spaces surrounding the quotes, like this:

words=(aa bb cc)
[[ " ${words[@]} " =~ " a " ]] && echo "YES" || echo "NO"; # This is the real answer, and it works
------
[[ "${words[@]}" =~ "a" ]] && echo "YES" || echo "NO"; # This does not work. Why???
  1. Whats the difference when you surround them with spaces or when you dont?

And for my curiosity. In the previous question I mentioned, some answers/comments go with:

${array[*]}, and others with ${array[@]}

  1. Do they both "iterate" through the loop in the same way?
Mayday
  • 4,680
  • 5
  • 24
  • 58
  • 1
    `Do they both "iterate" through the loop in the same way?` No, that's why `*` should be used here. `This does not work. Why???` What do you "not work"? It executes with no errors on my terminal. Does your shell crashes? What happens? What does not happen? What did you expect to happen? `Do they both "iterate" through the loop in the same way?` Please one question per question. I am _sure_ there is already a question how `@` differs from `*` and I am sure it is explained in bash manual. – KamilCuk Jul 18 '22 at 07:07
  • @KamilCuk It does not work, since I expect it to find only "aa", or "bb", or "cc", in the array, but in the first case, it says "a" is contained on ("aa", "bb", "cc") – Mayday Jul 18 '22 at 07:50
  • 1
    `words=(aa bb cc); [[ "${words[@]}" =~ "a" ]] && echo "YES" || echo "NO";` outputs `YES` for me. Och, you mean it should output `NO`, because there is no array element with exactly `a`? Ok, now I get you. Doesn't the answer https://stackoverflow.com/questions/3685970/check-if-a-bash-array-contains-a-value explain what is happening? What is unclear to you? – KamilCuk Jul 18 '22 at 07:52
  • There is no iteration in your code. As for the quotes, the double quotes to the right of the regexp operator tells bash to **not** treat the quoted part as a regexp. For instance, `[[ x =~ . ]]` is true while `[[ x =~ "." ]]` is false. – user1934428 Jul 18 '22 at 09:16
  • `" ${words[@]} "` normally expands to three words: `" aa"`, `"bb"`, and `"cc "`. But, _word splitting_ isn't performed inside the `[[...]]` construct. So, it expands to a single word, `" aa bb cc "`, and is equivalent to `" ${words[*]} "`, _in this context_. – M. Nejat Aydin Jul 18 '22 at 09:57

1 Answers1

1

[[ string =~ regex ]] checks if regex matches any substring of string. That operator does not iterate over your array entries as for entry in "${words[@]}" would do. It cannot even handle arrays.

[[ " ${words[@]} " =~ " a " ]] is only a hack for finding array elements. The array is converted into a single string where all array entries are surrounded by spaces. Then, we search for an element using a string search. This only works reliably, if the array entries themselves do not contain any spaces.

For the difference between ${array[*]} and ${array[@]} see bash's manual:

If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable [= a space by default], and ${name[@]} expands each element of name to a separate word.

So in this case, * would be the logical way to write [[ " ${words[*]} " =~ " a " ]], because it is equivalent to [[ " aa bb cc " =~ " a " ]].
That @ works here too is somewhat strange, because [[ " aa" "bb" "cc " =~ " a " ]] gives a syntax error. Bash tolerating the misused @ in [[ is probably an undocumented "feature" related to the (documented) disabled word-splitting inside [[. For the basic POSIX test command [ the difference between * and @ works as expected, meaning a=("x" "=" "y"); [ "${a[@]}" ] fails because x != y.

Socowi
  • 25,550
  • 3
  • 32
  • 54