3.22. Arrays

Newer versions of bash support one-dimensional arrays. Arrays may be declared with the variable[xx] notation or explicitly by a declare -a variable statement. To dereference (find the contents of) an array variable, use curly bracket notation, that is, ${variable[xx]}.


Example 3-89. Simple array usage

   1 #!/bin/bash
   2 
   3 
   4 area[11]=23
   5 area[13]=37
   6 area[51]=UFOs
   7 
   8 # Note that array members need not be consecutive
   9 # or contiguous.
  10 
  11 # Some members of the array can be left uninitialized.
  12 # Gaps in the array are o.k.
  13 
  14 
  15 echo -n "area[11] = "
  16 echo ${area[11]}
  17 echo -n "area[13] = "
  18 echo ${area[13]}
  19 # Note that {curly brackets} needed
  20 echo "Contents of area[51] are ${area[51]}."
  21 
  22 # Contents of uninitialized array variable print blank.
  23 echo -n "area[43] = "
  24 echo ${area[43]}
  25 echo "(area[43] unassigned)"
  26 
  27 echo
  28 
  29 # Sum of two array variables assigned to third
  30 area[5]=`expr ${area[11]} + ${area[13]}`
  31 echo "area[5] = area[11] + area[13]"
  32 echo -n "area[5] = "
  33 echo ${area[5]}
  34 
  35 area[6]=`expr ${area[11]} + ${area[51]}`
  36 echo "area[6] = area[11] + area[51]"
  37 echo -n "area[6] = "
  38 echo ${area[6]}
  39 # This doesn't work because
  40 # adding an integer to a string is not permitted.
  41 
  42 echo
  43 echo
  44 echo
  45 
  46 # -----------------------------------------------------------------
  47 # Another array, "area2".
  48 # Another way of assigning array variables...
  49 # array_name=( XXX YYY ZZZ ... )
  50 
  51 area2=( zero one two three four)
  52 
  53 echo -n "area2[0] = "
  54 echo ${area2[0]}
  55 # Aha, zero-based indexing (first element of array is [0], not [1]).
  56 
  57 echo -n "area2[1] = "
  58 echo ${area2[1]}  # [1] is second element of array.
  59 # -----------------------------------------------------------------
  60 
  61 
  62 echo
  63 echo
  64 echo
  65 
  66 # -----------------------------------------------
  67 # Yet another array, "area3".
  68 # Yet another way of assigning array variables...
  69 # array_name=([xx]=XXX [yy]=YYY ...)
  70 
  71 area3=([17]=seventeen [24]=twenty-four)
  72 
  73 echo -n "area3[17] = "
  74 echo ${area3[17]}
  75 
  76 echo -n "area3[24] = "
  77 echo ${area3[24]}
  78 # -----------------------------------------------
  79 
  80 
  81 exit 0

Arrays variables have a syntax all their own, and even standard bash operators have special options adapted for array use.


