21

The GNU bash manual tells me

An indexed array is created automatically if any variable is assigned to using the syntax

name[subscript]=value

The subscript is treated as an arithmetic expression that must evaluate to a number. If subscript evaluates to a number less than zero, it is used as an offset from one greater than the array’s maximum index (so a subcript of -1 refers to the last element of the array).

So I figure I will give it a try and get the following result:

$ muh=(1 4 'a' 'bleh' 2)
$ echo $muh
1
$ echo ${muh[*]}
1 4 a bleh 2    # so far so good so now I'll try a negative ...
$ echo ${muh[-1]}
-bash: muh: bad array subscript  # didn't go as planned!

Did I do something wrong, or is the website wrong, or is gnu bash that different from the bash I am running under CentOS? Thanks!

bob.sacamento
  • 6,283
  • 10
  • 56
  • 115

5 Answers5

21

If you just want the last element

$ echo ${muh[*]: -1}
2

If you want next to last element

$ echo ${muh[*]: -2:1}
bleh
Zombo
  • 1
  • 62
  • 391
  • 407
  • 2
    @bob.sacamento that's because `${parameter:-word}` is a different expansion that means *substitute `word` if `parameter` is unset or null.* – kojiro Apr 19 '13 at 17:15
  • 3
    Note that `${muh[*]: -2}` gives you the last 2 elements, not the second-to-last element. (I haven't checked the manual yet to see where the [deleted] that syntax comes from. – Keith Thompson Apr 19 '13 at 17:17
  • 2
    `${muh[*]: -2}` is an example of Substring Expansion, which on arrays acts like slicing. – chepner Apr 19 '13 at 17:21
  • @kojiro Allow me to offer a heartfelt random thank you from a random person on the internet! I never would have figured out that I needed a space to index backwards. – Christopher King Feb 09 '21 at 08:19
10

According to Greg Wooledge's wiki, (which links to the bash changelog) the negative index syntax was added to bash in version 4.2 alpha.

kojiro
  • 74,557
  • 19
  • 143
  • 201
7

Bash beore 4.2 (like the default one on Macs these days) doesn't support negative subscripts. Apart from the "substring expansion" used in the accepted answer, a possibly cleaner workaround is to count the desired index from the array start within the brackets:

$ array=(one two three)
$ echo "${array[${#array[@]}-1]}"
three

With this approach, you can pack other parameter expansion operations into the term, e.g. "remove matching prefix pattern" th:

$ echo "${array[${#array[@]}-1]#th}"
ree
tlwhitec
  • 1,845
  • 16
  • 15
3

If you do man bash the section on arrays does not list this behavior. It might be something new (gnu?) in bash.

Fails for me in CentOS 6.3 (bash 4.1.2)

stark
  • 12,615
  • 3
  • 33
  • 50
3

The negative subscript works perfectly fine for me on my computer with Ubuntu 14.04 / GNU bash version 4.3.11(1) however it returns:

line 46: [-1]: bad array subscript

When I try to run the same script on 4.2.46(1). I

Ludi
  • 109
  • 1
  • 5