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-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 |