194

I need to remove an element from an array in bash shell. Generally I'd simply do:

array=("${(@)array:#<element to remove>}")

Unfortunately the element I want to remove is a variable so I can't use the previous command. Down here an example:

array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}

Any idea?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Alex
  • 1,967
  • 2
  • 12
  • 6

22 Answers22

281

The following works as you would like in bash and zsh:

$ array=(pluto pippo)
$ delete=pluto
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings

If need to delete more than one element:

...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
   array=("${array[@]/$del}") #Quotes when working with strings
done

Caveat

This technique actually removes prefixes matching $delete from the elements, not necessarily whole elements.

Update

To really remove an exact item, you need to walk through the array, comparing the target to each element, and using unset to delete an exact match.

array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
  for i in "${!array[@]}"; do
    if [[ ${array[i]} = $target ]]; then
      unset 'array[i]'
    fi
  done
done

Note that if you do this, and one or more elements is removed, the indices will no longer be a continuous sequence of integers.

$ declare -p array
declare -a array=([0]="pluto" [2]="bob")

The simple fact is, arrays were not designed for use as mutable data structures. They are primarily used for storing lists of items in a single variable without needing to waste a character as a delimiter (e.g., to store a list of strings which can contain whitespace).

If gaps are a problem, then you need to rebuild the array to fill the gaps:

for i in "${!array[@]}"; do
    new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
