Advanced Bash-Scripting HOWTO: A guide to shell scripting, using Bash | ||
---|---|---|
Prev | Chapter 3. Tutorial / Reference | Next |
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-71. 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. exit 0 |
Arrays variables have a syntax all their own, and even standard bash operators have special options adapted for array use.
Example 3-72. 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-73. 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. 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-74. 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 |