7

I am trying to remove just the first appearance of any one keyword from a bash array.

ARRAY=(foo bar and any number of keywords)
keywords=(red, rednet, rd3.0)

I remove the keyword like this: ARRAY=( ${ARRAY[@]/"$keyword"/} ) then if "red' is the first found keyword, it will strip 'red' from both keywords and return "foo bar net" instead of "foo bar rednet".

Edit: Here is example, hopefully this makes it clearer.

for keyword in ${ARRAY[@]}; do
      if [ "$keyword" = "red" ] || [ "$keyword" = "rd3.0" ] || [ "$keyword" = "rednet" ]; then
           # HERE IS TROUBLE
           ARRAY=( ${ARRAY[@]/"$keyword"/} )
           echo "ARRAY is now ${ARRAY[@]}"
           break
      fi
 done

Which if the ARRAY=(red rednet rd3.0) returns net rd3.0 instead of rednet rd3.0

If I use unset,: unset ${ARRAY["$keyword"]} bash complains if the rd3.0 is in the array: :syntax error: invalid arithmetic operator (error token is ".0") What is the safe way to unset or remove just an exact match from an array?

codeforester
  • 39,467
  • 16
  • 112
  • 140
mateor
  • 1,293
  • 1
  • 16
  • 19

2 Answers2

9

Use the unset command with the array value at index, something like this:

#!/usr/bin/env bash
ARRAY=(foo bar any red alpha number of keywords rd3.0 and)
keywords=(red, rednet, rd3.0)

index=0
for keyword in ${ARRAY[@]}; do
      if [ "$keyword" = "red" ] || [ "$keyword" = "rd3.0" ] || [ "$keyword" = "rednet" ]; then
           # HERE IS TROUBLE
           # ARRAY=( ${ARRAY[@]/"$p"/} )
           unset ARRAY[$index]
           echo "ARRAY is now: ${ARRAY[@]}"
           break
      fi
      let index++
 done
higuaro
  • 15,730
  • 4
  • 36
  • 43
  • I see. I can do that. Thanks a bunch! – mateor Jan 10 '13 at 22:20
  • 5
    It's worth noting that this doesn't actually remove the element from the array, just sets it to a blank. So, if you run `a=( a b c ); unset a[1]; echo ${a[1]}` you will not get `c` as you might expect, but whatever is in a blank element. To truly remove the element, you should follow the `unset` with something like `a=( ${a[@] }` to shift the other elements forward to take the blank ones place. – William Everett Aug 03 '14 at 16:57
  • To reset indexes after the loop: `ARRAY=("${ARRAY[@]}")` – Artfaith Apr 17 '21 at 21:05
0

First: You should use quotation marks around your keys in the arrays. This avoids problems with for example rd3.0.

Like that:

ARRAY=("foo" "bar" "and" "any" "number" "of" "keywords")
keywords=("red", "rednet", "rd3.0")

In my opinion you need to copy the array and then use a for loop to filter the keywords. Exit the for loop after the first successful filtering. After that copy it back without the empty array elements. See this short examples (paragraph 10).

More on arrays: http://tldp.org/LDP/abs/html/arrays.html (everything you will ever need)

erik
  • 2,278
  • 1
  • 23
  • 30
  • In my script, this actually is a copy of the initial array. I just shortened it here. I am using for loops as well to iterate over it, and do exit. I have followed your link and seen it many times...my question is not so much about arrays as it is handling text I think. I am getting a string like this originally "red rednet rd3.0". Should I reformat to intersperse colons you think? – mateor Jan 10 '13 at 20:58
  • By colons, do you by chance really mean quotation marks? Colon is `:`, quotation marks are `""`. – user Oct 08 '13 at 20:12
  • Michael Kjörling: To long ago, but I see no colons in my post. You seem to be right. :-) – erik Oct 08 '13 at 23:25