How to Delete an Element from a Shell Array

This post originally started as an exploration of the difference between * and @ in shell arrays. But as I wrote it, I realized the issue I had encountered wasn’t actually about * vs @ — it was a subtle problem with the way I was removing elements from an array. In this post, I’ll walk through how to delete an element from a shell array.

Delete by Index

Start with the following array:

1
2
3
#!/bin/bash

array1=(a b c d a b c d)

To delete the second element (index 1):

1
2
3
4
5
6
#!/bin/bash

array1=(a b c d a b c d)
unset array1[1]

echo ${array1[*]}

Output:

1
a c d a b c d

Delete by Value

Sometimes you want to remove a specific value from the array — for example, remove all occurrences of b. The code is:

1
2
3
4
5
6
#!/bin/bash

array1=(a b c d a b c d)
array1=( ${array1[*]/b} )

echo ${array1[*]}

Result:

1
a c d a c d

Replacing * with @ in array1=( ${array1[*]/b} ) produces the same output.

Replacing ${array1[*]/b} with "${array1[*]/b}"

To better observe the behavior, add a loop to print each element of the array after removing b.
Code:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

array1=(a b c d a b c d)
array1=( "${array1[*]/b}" )

echo ${array1[*]}

for value in ${array1[@]}
do
echo $value
done

Result:

1
2
3
4
5
6
7
a c d a c d
a
c
d
a
c
d

Replacing @ with * in the for loop

The result is the same as above.

Changing the for loop to for value in "${array1[@]}" or for value in "${array1[*]}"

Code:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

array1=(a b c d a b c d)
array1=( "${array1[*]/b}" )

echo ${array1[*]}

for value in "${array1[@]}"
do
echo $value
done

Result:

1
2
a c d a c d
a c d a c d

Replacing ${array1[*]/b} with "${array1[@]/b}"

Code:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

array1=(a b c d a b c d)
array1=( "${array1[@]/b}" )

echo ${array1[*]}

for value in ${array1[@]}
do
echo $value
done

Result:

1
2
3
4
5
6
7
a c d a c d
a
c
d
a
c
d

Replacing @ with * in the for loop

The result is the same as above.

Changing the for loop to for value in "${array1[@]}"

Code:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

array1=(a b c d a b c d)
array1=( "${array1[@]/b}" )

echo ${array1[*]}

for value in "${array1[@]}"
do
echo $value
done

Result:

1
2
3
4
5
6
7
8
9
a c d a c d
a

c
d
a

c
d

Changing the for loop to for value in "${array1[*]}"

Code:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

array1=(a b c d a b c d)
array1=( "${array1[@]/b}" )

echo ${array1[*]}

for value in "${array1[*]}"
do
echo $value
done

Result:

1
2
a c d a c d
a c d a c d

Delete Elements Containing a Substring

Using a different array for this test:

1
2
3
4
5
6
#!/bin/bash

array1=(abc bcd cdf dfg)
array1=( "${array1[@]/*b*}" )

echo ${array1[*]}

Result:

1
cdf dfg

This approach, like the ones above, is essentially string substitution/pattern matching.

The Difference Between * and @

These subscripts differ only when the word appears within double quotes. 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, and ${name[@]} expands each element of name to a separate word.