959

Is there a way to do something like PHPs $array[] = 'foo'; in bash vs doing:

array[0]='foo'
array[1]='bar'
Socowi
  • 25,550
  • 3
  • 32
  • 54
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261

7 Answers7

1855

Yes there is:

ARRAY=()
ARRAY+=('foo')
ARRAY+=('bar')

Bash Reference Manual:

In the context where an assignment statement is assigning a value to a shell variable or array index (see Arrays), the ‘+=’ operator can be used to append to or add to the variable's previous value.

Also:

When += is applied to an array variable using compound assignment (see Arrays below), the variable's value is not unset (as it is when using =), and new values are appended to the array beginning at one greater than the array's maximum index (for indexed arrays)

U. Windl
  • 3,480
  • 26
  • 54
Etienne Dechamps
  • 24,037
  • 4
  • 32
  • 31
  • 29
    This works just fine with bash 3.2.48 (OS X 10.8.2). Note that `ARRAY` is just a placeholder for an actual variable name. Even if your array indices are _not_ sequential, appending with `+=` will simply assign to the highest index + 1. – mklement0 Sep 21 '12 at 03:01
  • 6
    Is there something like that in bash version 4.2.24(1)? – Ali Ismayilov Dec 01 '12 at 12:51
  • 229
    It is important to note, that ARRAY+=('foo') is way different than ARRAY+='foo', which appends the string 'foo' to the entry with the lowest(?) key. – TheConstructor May 03 '13 at 13:17
  • 9
    According to http://wiki.bash-hackers.org/scripting/bashchanges, this syntax first appeared in version 3.1-alpha1. – David Yaw Jan 03 '14 at 21:17
  • 3
    Also, `ARRAY=( "${ARRAY[@]}" "new value" )` works. But it sucks if array is empty and you set `set -u`. Note, that this syntax will not prepend empty element if array was empty. So, this syntax will help in older bashes – socketpair Jan 05 '14 at 20:09
  • 1
    like TheConstructor said, also do not confuse ARRAY+=('foo') with "let ARRAY+=('foo')" which will just treat the first item (foo) as an integer of 0, and convert the first index into an integer type (-i), and add the value (which is zero, unless you passed a variable that was declared (ie, declare -i foo=4), then the value would have been added, even if you didn't put the $ on since 'let' evaluates just like the $(()). – osirisgothra Oct 20 '14 at 13:59
  • 3
    why isn't it working for me? `:~/tmp$ myarray=(jj) :~/tmp$ echo $myarray jj :~/tmp$ myarray+=(hh) :~/tmp$ echo $myarray jj ` – Jas Mar 12 '15 at 11:00
  • 2
    @TheConstructor: Good point; it is _always_ index `0` that is targeted when you do `ARRAY+='foo'`, irrespective of what the array's true lowest index is. – mklement0 May 13 '15 at 17:23
  • 53
    @Jas: To access the _entire_ array, you must use `${myarray[@]}` - referencing an array variable as if it were a scalar is the same as accessing its element 0; in other words: `$myarray` is the same as `${myarray[0]}`. – mklement0 May 13 '15 at 17:25
  • 3
    @socketpair The `ARRAY=( "${ARRAY[@]}" "new value" )` will re-number the index to the elements inside the `ARRAY`. That *is* a problem with sparse `ARRAY`s. –  Jul 24 '15 at 07:05
  • 2
    For anyone trying to add the output of a command to an array do not use `ARRAY+=$()`, instead use a variable to hold the command's output and then add it to the array `VAR="$()"; ARRAY+=("$VAR")`. In Bash 4 and higher `mapfile` can also be used, see @gniourf_gniourf's answer [here](http://stackoverflow.com/a/32931403/2121777). – Vito Jan 16 '17 at 18:09
  • 3
    @Vito `ARRAY+=( $(command) )` would have been fine. `readarray`/`mapfile` are much more flexible of course – sehe Jan 20 '17 at 12:22
  • @AliIsmayilov and anyone else this isn't working for, make sure you're not [adding items in a subshell](https://stackoverflow.com/questions/22691436/unable-to-add-element-to-array-in-bash). – Walf Jul 18 '17 at 08:21
  • Is it possible to add multiple elements at once? For example, `ARRAY+=('foo' 'bar')` or `ARRAY+=('foo bar')`? – Mike Shiyan Jun 20 '18 at 09:04
  • 3
    @MikeShiyan Yes, `ARRAY+=('foo' 'bar')` will work and will add the items `'foo'` and `'bar'`. `ARRAY+=('foo bar')` will add a single item `'foo bar'`. – Etienne Dechamps Jun 20 '18 at 17:43
  • Adding elements this way to Associative array will not work in GNU bash, version 4.4.23(1)-release. – CqN Mar 20 '22 at 23:50
  • All please: **Do not** ask additional question and provide answers to such questions *as comments* when they are not covered by the original question. Instead **ask a new question** then, or preferably: *Look if such a question and answer already exists*. – U. Windl Apr 01 '22 at 11:45
92

As Dumb Guy points out, it's important to note whether the array starts at zero and is sequential. Since you can make assignments to and unset non-contiguous indices ${#array[@]} is not always the next item at the end of the array.

$ array=(a b c d e f g h)
$ array[42]="i"
$ unset array[2]
$ unset array[3]
$ declare -p array     # dump the array so we can see what it contains
declare -a array='([0]="a" [1]="b" [4]="e" [5]="f" [6]="g" [7]="h" [42]="i")'
$ echo ${#array[@]}
7
$ echo ${array[${#array[@]}]}
h

Here's how to get the last index:

$ end=(${!array[@]})   # put all the indices in an array
$ end=${end[@]: -1}    # get the last one
$ echo $end
42

That illustrates how to get the last element of an array. You'll often see this:

$ echo ${array[${#array[@]} - 1]}
g

As you can see, because we're dealing with a sparse array, this isn't the last element. This works on both sparse and contiguous arrays, though:

$ echo ${array[@]: -1}
i
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 5
    Great stuff; never knew that substring-extraction syntax could be applied to arrays too; the rules, determined by trial and error, are (bash 3.2.48): `${array[@]: start[:count]}` Returns count elems. or, if not specified, all _remaining_ elems. starting at the following elem.: - If start >= 0: from the elem. whose index is >= start. - If start < 0: from the elem. whose index is (last array index + 1) - abs(start); CAVEAT: if abs(start) > (last array index + 1), a null string is returned. If count is specified, as many elements are returned, even if their indices are not contiguous from start. – mklement0 Sep 21 '12 at 05:47
  • 6
    @mklement: In Bash 4.2, you can use negative array subscripts to access elements counting from the end of the array. `${array[-1]}` – Dennis Williamson Sep 21 '12 at 15:02
  • 1
    That's good to know, thanks. OS X (as of 10.8.2) still uses 3.2.48, and http://stackoverflow.com/questions/10418616/does-darwin-macos-modify-bash tells me that, unfortunately, "Apple use quite an old version of Bash, as they don't ship code that's licensed under GPL3." – mklement0 Sep 21 '12 at 15:29
67
$ declare -a arr
$ arr=("a")
$ arr=("${arr[@]}" "new")
$ echo ${arr[@]}
a new
$ arr=("${arr[@]}" "newest")
$ echo ${arr[@]}
a new newest
Zenexer
  • 18,788
  • 9
  • 71
  • 77
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 13
    nice for bash versions that do not support the semantics of += operator explained by e-t172 – akostadinov Sep 14 '12 at 19:26
  • 17
    a good backward-compatible solution, but beware that if any of the existing elements have spaces in them, they will be split into multiple elements; use `arr=("${arr[@]}" "new")` if you have elements with spaces in them – kbolino Mar 17 '13 at 16:50
  • 2
    This can also be used to push in front of the array, which is just what I need. – Tomáš Zato Oct 08 '15 at 08:17
  • If your array has hundreds of long strings, then the `+=` variant is probably much more efficient. – U. Windl Apr 01 '22 at 11:58
36

If your array is always sequential and starts at 0, then you can do this:

array[${#array[@]}]='foo'

# gets the length of the array
${#array_name[@]}

If you inadvertently use spaces between the equal sign:

array[${#array[@]}] = 'foo'

Then you will receive an error similar to:

array_name[3]: command not found
jww
  • 97,681
  • 90
  • 411
  • 885
Dumb Guy
  • 3,336
  • 21
  • 23
  • 7
    Yes, you can, but the `+=` syntax (see @e-t172's answer) is (a) simpler, and (b) also works with arrays that are non-contiguous and/or do not start with 0. – mklement0 Sep 21 '12 at 03:06
  • Honestly this solution (for me) is working better thant the "+=", because with the latter the length is sometimes wrong (increases by two when adding one element)... so I prefer this reply! :) – Pierpaolo Cira Dec 03 '17 at 15:51
  • This also works in earlier versions of bash, before `+=` was added, eg version 2 – Zoey Hewll Jun 11 '19 at 02:09
  • 1
    This also works when your elements have spaces in them - `$arr += ($el)` seemed to split the string by space and add each of the elements. – Max Sep 17 '19 at 11:42
10

With an indexed array, you can to something like this:

declare -a a=()
a+=('foo' 'bar')
codeforester
  • 39,467
  • 16
  • 112
  • 140
Grégory Roche
  • 162
  • 2
  • 9
1

Append element:

array+=("${element}")

Append another array:

array+=("${array[@]}")

Append command output:

readarray -t output < <(command)
array+=("${output[@]}")
0

also check this out :

test_array=(1 2 3 4)
test_array+=(5)
echo "${test_array[@]}"

Result : 1 2 3 4 5

the += operator can be used to append a single element or multiple elements to an array, in instance, you can append more elements to the array like this:

test_array+=(6 7 8 9)
echo "${test_array[@]}"

Result : 1 2 3 4 5 6 7 8 9

I wanna mention that you can also remove an element from an array in Bash without specifying the index, you can use the unset command with the element's value

test_array=(1 2 3 4 5)
unset test_array[2]
echo "${test_array[@]}" 

Result : 1 2 4 5

be careful, when you remove an element from an array, the indices of the remaining elements will be adjusted accordingly. In the example above, when I remove the third element with the value 3, the fourth and fifth elements become the new third and fourth elements, respectively.

Freeman
  • 9,464
  • 7
  • 35
  • 58