dimo414
  • 47,227
  • 18
  • 148
  • 244
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 70
    just know that: `$ array=(sun sunflower)` `$ delete=(sun)` `$ echo ${array[@]/$delete}` results in `flower` – bernstein Mar 26 '14 at 14:41
  • 1
    Good point. At least in `zsh`, `${array[@]/#%$delete}` works to restrict the match to full words. – chepner Mar 26 '14 at 14:49
  • 18
    Note that this is actually doing a substitution, so if the array is something like `(pluto1 pluto2 pippo)` then you will end up with `(1 2 pippo)`. – haridsv Sep 22 '14 at 05:22
  • @tommy.carstensen, I use a for loop to process the case when delete=(pluto pippo). If someone have a better idea, pls give do share – zhihong Oct 03 '14 at 12:36
  • 7
    Just be careful using this in a for loop because you'll end up with an empty element where the deleted element was. For sanity you could do something like `for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done` – Joel B Oct 21 '15 at 23:29
  • 2
    So how to delete only matching elements? – UmaN Feb 19 '16 at 09:09
  • 9
    Note: this may set the respective value to nothing, but the element will still be in the array. – phil294 Mar 26 '16 at 12:01
  • 1
    Regarding the caveat: it's removing `$del` wherever it matches, not necessarily just prefixes. Removing prefixes would be `${array[@]/#$del}` (at least in Bash). – Benjamin W. Jun 14 '16 at 17:23
  • @BenjaminW. I believe just `${array[@]/$del}` will delete anything prefixed. To take a previously quoted example, if `$ array=( sun flowersun )` `$ del=(sun)`, then `${array[@]/$del}` will result in `flowersun`. – Izy- Mar 08 '17 at 11:25
  • @Izy No, it results in `flower`. For `flowersun`, you'd have to use `${array[@]/#$del}`. Again, not speaking for zsh, just Bash. – Benjamin W. Mar 08 '17 at 18:02
  • 7
    In order to recreate the array, because the gaps have to disappear, the following is sufficient: `arr=("${arr[@]}")` – SOUser Jul 20 '19 at 22:51
  • At least for associated array you should use `array[$i]` instead of `array[i]` if you mean that the index is the value of variable i, not string 'i'. – jarno Aug 15 '19 at 07:09
  • @jarno That's just one problem. Another is that `array=("${new_array[@]}")` won't make a copy of the array. – chepner Aug 15 '19 at 12:01
  • Using `[[` introduces a stealth bug as the RHS is treated as a glob. Using `del=('pluto*')` will mess stuff up. – Mingye Wang Jun 11 '20 at 11:39
  • I tried just the first one. mmm does not work as expected. – KansaiRobot Aug 29 '21 at 07:12
  • Where did you locate that syntax `${array[@]/$delete}`? It's not in the [bash manual](https://www.gnu.org/software/bash/manual/html_node/Arrays.html). – Myridium Dec 23 '21 at 22:04
  • 1
    It's not specific to array expansions. It's documented under [Shell Parameter Expansions](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion). – chepner Dec 23 '21 at 22:16
  • While it's unfortunate that you can't use both anchors at once in the replacement expansion (i.e. this doesn't work: `array=( "${array[@]/#%$delete}" )`, you could just use `printf`,`grep`, & xargs: `array=( $(printf "%s\n" "${array[@]}" | grep -vow "${delete[@]/#/-e}" | xargs) )`... Maybe I'll add an additional answer for clarity... – Joshua Skrzypek Aug 18 '22 at 20:46
  • Ah @Niklas Holm beat me to it – Joshua Skrzypek Aug 18 '22 at 20:52
45

You could build up a new array without the undesired element, then assign it back to the old array. This works in bash:

array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
    [[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array

This yields:

echo "${array[@]}"
pippo
Steve Kehlet
  • 6,156
  • 5
  • 39
  • 40
25

This is the most direct way to unset a value if you know it's position.

$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
signull
  • 359
  • 3
  • 2
  • 6
    Try `echo ${array[1]}`, you will get null string. And to get `three` you need to do `echo ${array[2]}`. So `unset` is not the right mechanism to remove an element in bash array. – rashok Apr 03 '18 at 09:23
  • @rashok, no, `${array[1]+x}` is null string, so `array[1]` is unset. `unset` does not change the indexes of the remaining elements. Quoting the argument for unset is not needed. The way to destroy an array element is described in [Bash manual](https://www.gnu.org/software/bash/manual/html_node/Arrays.html). – jarno Aug 15 '19 at 05:44
  • 1
    @rashok I don't see why not. You cannot assume that `${array[1]}` exists just because the size is 2. If you want the indices, check `${!array[@]}`. – Daniel C. Sobral Feb 22 '20 at 21:42
  • 3
    you can update/refresh the index by: `array=(${array[*]})` – FullStack Alex Jan 05 '22 at 18:42
  • Quoting the argument for unset _is_ needed if the index is a variable that needs expanding. (This isn't an array-specific thing, just dealing with order of expansions.) I realize that's not part of the given example, however; just mentioning it for future readers. – Ti Strga Apr 19 '22 at 14:59
  • @FullStackAlex That breaks members that contain shell meta-characters or separators present in `$IFS`. The generic way to reindex a sparse, numerically-indexed array, and preserve the original values, is `array=("${array[@]}")` – Walf Aug 01 '23 at 05:03
10

This answer is specific to the case of deleting multiple values from large arrays, where performance is important.

The most voted solutions are (1) pattern substitution on an array, or (2) iterating over the array elements. The first is fast, but can only deal with elements that have distinct prefix, the second has O(n*k), n=array size, k=elements to remove. Associative array are relative new feature, and might not have been common when the question was originally posted.

For the exact match case, with large n and k, possible to improve performance from O(nk) to O(n+klog(k)). In practice, O(n) assuming k much lower than n. Most of the speed up is based on using associative array to identify items to be removed.

Performance (n-array size, k-values to delete). Performance measure seconds of user time

   N     K     New(seconds) Current(seconds)  Speedup
 1000   10     0.005        0.033             6X
10000   10     0.070        0.348             5X
10000   20     0.070        0.656             9X
10000    1     0.043        0.050             -7%

As expected, the current solution is linear to N*K, and the fast solution is practically linear to K, with much lower constant. The fast solution is slightly slower vs the current solution when k=1, due to additional setup.

The 'Fast' solution: array=list of input, delete=list of values to remove.

        declare -A delk
        for del in "${delete[@]}" ; do delk[$del]=1 ; done
                # Tag items to remove, based on
        for k in "${!array[@]}" ; do
                [ "${delk[${array[$k]}]-}" ] && unset 'array[k]'
        done
                # Compaction
        array=("${array[@]}")

Benchmarked against current solution, from the most-voted answer.

    for target in "${delete[@]}"; do
        for i in "${!array[@]}"; do
            if [[ ${array[i]} = $target ]]; then
                unset 'array[i]'
            fi
        done
    done
    array=("${array[@]}")
dash-o
  • 13,723
  • 1
  • 10
  • 37
8

Here's a one-line solution with mapfile:

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")

Example:

$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "^Claire\nSmith$")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 5 Contents: Adam Bob David Eve Fred

This method allows for great flexibility by modifying/exchanging the grep command and doesn't leave any empty strings in the array.

Niklas Holm
  • 949
  • 9
  • 14
  • @Socowi You're incorrect, at least on bash 4.4.19. `-d $'\0'` works perfectly fine while just `-d` without the argument does not. – Niklas Holm Mar 27 '19 at 09:13
  • Ah yes, I mixed it up. Sorry. What I meant was: `-d $'\0'` is the same as`-d $'\0 something'` or just `-d ''`. – Socowi Mar 27 '19 at 09:27
  • Doesn't hurt to use `$'\0'` for clarity though – Niklas Holm Mar 28 '19 at 10:05
  • First `-P` is non-POSIX (and not supported by BSD grep), consider `-E` for EREs instead. Second, while this is in some ways more flexible as is, the following supports the case where you have another array of items to delete as in other answers: `mapfile -d $'\0' -t arr2 < <(printf '%s\0' "${arr[@]}" | grep -Ezvw "${delete[@]/#/-e}")` – Joshua Skrzypek Aug 18 '22 at 21:04
5

Partial answer only

To delete the first item in the array

unset 'array[0]'

To delete the last item in the array

unset 'array[-1]'
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
consideRatio
  • 1,091
  • 13
  • 19
  • @gniourf_gniourf there is no need to use quotes for the argument of `unset`. – jarno Aug 15 '19 at 07:06
  • 7
    @jarno: these quotes MUST be used: if you have a file named `array0` in the current directory, then since `array[0]` is glob, it will first be expanded to `array0` before the unset command. – gniourf_gniourf Aug 15 '19 at 08:06
  • @gniourf_gniourf you are correct. This should be corrected in [Bash Reference Manual](https://www.gnu.org/software/bash/manual/html_node/Arrays.html) that currently says "unset name[subscript] destroys the array element at index subscript". – jarno Aug 15 '19 at 08:24
3

Here's a (probably very bash-specific) little function involving bash variable indirection and unset; it's a general solution that does not involve text substitution or discarding empty elements and has no problems with quoting/whitespace etc.

delete_ary_elmt() {
  local word=$1      # the element to search for & delete
  local aryref="$2[@]" # a necessary step since '${!$2[@]}' is a syntax error
  local arycopy=("${!aryref}") # create a copy of the input array
  local status=1
  for (( i = ${#arycopy[@]} - 1; i >= 0; i-- )); do # iterate over indices backwards
    elmt=${arycopy[$i]}
    [[ $elmt == $word ]] && unset "$2[$i]" && status=0 # unset matching elmts in orig. ary
  done
  return $status # return 0 if something was deleted; 1 if not
}

array=(a 0 0 b 0 0 0 c 0 d e 0 0 0)
delete_ary_elmt 0 array
for e in "${array[@]}"; do
  echo "$e"
done

# prints "a" "b" "c" "d" in lines

Use it like delete_ary_elmt ELEMENT ARRAYNAME without any $ sigil. Switch the == $word for == $word* for prefix matches; use ${elmt,,} == ${word,,} for case-insensitive matches; etc., whatever bash [[ supports.

It works by determining the indices of the input array and iterating over them backwards (so deleting elements doesn't screw up iteration order). To get the indices you need to access the input array by name, which can be done via bash variable indirection x=1; varname=x; echo ${!varname} # prints "1".

You can't access arrays by name like aryname=a; echo "${$aryname[@]}, this gives you an error. You can't do aryname=a; echo "${!aryname[@]}", this gives you the indices of the variable aryname (although it is not an array). What DOES work is aryref="a[@]"; echo "${!aryref}", which will print the elements of the array a, preserving shell-word quoting and whitespace exactly like echo "${a[@]}". But this only works for printing the elements of an array, not for printing its length or indices (aryref="!a[@]" or aryref="#a[@]" or "${!!aryref}" or "${#!aryref}", they all fail).

So I copy the original array by its name via bash indirection and get the indices from the copy. To iterate over the indices in reverse I use a C-style for loop. I could also do it by accessing the indices via ${!arycopy[@]} and reversing them with tac, which is a cat that turns around the input line order.

A function solution without variable indirection would probably have to involve eval, which may or may not be safe to use in that situation (I can't tell).

S.V.P.
  • 39
  • 1
  • 1
    This almost works nicely, however it doesn't redeclare the initial array passed into the function, so while that initial array has its values missing, it also has its indexes messed up. What this mean is that the next call you make to delete_ary_elmt on the same array will not work (or will remove the wrong things). For instance, after what you have pasted, try running `delete_ary_elmt "d" array` and then re-printing the array. You will see that the wrong element gets removed. Removing the last element will also then never work. – Scott Feb 26 '18 at 19:26
  • how do we fix the issue pointed out by Scott? I encountered the issue in succeeding calls to the delete function. – hermit.crab Apr 18 '23 at 03:10
2

To expand on the above answers, the following can be used to remove multiple elements from an array, without partial matching:

ARRAY=(one two onetwo three four threefour "one six")
TO_REMOVE=(one four)

TEMP_ARRAY=()
for pkg in "${ARRAY[@]}"; do
    for remove in "${TO_REMOVE[@]}"; do
        KEEP=true
        if [[ ${pkg} == ${remove} ]]; then
            KEEP=false
            break
        fi
    done
    if ${KEEP}; then
        TEMP_ARRAY+=(${pkg})
    fi
done
ARRAY=("${TEMP_ARRAY[@]}")
unset TEMP_ARRAY

This will result in an array containing: (two onetwo three threefour "one six")

Dylan
  • 482
  • 3
  • 9
2

Using unset

To remove an element at particular index, we can use unset and then do copy to another array. Only just unset is not required in this case. Because unset does not remove the element it just sets null string to the particular index in array.

declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in "${arr[@]}"
do
    arr2[$i]=$element
    ((++i))
done
echo "${arr[@]}"
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"

Output is

aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd

Using :<idx>

We can remove some set of elements using :<idx> also. For example if we want to remove 1st element we can use :1 as mentioned below.

declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"

Output is

bb cc dd ee
1st val is cc, 2nd val is dd
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
rashok
  • 12,790
  • 16
  • 88
  • 100
0

http://wiki.bash-hackers.org/syntax/pe#substring_removal

${PARAMETER#PATTERN} # remove from beginning

${PARAMETER##PATTERN} # remove from the beginning, greedy match

${PARAMETER%PATTERN} # remove from the end

${PARAMETER%%PATTERN} # remove from the end, greedy match

In order to do a full remove element, you have to do an unset command with an if statement. If you don't care about removing prefixes from other variables or about supporting whitespace in the array, then you can just drop the quotes and forget about for loops.

See example below for a few different ways to clean up an array.

options=("foo" "bar" "foo" "foobar" "foo bar" "bars" "bar")

# remove bar from the start of each element
options=("${options[@]/#"bar"}")
# options=("foo" "" "foo" "foobar" "foo bar" "s" "")

# remove the complete string "foo" in a for loop
count=${#options[@]}
for ((i = 0; i < count; i++)); do
   if [ "${options[i]}" = "foo" ] ; then
      unset 'options[i]'
   fi
done
# options=(  ""   "foobar" "foo bar" "s" "")

# remove empty options
# note the count variable can't be recalculated easily on a sparse array
for ((i = 0; i < count; i++)); do
   # echo "Element $i: '${options[i]}'"
   if [ -z "${options[i]}" ] ; then
      unset 'options[i]'
   fi
done
# options=("foobar" "foo bar" "s")

# list them with select
echo "Choose an option:"
PS3='Option? '
select i in "${options[@]}" Quit
 do
    case $i in 
       Quit) break ;;
       *) echo "You selected \"$i\"" ;;
    esac
 done

Output

Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option? 

Hope that helps.

Community
  • 1
  • 1
phyatt
  • 18,472
  • 5
  • 61
  • 80
0

There is also this syntax, e.g. if you want to delete the 2nd element :

array=("${array[@]:0:1}" "${array[@]:2}")

which is in fact the concatenation of 2 tabs. The first from the index 0 to the index 1 (exclusive) and the 2nd from the index 2 to the end.

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
OphyTe
  • 68
  • 9
0

If gaps are a problem, then you can make a new array with just 2 lines:

arr=(pluto pippo toby)
    unset arr[1]                                   #1ª line
Below are the values, ​​and their indices without continuity:
     for i in "${!arr[@]}"; do
     printf '${arr[%s]}=%s\n' "$i" "${arr[$i]}" >> borramepen;
     done

Output: ${arr[0]}=pluto ${arr[2]}=toby

Indices: 0 and 2

 To recover the continuity of the indices, it is necessary to do:
    IFS=' ' read -ra arr <<< "$(echo ${arr[@]})"   #2ª line

 Then, the output will be:
    for i in "${!arr[@]}"; do
    printf '${arr[%s]}=%s\n' "$i" "${arr[$i]}"
    done

Output: ${AR[0]}=pluto ${AR[1]}=toby

Indices: 0 and 1

clarke
  • 21
  • 2
0

The easiest way, with the only 2 shortest possible lines, to remove an array element and revive after that.

arr=(pluto pippo toby)

#check who is offside

echo ${arr[@]}
echo ${!arr[*]}

#remove the element

unset arr[1] (the shortest line 1) 

#check who is offside

echo ${arr[@]}
echo ${!arr[*]}

#make a revival (the shortest line 2)

arr=(${arr[@]})

#check that no one is offside now

echo ${arr[@]}
echo ${!arr[*]}
clarke
  • 21
  • 2
0

Similar to Niklas' answer but gives a simple, exact difference of arrays by leveraging comm:

readarray -d $'\0' -t result < <(comm -23 -z <(printf '%s\0' "${array[@]}") <(printf '%s\0' "${delete[@]}"))

This deletes literal values as per OP's question, and works on values that contain any characters (except the null byte) without breaking them, e.g.:

array=(Adam Bob Claire$'\n'Smith David Eve Fred "T'Pol")
delete=(Claire$'\n'Smith Fred)

#inspect original
echo "Size: ${#array[*]}, Contents: ${array[*]}"

# the magic. readarray is a more descriptive alias of mapfile
readarray -d $'\0' -t result < <(comm -23 -z \
    <(printf '%s\0' "${array[@]}") \
    <(printf '%s\0' "${delete[@]}") )

# inspect result
echo "Size: ${#result[*]}, Contents: ${result[*]}"

Produces this:

Size: 7, Contents: Adam Bob Claire
Smith David Eve Fred T'Pol
Size: 5, Contents: Adam Bob David Eve T'Pol

The parameters to comm are simple: -23 means suppress column 2 & 3 to show only column 1, items unique to the first array; and -z tells it to use null-separated i/o, to protect values.

Walf
  • 8,535
  • 2
  • 44
  • 59
-1

In ZSH this is dead easy (note this uses more bash compatible syntax than necessary where possible for ease of understanding):

# I always include an edge case to make sure each element
# is not being word split.
start=(one two three 'four 4' five)
work=(${(@)start})

idx=2
val=${work[idx]}

# How to remove a single element easily.
# Also works for associative arrays (at least in zsh)
work[$idx]=()

echo "Array size went down by one: "
[[ $#work -eq $(($#start - 1)) ]] && echo "OK"

echo "Array item "$val" is now gone: "
[[ -z ${work[(r)$val]} ]] && echo OK

echo "Array contents are as expected: "
wanted=("${start[@]:0:1}" "${start[@]:2}")
[[ "${(j.:.)wanted[@]}" == "${(j.:.)work[@]}" ]] && echo "OK"

echo "-- array contents: start --"
print -l -r -- "-- $#start elements" ${(@)start}
echo "-- array contents: work --"
print -l -r -- "-- $#work elements" "${work[@]}"

Results:

Array size went down by one:
OK
Array item two is now gone:
OK
Array contents are as expected:
OK
-- array contents: start --
-- 5 elements
one
two
three
four 4
five
-- array contents: work --
-- 4 elements
one
three
four 4
five
trevorj
  • 80
  • 3
  • Sorry, just tried. It did not work in zsh for an assoziative array – Falk Dec 08 '18 at 22:43
  • It works just fine, I just tested it (again). Things not working for you? Please explain what did not work exactly in as much detail as you can. What ZSH version are you using? – trevorj Apr 02 '19 at 02:51
-1

What I do is:

array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"

BAM, that item is removed.

zx485
  • 28,498
  • 28
  • 50
  • 59
garfield
  • 7
  • 1
-1

This is a quick-and-dirty solution that will work in simple cases but will break if (a) there are regex special characters in $delete, or (b) there are any spaces at all in any items. Starting with:

array+=(pluto)
array+=(pippo)
delete=(pluto)

Delete all entries exactly matching $delete:

array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)

resulting in echo $array -> pippo, and making sure it's an array: echo $array[1] -> pippo

fmt is a little obscure: fmt -1 wraps at the first column (to put each item on its own line. That's where the problem arises with items in spaces.) fmt -999999 unwraps it back to one line, putting back the spaces between items. There are other ways to do that, such as xargs.

Addendum: If you want to delete just the first match, use sed, as described here:

array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`)
Joshua Goldberg
  • 5,059
  • 2
  • 34
  • 39
-1

Actually, I just noticed that the shell syntax somewhat has a behavior built-in that allows for easy reconstruction of the array when, as posed in the question, an item should be removed.

# let's set up an array of items to consume:
x=()
for (( i=0; i<10; i++ )); do
    x+=("$i")
done

# here, we consume that array:
while (( ${#x[@]} )); do
    i=$(( $RANDOM % ${#x[@]} ))
    echo "${x[i]} / ${x[@]}"
    x=("${x[@]:0:i}" "${x[@]:i+1}")
done

Notice how we constructed the array using bash's x+=() syntax?

You could actually add more than one item with that, the content of a whole other array at once.

mar77i
  • 90
  • 6
-1

To avoid conflicts with array index using unset - see https://stackoverflow.com/a/49626928/3223785 and https://stackoverflow.com/a/47798640/3223785 for more information - reassign the array to itself: ARRAY_VAR=(${ARRAY_VAR[@]}).

#!/bin/bash

ARRAY_VAR=(0 1 2 3 4 5 6 7 8 9)
unset ARRAY_VAR[5]
unset ARRAY_VAR[4]
ARRAY_VAR=(${ARRAY_VAR[@]})
echo ${ARRAY_VAR[@]}
A_LENGTH=${#ARRAY_VAR[*]}
for (( i=0; i<=$(( $A_LENGTH -1 )); i++ )) ; do
    echo ""
    echo "INDEX - $i"
    echo "VALUE - ${ARRAY_VAR[$i]}"
done

exit 0

[Ref.: https://tecadmin.net/working-with-array-bash-script/ ]

Eduardo Lucio
  • 1,771
  • 2
  • 25
  • 43
-2

POSIX shell script does not have arrays.

So most probably you are using a specific dialect such as bash, korn shells or zsh.

Therefore, your question as of now cannot be answered.

Maybe this works for you:

unset array[$delete]
Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
  • 2
    Hi, I'm using bash shell atm. And "$delete" is not the position of the element but the string itself. So I don't think "unset" will work – Alex May 31 '13 at 16:07
-2

How about something like:

array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
-3
#/bin/bash

echo "# define array with six elements"
arr=(zero one two three 'four 4' five)

echo "# unset by index: 0"
unset -v 'arr[0]'
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

arr_delete_by_content() { # value to delete
        for i in ${!arr[*]}; do
                [ "${arr[$i]}" = "$1" ] && unset -v 'arr[$i]'
        done
        }

echo "# unset in global variable where value: three"
arr_delete_by_content three
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

echo "# rearrange indices"
arr=( "${arr[@]}" )
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

delete_value() { # value arrayelements..., returns array decl.
        local e val=$1; new=(); shift
        for e in "${@}"; do [ "$val" != "$e" ] && new+=("$e"); done
        declare -p new|sed 's,^[^=]*=,,'
        }

echo "# new array without value: two"
declare -a arr="$(delete_value two "${arr[@]}")"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

delete_values() { # arraydecl values..., returns array decl. (keeps indices)
        declare -a arr="$1"; local i v; shift
        for v in "${@}"; do 
                for i in ${!arr[*]}; do
                        [ "$v" = "${arr[$i]}" ] && unset -v 'arr[$i]'
                done
        done
        declare -p arr|sed 's,^[^=]*=,,'
        }
echo "# new array without values: one five (keep indices)"
declare -a arr="$(delete_values "$(declare -p arr|sed 's,^[^=]*=,,')" one five)"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

# new array without multiple values and rearranged indices is left to the reader