Example 3-90. Some special properties of arrays

   1 #!/bin/bash
   2 
   3 declare -a colors
   4 # Permits declaring an array without specifying size.
   5 
   6 echo "Enter your favorite colors (separated from each other by a space)."
   7 
   8 read -a colors
   9 # Special option to 'read' command,
  10 # allowing it to assign elements in an array.
  11 
  12 echo
  13 
  14   element_count=${#colors[@]} # Special syntax to extract number of elements in array.
  15 # element_count=${#colors[*]} works also.
  16 index=0
  17 
  18 # List all the elements in the array.
  19 while [ $index -lt $element_count ]
  20 do
  21   echo ${colors[$index]}
  22   let "index = $index + 1"
  23 done
  24 # Each array element listed on a separate line.
  25 # If this is not desired, use  echo -n "${colors[$index]} "
  26 
  27 echo
  28 
  29 # Again, list all the elements in the array, but using a more elegant method.
  30   echo ${colors[@]}
  31 # echo ${colors[*]} works also.
  32 
  33 
  34 echo
  35 
  36 exit 0

As seen in the previous example, either ${array_name[@]} or ${array_name[*]} refers to all the elements of the array. Similarly, to get a count of the number of elements in an array, use either ${#array_name[@]} or ${#array_name[*]}.

--

Arrays permit deploying old familiar algorithms as shell scripts. Whether this is necessarily a good idea is left to the reader to decide.


Example 3-91. An old friend: The Bubble Sort

   1 #!/bin/bash
   2 
   3 # Bubble sort, of sorts.
   4 
   5 # Recall the algorithm for a bubble sort. In this particular version...
   6 
   7 # With each successive pass through the array to be sorted,
   8 # compare two adjacent elements, and swap them if out of order.
   9 # At the end of the first pass, the "heaviest" element has sunk to bottom.
  10 # At the end of the second pass, the next "heaviest" one has sunk next to bottom.
  11 # And so forth.
  12 # This means that each successive pass needs to traverse less of the array.
  13 # You will therefore notice a speeding up in the printing of the later passes.
  14 
  15 
  16 exchange()
  17 {
  18   # Swaps two members of the array.
  19   local temp=${Countries[$1]} # Temporary storage for element getting swapped out.
  20   Countries[$1]=${Countries[$2]}
  21   Countries[$2]=$temp
  22   
  23   return
  24 }  
  25 
  26 declare -a Countries  # Declare array, optional here since it's initialized below.
  27 
  28 Countries=(Netherlands Ukraine Zair Turkey Russia Yemen Syria Brazil Argentina Nicaragua Japan Mexico Venezuela Greece England Israel Peru Canada Oman Denmark Wales France Kashmir Qatar Liechtenstein Hungary)
  29 # Couldn't think of one starting with X (darn).
  30 
  31 clear  # Clear the screen to start with. 
  32 
  33 echo "0: ${Countries[*]}"  # List entire array at pass 0.
  34 
  35 number_of_elements=${#Countries[@]}
  36 let "comparisons = $number_of_elements - 1"
  37 
  38 count=1 # Pass number.
  39 
  40 while [ $comparisons -gt 0 ]   # Beginning of outer loop
  41 do
  42 
  43   index=0  # Reset index to start of array after each pass.
  44 
  45   while [ $index -lt $comparisons ] # Beginning of inner loop
  46   do
  47     if [ ${Countries[$index]} \> ${Countries[`expr $index + 1`]} ]
  48     # If out of order...
  49     # Recalling that \> is ASCII comparison operator.
  50     then
  51       exchange $index `expr $index + 1`  # Swap.
  52     fi  
  53     let "index += 1"
  54   done # End of inner loop
  55   
  56 
  57 let "comparisons -= 1"
  58 # Since "heaviest" element bubbles to bottom, we need do one less comparison each pass.
  59 
  60 echo
  61 echo "$count: ${Countries[@]}"
  62 # Print resultant array at end of each pass.
  63 echo
  64 let "count += 1"   # Increment pass count.
  65 
  66 done  # End of outer loop
  67 
  68 # All done.
  69 
  70 exit 0

--

Arrays enable implementing a shell script version of the Sieve of Erastosthenes. Of course, a resource-intensive application of this nature should really be written in a compiled language, such as C. It runs excruciatingly slowly as a script.


Example 3-92. Complex array application: Sieve of Erastosthenes

   1 #!/bin/bash
   2 
   3 # sieve.sh
   4 # Sieve of Erastosthenes
   5 # Ancient algorithm for finding prime numbers.
   6 
   7 # This runs a couple of orders of magnitude
   8 # slower than equivalent C program.
   9 
  10 LOWER_LIMIT=1
  11 # Starting with 1.
  12 UPPER_LIMIT=1000
  13 # Up to 1000.
  14 # (You may set this higher...
  15 #  if you have time on your hands.)
  16 
  17 PRIME=1
  18 NON_PRIME=0
  19 
  20 let SPLIT=UPPER_LIMIT/2
  21 # Optimization:
  22 # Need to test numbers only
  23 # halfway to upper limit.
  24 
  25 
  26 declare -a Primes
  27 # Primes[] is an array.
  28 
  29 
  30 initialize ()
  31 {
  32 # Initialize the array.
  33 
  34 i=$LOWER_LIMIT
  35 until [ $i -gt $UPPER_LIMIT ]
  36 do
  37   Primes[i]=$PRIME
  38   let "i += 1"
  39 done
  40 # Assume all array members guilty (prime)
  41 # until proven innocent.
  42 }
  43 
  44 print_primes ()
  45 {
  46 # Print out the members of the Primes[] array
  47 # tagged as prime.
  48 
  49 i=$LOWER_LIMIT
  50 
  51 until [ $i -gt $UPPER_LIMIT ]
  52 do
  53 
  54   if [ ${Primes[i]} -eq $PRIME ]
  55   then
  56     printf "%8d" $i
  57     # 8 spaces per number
  58     # gives nice, even columns.
  59   fi
  60   
  61   let "i += 1"
  62   
  63 done
  64 
  65 }
  66 
  67 sift ()
  68 {
  69 # Sift out the non-primes.
  70 
  71 let i=$LOWER_LIMIT+1
  72 # We know 1 is prime, so
  73 # let's start with 2.
  74 
  75 until [ $i -gt $UPPER_LIMIT ]
  76 do
  77 
  78 if [ ${Primes[i]} -eq $PRIME ]
  79 # Don't bother sieving numbers
  80 # already sieved (tagged as non-prime).
  81 then
  82 
  83   t=$i
  84 
  85   while [ $t -le $UPPER_LIMIT ]
  86   do
  87     let "t += $i "
  88     Primes[t]=$NON_PRIME
  89     # Tag as non-prime
  90     # all multiples.
  91   done
  92 
  93 fi  
  94 
  95   let "i += 1"
  96 done  
  97 
  98 
  99 }
 100 
 101 
 102 # Invoke the functions sequentially.
 103 initialize
 104 sift
 105 print_primes
 106 echo
 107 # This is what they call structured programming.
 108 
 109 exit 0