Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove element from bash array by content (stored in variable) without leaving a blank slot [duplicate]

Tags:

arrays

bash

unset

I have an array list in a bash script, and a variable var. I know that $var appears in ${list[@]}, but have no easy way of determining its index. I'd like to remove it from list.

This answer achieves something very close to what I need, except that list retains an empty element where $var once was. Note, e.g.:

$ list=(one two three)
$ var="two"
$ list=( "${list[@]/$var}" )
$ echo ${list[@]}
one three
$ echo ${#list[@]}
3

The same thing happens if I use delete=( "$var" ) and replace $var for $delete in the third line. Also, doing list=( "${list[@]/$var/}" ) makes no difference either. (I'll note that, experimenting with the comment to that answer, I managed to match only whole words using list=( "${list[@]/%$var}" ), omitting the #.)

I also saw this answer proposing a nice trick to keep track of index and use unset, but that is unfeasible in my case. Finally, the same issue also appeared here, except that OP was satisfied with the result and probably didn't run into the problem empty elements create for me later on in my script, when I iterate through list. I tried to negate that problem by using expansion as follows, without any apparent effect:

for item in "${list[@]}"; do
  if [ -n ${item:+'x'} ];then
    ...
  fi
done

It's the same when I do [ ${#item} > 0 ], and I'm running out of ideas. Suggestions?

EDIT:

I have no understanding of why this happens, but @l0b0's comment made me notice something. Using the above preamble, I get:

$ for item in "${list[@]}"; do echo "Here!"; done
Here!
Here!
Here!

but:

$ for item in ${list[@]}; do echo "Here!"; done
Here!
Here!

I'm not sure I can omit the quotes in my script, though, as items are considerably more complicated there (file names and paths, both containing spaces and odd characters).

like image 900
Jonathan Y. Avatar asked May 04 '14 23:05

Jonathan Y.


People also ask

How do you delete an element from an array in bash?

To delete an element from the array, we can use the command unset. The command takes in the name of the variable in our case the array name and the index of that element.

How do you remove an element from an array array?

pop() function: This method is use to remove elements from the end of an array. shift() function: This method is use to remove elements from the start of an array. splice() function: This method is use to remove elements from the specific index of an array.

What is the syntax to remove an element from a specific array index?

Answer: Use the splice() Method You can use the splice() method to remove the item from an array at specific index in JavaScript. The syntax for removing array elements can be given with splice(startIndex, deleteCount) .


2 Answers

You can delete an element from existing array though the whole process isn't very straightforward and may appear like a hack.

#!/bin/bash

list=( "one" "two" "three" "four" "five" )
var1="two"
var2="four"

printf "%s\n" "Before:"
for (( i=0; i<${#list[@]}; i++ )); do 
    printf "%s = %s\n" "$i" "${list[i]}"; 
done

for (( i=0; i<${#list[@]}; i++ )); do 
    if [[ ${list[i]} == $var1 || ${list[i]} == $var2 ]]; then
        list=( "${list[@]:0:$i}" "${list[@]:$((i + 1))}" )
        i=$((i - 1))
    fi
done

printf "\n%s\n" "After:"
for (( i=0; i<${#list[@]}; i++ )); do 
    printf "%s = %s\n" "$i" "${list[i]}"; 
done

This script outputs:

Before:
0 = one
1 = two
2 = three
3 = four
4 = five

After:
0 = one
1 = three
2 = five

Key part of the script is:

list=( "${list[@]:0:$i}" "${list[@]:$((i + 1))}" )

Here we re-construct your existing array by specifying the index and length to remove the element from array completely and re-order the indices.

like image 199
jaypal singh Avatar answered Sep 22 '22 09:09

jaypal singh


If you want to delete the array element & shift the indices, you can use answer by l0b0 or JS웃.

However, if you don't want to shift the indices, you can use below script-let: (Particularly useful for associative arrays)

$ list=(one two three)
$ delete_me=two
$ for i in ${!list[@]};do
    if [ "${list[$i]}" == "$delete_me" ]; then
        unset list[$i]
    fi 
done

$ for i in ${!list[@]};do echo "$i = ${list[$i]}"; done
0 = one
2 = three

If you want to shift the indices to make them continuous, re-construct the array as this:

$ list=("${list[@]}")
$ for i in ${!list[@]};do echo "$i = ${list[$i]}"; done
0 = one
1 = three
like image 45
anishsane Avatar answered Sep 22 '22 09:09

anishsane