Arrays in BASH

Arrays in BASH

Created:12 Feb 2017 14:48:27 , in  Host development

Once one is somewhat familiar with basic number and string handling rules in BASH, it is time to move on to learning about a container for these. BASH provides one such a convenient container in the form of array.

This article is about the number indexed arrays. If you've met the concept somewhere else before you should have no problem learning basic rules here now. After all, arrays in BASH work in a very similar manner to how they work in other programming languages, what differs is syntax. For these of you who haven't dealt with arrays so far there is an introductory section on the topic below.

This article has two parts about it. First of them is above-mentioned introductory section, the other a fairly large BASH script, which utilizes arrays extensively. The code the script consists of has been splat into a few smaller chunks. Each of them is followed by an explanatory comment.

BASH array operations and syntax

Suppose you have four strings: a b c d, and you want to place them all in array, here is how you could achieve this:

arr=(a b c d) 

To take a look at what is in your array you use declare built-in:

declare -p arr

If you need to get all the values stored in array, you can do that in two ways:

${arr[@]} or ${arr[*]}

You can obtain all the keys in array:


Frequently the number of items in the array is needed, here is the syntax for it:


Obtaining particular value from array can be done as follows:


Using 0 as index results in first value being returned.

Like in other programming languages, arrays in BASH are zero-based.

To add another item to the end of array you use syntax:

arr+=("new item")

Removing array values is done using unset built-n, you also need an index of the value you want to remove:

unset arr[3]

You can loop over array values like this:

for item in "${arr[@]}"; do echo $item; done;

You can also loop over values starting from the last one:

