2

There are two shell script arrays. For demonstration purposes the first array holds two elements. These two elements will also be in the second array with the addition of another array.

The purpose of this nested for loop is to remove the matching strings from the second array. So, at the end the third element should be the only element still within the second array.

I believe that:

  • My syntax / algorithm with the for loops is skipping an iteration
  • My understanding of the method of unset array is incorrect

Please note that i want to completely remove the element and not just leave an element empty.

CODE

first_string='first'
second_string='second'
third_string='third'

strings_to_remove=()
strings_to_remove+=("$first_string")
strings_to_remove+=("$second_string")

main_array=()
main_array+=("$first_string")
main_array+=("$second_string")
main_array=("$third_string")

for i in "${main_array[@]}";    do
    echo $i
done

echo ''

for r in "${!strings_to_remove[@]}"; do
  index=''
  for i in "${!main_array[@]}"; do
    if [[ "${main_array[$i]}" = "${strings_to_remove[$r]}" ]];  then
      index=$i
    fi
  done
  if [[ $index -ne '' ]]; then
    unset main_array[$index]
    main_array=( "${main_array[@]}" )
  fi
done

echo ''

for i in "${main_array[@]}";    do
    echo "$i"
done

OUTPUT

first
second
third

first
third

The first and second element should be removed, however only the second is removed. I am unsure whether the algorithm itself or the syntax is incorrect.

1

2 Answers 2

2

The problem is here:

[[ $index -ne '' ]]

This is doing an arithmetic test, and [[ 0 -ne '' ]] return false. Change from:

index=''
...
if [[ $index -ne '' ]]; then

to:

index=-1
...
if [[ $index -ge 0 ]]; then

and you are done.

Sign up to request clarification or add additional context in comments.

3 Comments

You are correct about this issue and it needs to be addressed. However I believe the main problem is that the assignment of $index is being overwritten in the for loop. There are 2 matching strings. Since the loop does not exit when the first match is found it will always be overwritten with the second assignment. That is why the second element is removed and the first is not.
@MattClendenen The $index is being cleaned up just before the inner for-loop which search the value in the array, as expected. The first element wasn't removed because of the -ne ''. Try running your script with bash -x the_script.
Your solution is correct. Originally I was hung up by the overwriting of $index, which does happen. However, on the last iteration of the outer loop. There is only one match instead of two, so it does not overwrite and removes the remaining element. With this algorithm, the last match in the array will be removed first.
1

I would do this:

strings_to_remove=( first second )
main_array=( "${strings_to_remove[@]}" third )
unique=()

stringified_remove=$( IFS=:; echo "${strings_to_remove[*]}" )
for elem in "${main_array[@]}"; do 
    if [[ ":$stringified_remove:" != *:"$elem":* ]]; then
        unique+=( "$elem" )
    fi
done

declare -p unique
declare -a unique='([0]="third")'

In bash, within [[ double brackets ]], == and != are pattern-matching operators. So we comparing the pattern *:first:* to the stringified form of the array :first:second:. Most of the time, that's good enough. It can give you false positives if your remove array contains something like "foo:bar" and your keep array contains "foo" -- or generally, if the "join" character (I used a colon) appears in your data.

If you need more unambiguous "contains" checking, you'll have to do this:

for check in "${main_array[@]}"; do
    add=true
    for remove in "${strings_to_remove[@]}"; do              
        if [[ $check == $remove ]]; then         
            add=false
            break
        fi
    done
    $add && unique+=("$check")
done

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.