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
#!/bin/bash
area[11]=23
area[13]=37
area[51]=UFOs
# Note that array members need not be consecutive
# or contiguous.
# Some members of the array can be left uninitialized.
# Gaps in the array are o.k.
echo -n "area[11] = "
echo ${area[11]}
echo -n "area[13] = "
echo ${area[13]}
# Note that {curly brackets} needed
echo "Contents of area[51] are ${area[51]}."
# Contents of uninitialized array variable print blank.
echo -n "area[43] = "
echo ${area[43]}
echo "(area[43] unassigned)"
echo
# Sum of two array variables assigned to third
area[5]=`expr ${area[11]} + ${area[13]}`
echo "area[5] = area[11] + area[13]"
echo -n "area[5] = "
echo ${area[5]}
area[6]=`expr ${area[11]} + ${area[51]}`
echo "area[6] = area[11] + area[51]"
echo -n "area[6] = "
echo ${area[6]}
# This doesn't work because
# adding an integer to a string is not permitted.
echo
echo
echo
# -----------------------------------------------------------------
# Another array, "area2".
# Another way of assigning array variables...
# array_name=( XXX YYY ZZZ ... )
area2=( zero one two three four)
echo -n "area2[0] = "
echo ${area2[0]}
# Aha, zero-based indexing (first element of array is [0], not [1]).
echo -n "area2[1] = "
echo ${area2[1]} # [1] is second element of array.
# -----------------------------------------------------------------
echo
echo
echo
# -----------------------------------------------
# Yet another array, "area3".
# Yet another way of assigning array variables...
# array_name=([xx]=XXX [yy]=YYY ...)
area3=([17]=seventeen [24]=twenty-four)
echo -n "area3[17] = "
echo ${area3[17]}
echo -n "area3[24] = "
echo ${area3[24]}
# -----------------------------------------------
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
#!/bin/bash
declare -a colors
# Permits declaring an array without specifying size.
echo "Enter your favorite colors (separated from each other by a space)."
read -a colors
# Special option to 'read' command,
# allowing it to assign elements in an array.
echo
element_count=${#colors[@]} # Special syntax to extract number of elements in array.
# element_count=${#colors[*]} works also.
index=0
# List all the elements in the array.
while [ $index -lt $element_count ]
do
echo ${colors[$index]}
let "index = $index + 1"
done
# Each array element listed on a separate line.
# If this is not desired, use echo -n "${colors[$index]} "
echo
# Again, list all the elements in the array, but using a more elegant method.
echo ${colors[@]}
# echo ${colors[*]} works also.
echo
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
#!/bin/bash
# Bubble sort, of sorts.
# Recall the algorithm for a bubble sort. In this particular version...
# With each successive pass through the array to be sorted,
# compare two adjacent elements, and swap them if out of order.
# At the end of the first pass, the "heaviest" element has sunk to bottom.
# At the end of the second pass, the next "heaviest" one has sunk next to bottom.
# And so forth.
# This means that each successive pass needs to traverse less of the array.
# You will therefore notice a speeding up in the printing of the later passes.
exchange()
{
# Swaps two members of the array.
local temp=${Countries[$1]} # Temporary storage for element getting swapped out.
Countries[$1]=${Countries[$2]}
Countries[$2]=$temp
return
}
declare -a Countries # Declare array, optional here since it's initialized below.
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)
# Couldn't think of one starting with X (darn).
clear # Clear the screen to start with.
echo "0: ${Countries[*]}" # List entire array at pass 0.
number_of_elements=${#Countries[@]}
let "comparisons = $number_of_elements - 1"
count=1 # Pass number.
while [ $comparisons -gt 0 ] # Beginning of outer loop
do
index=0 # Reset index to start of array after each pass.
while [ $index -lt $comparisons ] # Beginning of inner loop
do
if [ ${Countries[$index]} \> ${Countries[`expr $index + 1`]} ]
# If out of order...
# Recalling that \> is ASCII comparison operator.
then
exchange $index `expr $index + 1` # Swap.
fi
let "index += 1"
done # End of inner loop
let "comparisons -= 1"
# Since "heaviest" element bubbles to bottom, we need do one less comparison each pass.
echo
echo "$count: ${Countries[@]}"
# Print resultant array at end of each pass.
echo
let "count += 1" # Increment pass count.
done # End of outer loop
# All done.
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
#!/bin/bash
# sieve.sh
# Sieve of Erastosthenes
# Ancient algorithm for finding prime numbers.
# This runs a couple of orders of magnitude
# slower than equivalent C program.
LOWER_LIMIT=1
# Starting with 1.
UPPER_LIMIT=1000
# Up to 1000.
# (You may set this higher...
# if you have time on your hands.)
PRIME=1
NON_PRIME=0
let SPLIT=UPPER_LIMIT/2
# Optimization:
# Need to test numbers only
# halfway to upper limit.
declare -a Primes
# Primes[] is an array.
initialize ()
{
# Initialize the array.
i=$LOWER_LIMIT
until [ $i -gt $UPPER_LIMIT ]
do
Primes[i]=$PRIME
let "i += 1"
done
# Assume all array members guilty (prime)
# until proven innocent.
}
print_primes ()
{
# Print out the members of the Primes[] array
# tagged as prime.
i=$LOWER_LIMIT
until [ $i -gt $UPPER_LIMIT ]
do
if [ ${Primes[i]} -eq $PRIME ]
then
printf "%8d" $i
# 8 spaces per number
# gives nice, even columns.
fi
let "i += 1"
done
}
sift ()
{
# Sift out the non-primes.
let i=$LOWER_LIMIT+1
# We know 1 is prime, so
# let's start with 2.
until [ $i -gt $UPPER_LIMIT ]
do
if [ ${Primes[i]} -eq $PRIME ]
# Don't bother sieving numbers
# already sieved (tagged as non-prime).
then
t=$i
while [ $t -le $UPPER_LIMIT ]
do
let "t += $i "
Primes[t]=$NON_PRIME
# Tag as non-prime
# all multiples.
done
fi
let "i += 1"
done
}
# Invoke the functions sequentially.
initialize
sift
print_primes
echo
# This is what they call structured programming.
exit 0 |