for (( i=${#arr[@]};i>0; i-- )); do echo "${arr[$i - 1]}";done

It is possible to join all items in array with a separator:

str=$(IFS=,; echo "${arr[*]}")

To break up a string and place resulting parts in array you use IFS variable:

IFS=/ read -a arr <<< "a/b/c/d/e"

IFS stores separator(s) to be used for splitting, in the above case it is "/"

One can obtain a slice of the original array using parameter expansions.


Parameter expansions can also be used to update all elements of array in one go. For example, the line of code below adds + sign to the beginning of each string stored in array arr.

arr=( "${arr[@]/#/+}" )

Values in array can be updated in various other ways using parameter expansions. Check BASH manual for more information on this.

BASH array operations and syntax table

Array operation Syntax
Creating array

arr=(a b c d)

declare -p arr
Array values

${arr[@]} or ${arr[*]}
Array keys

Number of items

Single value

Adding new value

arr+=("new item")
Removing a value

unset arr[index]
Looping using for loop

for item in "${arr[@]}"; do echo $item; done;
Looping from the end

for (( i=${#arr[@]};i>0; i-- )); do echo "${arr[$i - 1]}";done
Joining items in array with ,

str=$(IFS=,; echo "${arr[*]}")
Breaking up string

IFS=/ read -a arr <<< "a/b/c/d/e"

Adding prefix to each value

( "${arr[@]/#/some}" )

Next version name of computer program

Imagine you have written a computer program, possibly a website, and you store its consecutive versions in a directory. Each version of the program has its own sub-directory with unique name in this directory. The version sub-directories could be named as follows: 0.1, 0.1.1, 0.5, 1.0, 1.2.1 etc. .

At some point you come to the conclusion you no longer want to deal with these version names by hand. Instead you would like a program that simply gives the next version name if provided no extra instruction (e.g next version of 1.1 would be 1.2) or finds correct version when clued (e.g. you might need version 1.3.4 to be 1.4.0 ).

Here is a program that finds next version name, it is appropriately called It consists of a few global variables and 4 functions (each function has a short description above its name). The makes extensive use of arrays. - part 1

#!/usr/bin/env bash

# Program name:
# Author: Sylwester Wojnowski
# WWW:

#directory in which consecutive version sub-directories are stored
# normalize version directory names. 
#If set to 1, all version directories will be renamed to have the same length

########## Private ############ 

# set by find versions
# set by find_latest_version

# find_versions() finds directories with names like 0.45, 1.1 1.11.5 or in VERSIONS_DIR 
# and places them in global VERSIONS array 
  declare -n ITEMS=VERSIONS
  local REGEX='^([0-9]+\.)+[0-9]+$'
  local ASSORTED=
  local VERSIONS_DIR="$1"

  # make sure some directory is given 
  [[ -z "$VERSIONS_DIR" ]] && { 
    echo "Versions directory not given."
    exit 1

  # check, you can enter 
  # suppress output from command cd
  cd "$VERSIONS_DIR" &> /dev/null

  # check status of the last command
  [[ "$?" != "0" ]] && {
    echo "Versions directory does not exist. Exiting ..."
    exit 1

  # store all directory names in an array
  ASSORTED=( * )     

  # filter version directories out from the rest and store them in ITEMS array
  for item in "${ASSORTED[@]}"; do 
    [[ $item =~ $REGEX ]] && {

  # exit if no version found
  (( ${#ITEMS[@]} == 0 )) && {
    printf "Directory $VERSIONS_DIR hosts no versions at the moment. Add first, perhaps something like 0.0.1 ..."

  # get back to the previous working directory
  cd "$OLDPWD"

Comments on the syntax used in the above piece of code

Create new empty VERSIONS array:


Display values in array VERSIONS (see my article on declare and env for more details):

declare -p VERSIONS

Array ITEMS references global array versions. (see my article on env an bash declare for more details):


Populate array ASSORTED with files and directories from the current working directory:


Reference all items ( @ ) of ASSORTED at the same time:


If you need to reference a single item from an array you would use a numeric value in place of @:


Iterate through array ASSORTED and assign items that pass regular expressions based test to array ITEMS.

for item in "${ASSORTED[@]}"; do 
    [[ $item =~ $REGEX ]] && {

Find number of items currently in array ITEMS:

${#ITEMS[@]} part 2

# standardize_versions() updates VERSIONS array names in such a way that each version has length of the longest version name
# e.g. if the longest version name is and current version is 1.1, 1.1 becomes
# Original sub-directory names remain the same unless NORMALIZE global variable has value 1.
  local LONGEST=0
  # find longest version name
  for version in "${VERSIONS[@]}"; do
    local vlen=
    IFS=. read -r -a vlen <<< "$version" 
    (( ${#vlen[@]} > $LONGEST  )) && {
  # extend shorter version names with '.0'
  for (( i=0 ; $i < ${#VERSIONS[@]}; i++ )); do
    local orig_version=${VERSIONS[$i]} 
    local version=${VERSIONS[$i]}
    IFS=. read -r -a vlen <<< "$version"
    while (( ${#vlen[@]} < $LONGEST )); do
      IFS=. read -r -a vlen <<< "$version"
    (( $NORMALIZE == 1 )) && [[ "$version" != "$orig_version" ]] && {
      cd "$VERSIONS_DIR" 
      mv "$orig_version" "$version"
      cd "$OLDPWD" 

Comments on the syntax used in the above piece of code:

Split variable $version (string) using dot and place resulting values in array vlen:

IFS=. read -r -a vlen <<< "$version"

Iterate over array VERSIONS using for loop:

for (( i=0 ; $i < ${#VERSIONS[@]}; i++ )); do

Set variable orig_version to value ${VERSIONS[$i]}, where VERSIONS is an array and $i is a number ( holds particular array index )

orig_version=${VERSIONS[$i]} part 3

# find_last_version() finds the highest version ( last created might not be the most recent )
# is higher than and is higher

  declare -n last_version=LAST_VERSION
  local last_version_a=
  local next_version_a=
  for version in "${VERSIONS[@]}"; do
    # set first version name
    [[ -z "$last_version" ]] && {
      IFS=. read -r -a last_version_a <<< "$version"  
    #  compare version names here  
    } || {
      IFS=. read -r -a next_version_a <<< "$version"
      # now compare two arrays
      for (( i=0; $i < ${#last_version_a[@]}; i++ )); do
        (( ${last_version_a[$i]} > ${next_version_a[$i]} )) && { break; }
        (( ${last_version_a[$i]} < ${next_version_a[$i]} )) && { 
          IFS=. read -r -a last_version_a <<< "$version"
        (( ${last_version_a[$i]} == ${next_version_a[$i]} )) && {

Compare numeric values ${last_version_a[$i]} and ${next_version_a[$i]}. Last_version_a and next_version_a are arrays and ${last_version_a[$i]} and # ${next_version_a[$i]} are values with index $i (number)

(( ${last_version_a[$i]} > ${next_version_a[$i]} ))
(( ${last_version_a[$i]} < ${next_version_a[$i]} ))
(( ${last_version_a[$i]} == ${next_version_a[$i]} ))

find_next_version_name part 4

# find_next_version_name() generates next version name.
# If invoked with no argument, it updates the rightmost part of the highest version name
# ( e.g. 1.2.1 becomes 1.2.2 ).
# if invoked with index, the function increments number at the index given as its sole argument by 1;
# e.g. if the highest version is 1.2.2 and the function is invokes like this: find_next_version_name 1 
# next version will be 1.3.0, invoking it as follows: find_next_version_name 0 produces 2.0.0 

find_next_version_name() {

  find_versions "$VERSIONS_DIR"
  local last_version=
  local index=-1
  local indices=
  IFS=. read -r -a last_version <<< "$LAST_VERSION"
  [[ ! -z "$1" ]] && {
    # check if given index exists
    for i in $indices; do
      [[ "$1" == "$i" ]] && {
    (( index == -1 )) && {
      printf "*** \n ${1} is not a valid index. Possible indices are 0 numbered. Each index correspond to a placement of a number in dot separated string representing the last version, which in this particular case is ${LAST_VERSION}. Hence, possible values here are ${indices}. \n Exiting ...\n *** \n"
      exit 1  
    (( last_version[$index]+=1 )) 

    for (( (( index++ )), j=index; $j < ${#last_version[@]}; j++  )); do 
      [[ last_version[$j] ]] && { last_version[$j]=0 ;}
  } || {
    (( last_version[$index]+=1 ))
  echo $( IFS=.; echo "${last_version[*]}")

#run the program
find_next_version_name $1

Comments on the syntax used in the above piece of code:

Display indices of array last_version:


Increment value of array last_version with index $index:

(( last_version[$index]+=1 ))

Test for existence of index $i in array last_version:

[[ last_version[$j] ]]

Display all values currently in array last_version ( * works the same as @ )


Join all items in array last_version using dot symbol (.)

echo $( IFS=.; echo "${last_version[*]}")

Other things worth knowing about BASH arrays

There is special syntax for both removing indices from an array and destroying whole arrays. In both cases built-in unset is used:

Remove member variable at index 0:

unset ${ARRAY[0]}

Destroy array ARRAY:

unset ARRAY

Running the program

If you want to run the program used as an example above, save all its four pieces to a file with the name, and make the file executable:

$ chmod 755

Next update VERSIONS_DIR variable value at the beginning of the file to point to a directory (trailing slash required) you store you program sub-directories in. The sub-directories must have names that follow the pattern number.number. ... .number for the program to find them.

Run the program:



Arrays in BASH, in terms how they behave and what they offer, are not that different from what one normally expects and gets when programming computers in a modern programming language. However, array syntax BASH provides looks fairly cryptic and different in many cases, at least at the first sight. Even more so when a piece of code is based largely on arrays. Learning the syntax requires a little bit of open mind, time, attention to detail and some decent examples resource. I hope this article will become such a resource to you.

This post was updated on 04 Nov 2017 14:28:19

Tags:  BASH 

Author, Copyright and citation


Sylwester Wojnowski

Author of the above article, Sylwester Wojnowski, enjoys sWWW writing computer code in PHP, JavaScript and BASH, and some other things he wrote more on on the About page of this website.


©Copyright, 2021 Sylwester Wojnowski. This article may not be reproduced or published as a whole or in parts without permission from the author. If you share it, please give author credit and do not remove embedded links.

Computer code, if present in the article, is excluded from the above and licensed under GPLv3.


Cite this article as:

Wojnowski, Sylwester. "Arrays in BASH." From sWWW - Code For The Web .

Add Comment

Allowed BB Code - style tags: [b][/b], [i][/i], [code=text][/code],[code=javascript][/code],[code=php][/code],[code=bash][/code],[code=css][/code],[code=html][/code]

I constent to processing my data given through this form for purposes of a reply by the administrator of this website.

Recent Comments

Nobody has commented on this post yet. Be first!

Post navigation

  BASH recursion examples - part 2

  installing PHPUnit