2612 lines
54 KiB
HTML
2612 lines
54 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<HTML
|
|
><HEAD
|
|
><TITLE
|
|
>Arrays</TITLE
|
|
><META
|
|
NAME="GENERATOR"
|
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
|
|
REL="HOME"
|
|
TITLE="Advanced Bash-Scripting Guide"
|
|
HREF="index.html"><LINK
|
|
REL="UP"
|
|
TITLE="Advanced Topics"
|
|
HREF="part5.html"><LINK
|
|
REL="PREVIOUS"
|
|
TITLE="List Constructs"
|
|
HREF="list-cons.html"><LINK
|
|
REL="NEXT"
|
|
TITLE="Indirect References"
|
|
HREF="ivr.html"></HEAD
|
|
><BODY
|
|
CLASS="CHAPTER"
|
|
BGCOLOR="#FFFFFF"
|
|
TEXT="#000000"
|
|
LINK="#0000FF"
|
|
VLINK="#840084"
|
|
ALINK="#0000FF"
|
|
><DIV
|
|
CLASS="NAVHEADER"
|
|
><TABLE
|
|
SUMMARY="Header navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TH
|
|
COLSPAN="3"
|
|
ALIGN="center"
|
|
>Advanced Bash-Scripting Guide: </TH
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="left"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="list-cons.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="80%"
|
|
ALIGN="center"
|
|
VALIGN="bottom"
|
|
></TD
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="right"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="ivr.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"></DIV
|
|
><DIV
|
|
CLASS="CHAPTER"
|
|
><H1
|
|
><A
|
|
NAME="ARRAYS"
|
|
></A
|
|
>Chapter 27. Arrays</H1
|
|
><P
|
|
><A
|
|
NAME="ARRAYREF"
|
|
></A
|
|
></P
|
|
><P
|
|
>Newer versions of Bash support one-dimensional arrays.
|
|
<A
|
|
NAME="BRACKARRAY"
|
|
></A
|
|
>
|
|
Array elements may be initialized with the
|
|
<TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>variable[xx]</B
|
|
></TT
|
|
> notation. Alternatively,
|
|
a script may introduce the entire array by an explicit
|
|
<TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>declare -a variable</B
|
|
></TT
|
|
> statement. To
|
|
dereference (retrieve the contents of) an array element, use
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>curly bracket</I
|
|
> notation, that is,
|
|
<TT
|
|
CLASS="USERINPUT"
|
|
><B
|
|
>${element[xx]}</B
|
|
></TT
|
|
>.</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYNOTATION"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EX66"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-1. Simple array usage</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
|
|
|
|
area[11]=23
|
|
area[13]=37
|
|
area[51]=UFOs
|
|
|
|
# Array members need not be consecutive or contiguous.
|
|
|
|
# Some members of the array can be left uninitialized.
|
|
# Gaps in the array are okay.
|
|
# In fact, arrays with sparse data ("sparse arrays")
|
|
#+ are useful in spreadsheet-processing software.
|
|
|
|
|
|
echo -n "area[11] = "
|
|
echo ${area[11]} # {curly brackets} needed.
|
|
|
|
echo -n "area[13] = "
|
|
echo ${area[13]}
|
|
|
|
echo "Contents of area[51] are ${area[51]}."
|
|
|
|
# Contents of uninitialized array variable print blank (null variable).
|
|
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 fails 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</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="ARRAYINIT0"
|
|
></A
|
|
></P
|
|
><P
|
|
>As we have seen, a convenient way of initializing an entire array
|
|
is the <TT
|
|
CLASS="VARNAME"
|
|
>array=( element1 element2 ... elementN )</TT
|
|
>
|
|
notation.</P
|
|
><P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>base64_charset=( {A..Z} {a..z} {0..9} + / = )
|
|
# Using extended brace expansion
|
|
#+ to initialize the elements of the array.
|
|
# Excerpted from vladz's "base64.sh" script
|
|
#+ in the "Contributed Scripts" appendix.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></P
|
|
><P
|
|
><A
|
|
NAME="ARRAYOPSVARS"
|
|
></A
|
|
></P
|
|
><TABLE
|
|
CLASS="SIDEBAR"
|
|
BORDER="1"
|
|
CELLPADDING="5"
|
|
><TR
|
|
><TD
|
|
><DIV
|
|
CLASS="SIDEBAR"
|
|
><A
|
|
NAME="AEN18812"
|
|
></A
|
|
><P
|
|
></P
|
|
><P
|
|
>Bash permits array operations on variables, even if
|
|
the variables are not explicitly declared as arrays.</P
|
|
><P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>string=abcABC123ABCabc
|
|
echo ${string[@]} # abcABC123ABCabc
|
|
echo ${string[*]} # abcABC123ABCabc
|
|
echo ${string[0]} # abcABC123ABCabc
|
|
echo ${string[1]} # No output!
|
|
# Why?
|
|
echo ${#string[@]} # 1
|
|
# One element in the array.
|
|
# The string itself.
|
|
|
|
# Thank you, Michael Zick, for pointing this out.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
Once again this demonstrates that <A
|
|
HREF="untyped.html#BVUNTYPED"
|
|
>Bash
|
|
variables are untyped</A
|
|
>.
|
|
</P
|
|
><P
|
|
></P
|
|
></DIV
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="POEM"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-2. Formatting a poem</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# poem.sh: Pretty-prints one of the ABS Guide author's favorite poems.
|
|
|
|
# Lines of the poem (single stanza).
|
|
Line[1]="I do not know which to prefer,"
|
|
Line[2]="The beauty of inflections"
|
|
Line[3]="Or the beauty of innuendoes,"
|
|
Line[4]="The blackbird whistling"
|
|
Line[5]="Or just after."
|
|
# Note that quoting permits embedding whitespace.
|
|
|
|
# Attribution.
|
|
Attrib[1]=" Wallace Stevens"
|
|
Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\""
|
|
# This poem is in the Public Domain (copyright expired).
|
|
|
|
echo
|
|
|
|
tput bold # Bold print.
|
|
|
|
for index in 1 2 3 4 5 # Five lines.
|
|
do
|
|
printf " %s\n" "${Line[index]}"
|
|
done
|
|
|
|
for index in 1 2 # Two attribution lines.
|
|
do
|
|
printf " %s\n" "${Attrib[index]}"
|
|
done
|
|
|
|
tput sgr0 # Reset terminal.
|
|
# See 'tput' docs.
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# Exercise:
|
|
# --------
|
|
# Modify this script to pretty-print a poem from a text data file.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="ARRAYSYNTAX"
|
|
></A
|
|
></P
|
|
><P
|
|
>Array variables have a syntax all their own, and even
|
|
standard Bash commands and operators have special options adapted
|
|
for array use.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="ARRAYOPS"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-3. Various array operations</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# array-ops.sh: More fun with arrays.
|
|
|
|
|
|
array=( zero one two three four five )
|
|
# Element 0 1 2 3 4 5
|
|
|
|
echo ${array[0]} # zero
|
|
echo ${array:0} # zero
|
|
# Parameter expansion of first element,
|
|
#+ starting at position # 0 (1st character).
|
|
echo ${array:1} # ero
|
|
# Parameter expansion of first element,
|
|
#+ starting at position # 1 (2nd character).
|
|
|
|
echo "--------------"
|
|
|
|
echo ${#array[0]} # 4
|
|
# Length of first element of array.
|
|
echo ${#array} # 4
|
|
# Length of first element of array.
|
|
# (Alternate notation)
|
|
|
|
echo ${#array[1]} # 3
|
|
# Length of second element of array.
|
|
# Arrays in Bash have zero-based indexing.
|
|
|
|
echo ${#array[*]} # 6
|
|
# Number of elements in array.
|
|
echo ${#array[@]} # 6
|
|
# Number of elements in array.
|
|
|
|
echo "--------------"
|
|
|
|
array2=( [0]="first element" [1]="second element" [3]="fourth element" )
|
|
# ^ ^ ^ ^ ^ ^ ^ ^ ^
|
|
# Quoting permits embedding whitespace within individual array elements.
|
|
|
|
echo ${array2[0]} # first element
|
|
echo ${array2[1]} # second element
|
|
echo ${array2[2]} #
|
|
# Skipped in initialization, and therefore null.
|
|
echo ${array2[3]} # fourth element
|
|
echo ${#array2[0]} # 13 (length of first element)
|
|
echo ${#array2[*]} # 3 (number of elements in array)
|
|
|
|
exit</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="ARRAYSTRINGOPS"
|
|
></A
|
|
></P
|
|
><P
|
|
>Many of the standard <A
|
|
HREF="string-manipulation.html#STRINGMANIP"
|
|
>string
|
|
operations</A
|
|
> work on arrays.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="ARRAYSTROPS"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-4. String operations on arrays</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# array-strops.sh: String operations on arrays.
|
|
|
|
# Script by Michael Zick.
|
|
# Used in ABS Guide with permission.
|
|
# Fixups: 05 May 08, 04 Aug 08.
|
|
|
|
# In general, any string operation using the ${name ... } notation
|
|
#+ can be applied to all string elements in an array,
|
|
#+ with the ${name[@] ... } or ${name[*] ...} notation.
|
|
|
|
|
|
arrayZ=( one two three four five five )
|
|
|
|
echo
|
|
|
|
# Trailing Substring Extraction
|
|
echo ${arrayZ[@]:0} # one two three four five five
|
|
# ^ All elements.
|
|
|
|
echo ${arrayZ[@]:1} # two three four five five
|
|
# ^ All elements following element[0].
|
|
|
|
echo ${arrayZ[@]:1:2} # two three
|
|
# ^ Only the two elements after element[0].
|
|
|
|
echo "---------"
|
|
|
|
|
|
# Substring Removal
|
|
|
|
# Removes shortest match from front of string(s).
|
|
|
|
echo ${arrayZ[@]#f*r} # one two three five five
|
|
# ^ # Applied to all elements of the array.
|
|
# Matches "four" and removes it.
|
|
|
|
# Longest match from front of string(s)
|
|
echo ${arrayZ[@]##t*e} # one two four five five
|
|
# ^^ # Applied to all elements of the array.
|
|
# Matches "three" and removes it.
|
|
|
|
# Shortest match from back of string(s)
|
|
echo ${arrayZ[@]%h*e} # one two t four five five
|
|
# ^ # Applied to all elements of the array.
|
|
# Matches "hree" and removes it.
|
|
|
|
# Longest match from back of string(s)
|
|
echo ${arrayZ[@]%%t*e} # one two four five five
|
|
# ^^ # Applied to all elements of the array.
|
|
# Matches "three" and removes it.
|
|
|
|
echo "----------------------"
|
|
|
|
|
|
# Substring Replacement
|
|
|
|
# Replace first occurrence of substring with replacement.
|
|
echo ${arrayZ[@]/fiv/XYZ} # one two three four XYZe XYZe
|
|
# ^ # Applied to all elements of the array.
|
|
|
|
# Replace all occurrences of substring.
|
|
echo ${arrayZ[@]//iv/YY} # one two three four fYYe fYYe
|
|
# Applied to all elements of the array.
|
|
|
|
# Delete all occurrences of substring.
|
|
# Not specifing a replacement defaults to 'delete' ...
|
|
echo ${arrayZ[@]//fi/} # one two three four ve ve
|
|
# ^^ # Applied to all elements of the array.
|
|
|
|
# Replace front-end occurrences of substring.
|
|
echo ${arrayZ[@]/#fi/XY} # one two three four XYve XYve
|
|
# ^ # Applied to all elements of the array.
|
|
|
|
# Replace back-end occurrences of substring.
|
|
echo ${arrayZ[@]/%ve/ZZ} # one two three four fiZZ fiZZ
|
|
# ^ # Applied to all elements of the array.
|
|
|
|
echo ${arrayZ[@]/%o/XX} # one twXX three four five five
|
|
# ^ # Why?
|
|
|
|
echo "-----------------------------"
|
|
|
|
|
|
replacement() {
|
|
echo -n "!!!"
|
|
}
|
|
|
|
echo ${arrayZ[@]/%e/$(replacement)}
|
|
# ^ ^^^^^^^^^^^^^^
|
|
# on!!! two thre!!! four fiv!!! fiv!!!
|
|
# The stdout of replacement() is the replacement string.
|
|
# Q.E.D: The replacement action is, in effect, an 'assignment.'
|
|
|
|
echo "------------------------------------"
|
|
|
|
# Accessing the "for-each":
|
|
echo ${arrayZ[@]//*/$(replacement optional_arguments)}
|
|
# ^^ ^^^^^^^^^^^^^
|
|
# !!! !!! !!! !!! !!! !!!
|
|
|
|
# Now, if Bash would only pass the matched string
|
|
#+ to the function being called . . .
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# Before reaching for a Big Hammer -- Perl, Python, or all the rest --
|
|
# recall:
|
|
# $( ... ) is command substitution.
|
|
# A function runs as a sub-process.
|
|
# A function writes its output (if echo-ed) to stdout.
|
|
# Assignment, in conjunction with "echo" and command substitution,
|
|
#+ can read a function's stdout.
|
|
# The name[@] notation specifies (the equivalent of) a "for-each"
|
|
#+ operation.
|
|
# Bash is more powerful than you think!</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
HREF="commandsub.html#COMMANDSUBREF"
|
|
>Command substitution</A
|
|
> can
|
|
construct the individual elements of an array.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="SCRIPTARRAY"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-5. Loading the contents of a script into an array</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# script-array.sh: Loads this script into an array.
|
|
# Inspired by an e-mail from Chris Martin (thanks!).
|
|
|
|
script_contents=( $(cat "$0") ) # Stores contents of this script ($0)
|
|
#+ in an array.
|
|
|
|
for element in $(seq 0 $((${#script_contents[@]} - 1)))
|
|
do # ${#script_contents[@]}
|
|
#+ gives number of elements in the array.
|
|
#
|
|
# Question:
|
|
# Why is seq 0 necessary?
|
|
# Try changing it to seq 1.
|
|
echo -n "${script_contents[$element]}"
|
|
# List each field of this script on a single line.
|
|
# echo -n "${script_contents[element]}" also works because of ${ ... }.
|
|
echo -n " -- " # Use " -- " as a field separator.
|
|
done
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# Exercise:
|
|
# --------
|
|
# Modify this script so it lists itself
|
|
#+ in its original format,
|
|
#+ complete with whitespace, line breaks, etc.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>In an array context, some Bash <A
|
|
HREF="internal.html#BUILTINREF"
|
|
>builtins</A
|
|
> have a slightly
|
|
altered meaning. <A
|
|
NAME="ARRAYUNSET"
|
|
></A
|
|
>For example, <A
|
|
HREF="internal.html#UNSETREF"
|
|
>unset</A
|
|
> deletes array elements, or even
|
|
an entire array.</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYSPECIALPROPS"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EX67"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-6. Some special properties of arrays</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
|
|
declare -a colors
|
|
# All subsequent commands in this script will treat
|
|
#+ the variable "colors" as an array.
|
|
|
|
echo "Enter your favorite colors (separated from each other by a space)."
|
|
|
|
read -a colors # Enter at least 3 colors to demonstrate features below.
|
|
# Special option to 'read' command,
|
|
#+ allowing assignment of elements in an array.
|
|
|
|
echo
|
|
|
|
element_count=${#colors[@]}
|
|
# Special syntax to extract number of elements in array.
|
|
# element_count=${#colors[*]} works also.
|
|
#
|
|
# The "@" variable allows word splitting within quotes
|
|
#+ (extracts variables separated by whitespace).
|
|
#
|
|
# This corresponds to the behavior of "$@" and "$*"
|
|
#+ in positional parameters.
|
|
|
|
index=0
|
|
|
|
while [ "$index" -lt "$element_count" ]
|
|
do # List all the elements in the array.
|
|
echo ${colors[$index]}
|
|
# ${colors[index]} also works because it's within ${ ... } brackets.
|
|
let "index = $index + 1"
|
|
# Or:
|
|
# ((index++))
|
|
done
|
|
# Each array element listed on a separate line.
|
|
# If this is not desired, use echo -n "${colors[$index]} "
|
|
#
|
|
# Doing it with a "for" loop instead:
|
|
# for i in "${colors[@]}"
|
|
# do
|
|
# echo "$i"
|
|
# done
|
|
# (Thanks, S.C.)
|
|
|
|
echo
|
|
|
|
# Again, list all the elements in the array, but using a more elegant method.
|
|
echo ${colors[@]} # echo ${colors[*]} also works.
|
|
|
|
echo
|
|
|
|
# The "unset" command deletes elements of an array, or entire array.
|
|
unset colors[1] # Remove 2nd element of array.
|
|
# Same effect as colors[1]=
|
|
echo ${colors[@]} # List array again, missing 2nd element.
|
|
|
|
unset colors # Delete entire array.
|
|
# unset colors[*] and
|
|
#+ unset colors[@] also work.
|
|
echo; echo -n "Colors gone."
|
|
echo ${colors[@]} # List array again, now empty.
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="ARRAYNUMELEMENTS"
|
|
></A
|
|
></P
|
|
><P
|
|
>As seen in the previous example, either
|
|
<B
|
|
CLASS="COMMAND"
|
|
>${array_name[@]}</B
|
|
> or
|
|
<B
|
|
CLASS="COMMAND"
|
|
>${array_name[*]}</B
|
|
> refers to
|
|
<EM
|
|
>all</EM
|
|
> the elements of the array.
|
|
Similarly, to get a count of the number of elements in an
|
|
array, use either <B
|
|
CLASS="COMMAND"
|
|
>${#array_name[@]}</B
|
|
>
|
|
or <B
|
|
CLASS="COMMAND"
|
|
>${#array_name[*]}</B
|
|
>.
|
|
<B
|
|
CLASS="COMMAND"
|
|
>${#array_name}</B
|
|
> is the length (number of
|
|
characters) of <B
|
|
CLASS="COMMAND"
|
|
>${array_name[0]}</B
|
|
>, the first
|
|
element of the array.</P
|
|
><P
|
|
><A
|
|
NAME="EMPTYARRAY0"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EMPTYARRAY"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-7. Of empty arrays and empty elements</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# empty-array.sh
|
|
|
|
# Thanks to Stephane Chazelas for the original example,
|
|
#+ and to Michael Zick and Omair Eshkenazi, for extending it.
|
|
# And to Nathan Coulter for clarifications and corrections.
|
|
|
|
|
|
# An empty array is not the same as an array with empty elements.
|
|
|
|
array0=( first second third )
|
|
array1=( '' ) # "array1" consists of one empty element.
|
|
array2=( ) # No elements . . . "array2" is empty.
|
|
array3=( ) # What about this array?
|
|
|
|
|
|
echo
|
|
ListArray()
|
|
{
|
|
echo
|
|
echo "Elements in array0: ${array0[@]}"
|
|
echo "Elements in array1: ${array1[@]}"
|
|
echo "Elements in array2: ${array2[@]}"
|
|
echo "Elements in array3: ${array3[@]}"
|
|
echo
|
|
echo "Length of first element in array0 = ${#array0}"
|
|
echo "Length of first element in array1 = ${#array1}"
|
|
echo "Length of first element in array2 = ${#array2}"
|
|
echo "Length of first element in array3 = ${#array3}"
|
|
echo
|
|
echo "Number of elements in array0 = ${#array0[*]}" # 3
|
|
echo "Number of elements in array1 = ${#array1[*]}" # 1 (Surprise!)
|
|
echo "Number of elements in array2 = ${#array2[*]}" # 0
|
|
echo "Number of elements in array3 = ${#array3[*]}" # 0
|
|
}
|
|
|
|
# ===================================================================
|
|
|
|
ListArray
|
|
|
|
# Try extending those arrays.
|
|
|
|
# Adding an element to an array.
|
|
array0=( "${array0[@]}" "new1" )
|
|
array1=( "${array1[@]}" "new1" )
|
|
array2=( "${array2[@]}" "new1" )
|
|
array3=( "${array3[@]}" "new1" )
|
|
|
|
ListArray
|
|
|
|
# or
|
|
array0[${#array0[*]}]="new2"
|
|
array1[${#array1[*]}]="new2"
|
|
array2[${#array2[*]}]="new2"
|
|
array3[${#array3[*]}]="new2"
|
|
|
|
ListArray
|
|
|
|
# When extended as above, arrays are 'stacks' ...
|
|
# Above is the 'push' ...
|
|
# The stack 'height' is:
|
|
height=${#array2[@]}
|
|
echo
|
|
echo "Stack height for array2 = $height"
|
|
|
|
# The 'pop' is:
|
|
unset array2[${#array2[@]}-1] # Arrays are zero-based,
|
|
height=${#array2[@]} #+ which means first element has index 0.
|
|
echo
|
|
echo "POP"
|
|
echo "New stack height for array2 = $height"
|
|
|
|
ListArray
|
|
|
|
# List only 2nd and 3rd elements of array0.
|
|
from=1 # Zero-based numbering.
|
|
to=2
|
|
array3=( ${array0[@]:1:2} )
|
|
echo
|
|
echo "Elements in array3: ${array3[@]}"
|
|
|
|
# Works like a string (array of characters).
|
|
# Try some other "string" forms.
|
|
|
|
# Replacement:
|
|
array4=( ${array0[@]/second/2nd} )
|
|
echo
|
|
echo "Elements in array4: ${array4[@]}"
|
|
|
|
# Replace all matching wildcarded string.
|
|
array5=( ${array0[@]//new?/old} )
|
|
echo
|
|
echo "Elements in array5: ${array5[@]}"
|
|
|
|
# Just when you are getting the feel for this . . .
|
|
array6=( ${array0[@]#*new} )
|
|
echo # This one might surprise you.
|
|
echo "Elements in array6: ${array6[@]}"
|
|
|
|
array7=( ${array0[@]#new1} )
|
|
echo # After array6 this should not be a surprise.
|
|
echo "Elements in array7: ${array7[@]}"
|
|
|
|
# Which looks a lot like . . .
|
|
array8=( ${array0[@]/new1/} )
|
|
echo
|
|
echo "Elements in array8: ${array8[@]}"
|
|
|
|
# So what can one say about this?
|
|
|
|
# The string operations are performed on
|
|
#+ each of the elements in var[@] in succession.
|
|
# Therefore : Bash supports string vector operations.
|
|
# If the result is a zero length string,
|
|
#+ that element disappears in the resulting assignment.
|
|
# However, if the expansion is in quotes, the null elements remain.
|
|
|
|
# Michael Zick: Question, are those strings hard or soft quotes?
|
|
# Nathan Coulter: There is no such thing as "soft quotes."
|
|
#! What's really happening is that
|
|
#!+ the pattern matching happens after
|
|
#!+ all the other expansions of [word]
|
|
#!+ in cases like ${parameter#word}.
|
|
|
|
|
|
zap='new*'
|
|
array9=( ${array0[@]/$zap/} )
|
|
echo
|
|
echo "Number of elements in array9: ${#array9[@]}"
|
|
array9=( "${array0[@]/$zap/}" )
|
|
echo "Elements in array9: ${array9[@]}"
|
|
# This time the null elements remain.
|
|
echo "Number of elements in array9: ${#array9[@]}"
|
|
|
|
|
|
# Just when you thought you were still in Kansas . . .
|
|
array10=( ${array0[@]#$zap} )
|
|
echo
|
|
echo "Elements in array10: ${array10[@]}"
|
|
# But, the asterisk in zap won't be interpreted if quoted.
|
|
array10=( ${array0[@]#"$zap"} )
|
|
echo
|
|
echo "Elements in array10: ${array10[@]}"
|
|
# Well, maybe we _are_ still in Kansas . . .
|
|
# (Revisions to above code block by Nathan Coulter.)
|
|
|
|
|
|
# Compare array7 with array10.
|
|
# Compare array8 with array9.
|
|
|
|
# Reiterating: No such thing as soft quotes!
|
|
# Nathan Coulter explains:
|
|
# Pattern matching of 'word' in ${parameter#word} is done after
|
|
#+ parameter expansion and *before* quote removal.
|
|
# In the normal case, pattern matching is done *after* quote removal.
|
|
|
|
exit</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>The relationship of <B
|
|
CLASS="COMMAND"
|
|
>${array_name[@]}</B
|
|
>
|
|
and <B
|
|
CLASS="COMMAND"
|
|
>${array_name[*]}</B
|
|
> is analogous to that
|
|
between <A
|
|
HREF="internalvariables.html#APPREF"
|
|
>$@ and $*</A
|
|
>. This powerful
|
|
array notation has a number of uses.</P
|
|
><P
|
|
><A
|
|
NAME="COPYARRAY0"
|
|
></A
|
|
></P
|
|
><P
|
|
> <TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
># Copying an array.
|
|
array2=( "${array1[@]}" )
|
|
# or
|
|
array2="${array1[@]}"
|
|
#
|
|
# However, this fails with "sparse" arrays,
|
|
#+ arrays with holes (missing elements) in them,
|
|
#+ as Jochen DeSmet points out.
|
|
# ------------------------------------------
|
|
array1[0]=0
|
|
# array1[1] not assigned
|
|
array1[2]=2
|
|
array2=( "${array1[@]}" ) # Copy it?
|
|
|
|
echo ${array2[0]} # 0
|
|
echo ${array2[2]} # (null), should be 2
|
|
# ------------------------------------------
|
|
|
|
|
|
|
|
# Adding an element to an array.
|
|
array=( "${array[@]}" "new element" )
|
|
# or
|
|
array[${#array[*]}]="new element"
|
|
|
|
# Thanks, S.C.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYINITCS"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="TIP"
|
|
><P
|
|
></P
|
|
><TABLE
|
|
CLASS="TIP"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="25"
|
|
ALIGN="CENTER"
|
|
VALIGN="TOP"
|
|
><IMG
|
|
SRC="../images/tip.gif"
|
|
HSPACE="5"
|
|
ALT="Tip"></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
><P
|
|
>The <B
|
|
CLASS="COMMAND"
|
|
>array=( element1 element2 ... elementN )</B
|
|
>
|
|
initialization operation, with the help of <A
|
|
HREF="commandsub.html#COMMANDSUBREF"
|
|
>command substitution</A
|
|
>, makes it
|
|
possible to load the contents of a text file into an array.</P
|
|
><P
|
|
>
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
|
|
filename=sample_file
|
|
|
|
# cat sample_file
|
|
#
|
|
# 1 a b c
|
|
# 2 d e fg
|
|
|
|
|
|
declare -a array1
|
|
|
|
array1=( `cat "$filename"`) # Loads contents
|
|
# List file to stdout #+ of $filename into array1.
|
|
#
|
|
# array1=( `cat "$filename" | tr '\n' ' '`)
|
|
# change linefeeds in file to spaces.
|
|
# Not necessary because Bash does word splitting,
|
|
#+ changing linefeeds to spaces.
|
|
|
|
echo ${array1[@]} # List the array.
|
|
# 1 a b c 2 d e fg
|
|
#
|
|
# Each whitespace-separated "word" in the file
|
|
#+ has been assigned to an element of the array.
|
|
|
|
element_count=${#array1[*]}
|
|
echo $element_count # 8</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>Clever scripting makes it possible to add array operations.</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYASSIGN0"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="ARRAYASSIGN"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-8. Initializing arrays</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#! /bin/bash
|
|
# array-assign.bash
|
|
|
|
# Array operations are Bash-specific,
|
|
#+ hence the ".bash" in the script name.
|
|
|
|
# Copyright (c) Michael S. Zick, 2003, All rights reserved.
|
|
# License: Unrestricted reuse in any form, for any purpose.
|
|
# Version: $ID$
|
|
#
|
|
# Clarification and additional comments by William Park.
|
|
|
|
# Based on an example provided by Stephane Chazelas
|
|
#+ which appeared in an earlier version of the
|
|
#+ Advanced Bash Scripting Guide.
|
|
|
|
# Output format of the 'times' command:
|
|
# User CPU <space> System CPU
|
|
# User CPU of dead children <space> System CPU of dead children
|
|
|
|
# Bash has two versions of assigning all elements of an array
|
|
#+ to a new array variable.
|
|
# Both drop 'null reference' elements
|
|
#+ in Bash versions 2.04 and later.
|
|
# An additional array assignment that maintains the relationship of
|
|
#+ [subscript]=value for arrays may be added to newer versions.
|
|
|
|
# Constructs a large array using an internal command,
|
|
#+ but anything creating an array of several thousand elements
|
|
#+ will do just fine.
|
|
|
|
declare -a bigOne=( /dev/* ) # All the files in /dev . . .
|
|
echo
|
|
echo 'Conditions: Unquoted, default IFS, All-Elements-Of'
|
|
echo "Number of elements in array is ${#bigOne[@]}"
|
|
|
|
# set -vx
|
|
|
|
|
|
|
|
echo
|
|
echo '- - testing: =( ${array[@]} ) - -'
|
|
times
|
|
declare -a bigTwo=( ${bigOne[@]} )
|
|
# Note parens: ^ ^
|
|
times
|
|
|
|
|
|
echo
|
|
echo '- - testing: =${array[@]} - -'
|
|
times
|
|
declare -a bigThree=${bigOne[@]}
|
|
# No parentheses this time.
|
|
times
|
|
|
|
# Comparing the numbers shows that the second form, pointed out
|
|
#+ by Stephane Chazelas, is faster.
|
|
#
|
|
# As William Park explains:
|
|
#+ The bigTwo array assigned element by element (because of parentheses),
|
|
#+ whereas bigThree assigned as a single string.
|
|
# So, in essence, you have:
|
|
# bigTwo=( [0]="..." [1]="..." [2]="..." ... )
|
|
# bigThree=( [0]="... ... ..." )
|
|
#
|
|
# Verify this by: echo ${bigTwo[0]}
|
|
# echo ${bigThree[0]}
|
|
|
|
|
|
# I will continue to use the first form in my example descriptions
|
|
#+ because I think it is a better illustration of what is happening.
|
|
|
|
# The reusable portions of my examples will actual contain
|
|
#+ the second form where appropriate because of the speedup.
|
|
|
|
# MSZ: Sorry about that earlier oversight folks.
|
|
|
|
|
|
# Note:
|
|
# ----
|
|
# The "declare -a" statements in lines 32 and 44
|
|
#+ are not strictly necessary, since it is implicit
|
|
#+ in the Array=( ... ) assignment form.
|
|
# However, eliminating these declarations slows down
|
|
#+ the execution of the following sections of the script.
|
|
# Try it, and see.
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="NOTE"
|
|
><P
|
|
></P
|
|
><TABLE
|
|
CLASS="NOTE"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="25"
|
|
ALIGN="CENTER"
|
|
VALIGN="TOP"
|
|
><IMG
|
|
SRC="../images/note.gif"
|
|
HSPACE="5"
|
|
ALT="Note"></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
><P
|
|
>Adding a superfluous <B
|
|
CLASS="COMMAND"
|
|
>declare -a</B
|
|
>
|
|
statement to an array declaration may speed up execution of
|
|
subsequent operations on the array.</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
><A
|
|
NAME="ARRAYAPPEND0"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="COPYARRAY"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-9. Copying and concatenating arrays</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#! /bin/bash
|
|
# CopyArray.sh
|
|
#
|
|
# This script written by Michael Zick.
|
|
# Used here with permission.
|
|
|
|
# How-To "Pass by Name & Return by Name"
|
|
#+ or "Building your own assignment statement".
|
|
|
|
|
|
CpArray_Mac() {
|
|
|
|
# Assignment Command Statement Builder
|
|
|
|
echo -n 'eval '
|
|
echo -n "$2" # Destination name
|
|
echo -n '=( ${'
|
|
echo -n "$1" # Source name
|
|
echo -n '[@]} )'
|
|
|
|
# That could all be a single command.
|
|
# Matter of style only.
|
|
}
|
|
|
|
declare -f CopyArray # Function "Pointer"
|
|
CopyArray=CpArray_Mac # Statement Builder
|
|
|
|
Hype()
|
|
{
|
|
|
|
# Hype the array named $1.
|
|
# (Splice it together with array containing "Really Rocks".)
|
|
# Return in array named $2.
|
|
|
|
local -a TMP
|
|
local -a hype=( Really Rocks )
|
|
|
|
$($CopyArray $1 TMP)
|
|
TMP=( ${TMP[@]} ${hype[@]} )
|
|
$($CopyArray TMP $2)
|
|
}
|
|
|
|
declare -a before=( Advanced Bash Scripting )
|
|
declare -a after
|
|
|
|
echo "Array Before = ${before[@]}"
|
|
|
|
Hype before after
|
|
|
|
echo "Array After = ${after[@]}"
|
|
|
|
# Too much hype?
|
|
|
|
echo "What ${after[@]:3:2}?"
|
|
|
|
declare -a modest=( ${after[@]:2:1} ${after[@]:3:2} )
|
|
# ---- substring extraction ----
|
|
|
|
echo "Array Modest = ${modest[@]}"
|
|
|
|
# What happened to 'before' ?
|
|
|
|
echo "Array Before = ${before[@]}"
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="ARRAYAPPEND"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-10. More on concatenating arrays</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#! /bin/bash
|
|
# array-append.bash
|
|
|
|
# Copyright (c) Michael S. Zick, 2003, All rights reserved.
|
|
# License: Unrestricted reuse in any form, for any purpose.
|
|
# Version: $ID$
|
|
#
|
|
# Slightly modified in formatting by M.C.
|
|
|
|
|
|
# Array operations are Bash-specific.
|
|
# Legacy UNIX /bin/sh lacks equivalents.
|
|
|
|
|
|
# Pipe the output of this script to 'more'
|
|
#+ so it doesn't scroll off the terminal.
|
|
# Or, redirect output to a file.
|
|
|
|
|
|
declare -a array1=( zero1 one1 two1 )
|
|
# Subscript packed.
|
|
declare -a array2=( [0]=zero2 [2]=two2 [3]=three2 )
|
|
# Subscript sparse -- [1] is not defined.
|
|
|
|
echo
|
|
echo '- Confirm that the array is really subscript sparse. -'
|
|
echo "Number of elements: 4" # Hard-coded for illustration.
|
|
for (( i = 0 ; i < 4 ; i++ ))
|
|
do
|
|
echo "Element [$i]: ${array2[$i]}"
|
|
done
|
|
# See also the more general code example in basics-reviewed.bash.
|
|
|
|
|
|
declare -a dest
|
|
|
|
# Combine (append) two arrays into a third array.
|
|
echo
|
|
echo 'Conditions: Unquoted, default IFS, All-Elements-Of operator'
|
|
echo '- Undefined elements not present, subscripts not maintained. -'
|
|
# # The undefined elements do not exist; they are not being dropped.
|
|
|
|
dest=( ${array1[@]} ${array2[@]} )
|
|
# dest=${array1[@]}${array2[@]} # Strange results, possibly a bug.
|
|
|
|
# Now, list the result.
|
|
echo
|
|
echo '- - Testing Array Append - -'
|
|
cnt=${#dest[@]}
|
|
|
|
echo "Number of elements: $cnt"
|
|
for (( i = 0 ; i < cnt ; i++ ))
|
|
do
|
|
echo "Element [$i]: ${dest[$i]}"
|
|
done
|
|
|
|
# Assign an array to a single array element (twice).
|
|
dest[0]=${array1[@]}
|
|
dest[1]=${array2[@]}
|
|
|
|
# List the result.
|
|
echo
|
|
echo '- - Testing modified array - -'
|
|
cnt=${#dest[@]}
|
|
|
|
echo "Number of elements: $cnt"
|
|
for (( i = 0 ; i < cnt ; i++ ))
|
|
do
|
|
echo "Element [$i]: ${dest[$i]}"
|
|
done
|
|
|
|
# Examine the modified second element.
|
|
echo
|
|
echo '- - Reassign and list second element - -'
|
|
|
|
declare -a subArray=${dest[1]}
|
|
cnt=${#subArray[@]}
|
|
|
|
echo "Number of elements: $cnt"
|
|
for (( i = 0 ; i < cnt ; i++ ))
|
|
do
|
|
echo "Element [$i]: ${subArray[$i]}"
|
|
done
|
|
|
|
# The assignment of an entire array to a single element
|
|
#+ of another array using the '=${ ... }' array assignment
|
|
#+ has converted the array being assigned into a string,
|
|
#+ with the elements separated by a space (the first character of IFS).
|
|
|
|
# If the original elements didn't contain whitespace . . .
|
|
# If the original array isn't subscript sparse . . .
|
|
# Then we could get the original array structure back again.
|
|
|
|
# Restore from the modified second element.
|
|
echo
|
|
echo '- - Listing restored element - -'
|
|
|
|
declare -a subArray=( ${dest[1]} )
|
|
cnt=${#subArray[@]}
|
|
|
|
echo "Number of elements: $cnt"
|
|
for (( i = 0 ; i < cnt ; i++ ))
|
|
do
|
|
echo "Element [$i]: ${subArray[$i]}"
|
|
done
|
|
echo '- - Do not depend on this behavior. - -'
|
|
echo '- - This behavior is subject to change - -'
|
|
echo '- - in versions of Bash newer than version 2.05b - -'
|
|
|
|
# MSZ: Sorry about any earlier confusion folks.
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
>Arrays permit deploying old familiar algorithms as shell scripts.
|
|
Whether this is necessarily a good idea is left for the reader to
|
|
decide.</P
|
|
><P
|
|
><A
|
|
NAME="BUBBLESORT"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="BUBBLE"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-11. The Bubble Sort</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# bubble.sh: 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.
|
|
|
|
# Is it permissable to split an array variable over multiple lines
|
|
#+ using an escape (\)?
|
|
# Yes.
|
|
|
|
Countries=(Netherlands Ukraine Zaire Turkey Russia Yemen Syria \
|
|
Brazil Argentina Nicaragua Japan Mexico Venezuela Greece England \
|
|
Israel Peru Canada Oman Denmark Wales France Kenya \
|
|
Xanadu Qatar Liechtenstein Hungary)
|
|
|
|
# "Xanadu" is the mythical place where, according to Coleridge,
|
|
#+ Kubla Khan did a pleasure dome decree.
|
|
|
|
|
|
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
|
|
#+ within single brackets.
|
|
|
|
# if [[ ${Countries[$index]} > ${Countries[`expr $index + 1`]} ]]
|
|
#+ also works.
|
|
then
|
|
exchange $index `expr $index + 1` # Swap.
|
|
fi
|
|
let "index += 1" # Or, index+=1 on Bash, ver. 3.1 or newer.
|
|
done # End of inner loop
|
|
|
|
# ----------------------------------------------------------------------
|
|
# Paulo Marcel Coelho Aragao suggests for-loops as a simpler altenative.
|
|
#
|
|
# for (( last = $number_of_elements - 1 ; last > 0 ; last-- ))
|
|
## Fix by C.Y. Hunt ^ (Thanks!)
|
|
# do
|
|
# for (( i = 0 ; i < last ; i++ ))
|
|
# do
|
|
# [[ "${Countries[$i]}" > "${Countries[$((i+1))]}" ]] \
|
|
# && exchange $i $((i+1))
|
|
# done
|
|
# done
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
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</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYNEST"
|
|
></A
|
|
></P
|
|
><P
|
|
>Is it possible to nest arrays within arrays?</P
|
|
><P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# "Nested" array.
|
|
|
|
# Michael Zick provided this example,
|
|
#+ with corrections and clarifications by William Park.
|
|
|
|
AnArray=( $(ls --inode --ignore-backups --almost-all \
|
|
--directory --full-time --color=none --time=status \
|
|
--sort=time -l ${PWD} ) ) # Commands and options.
|
|
|
|
# Spaces are significant . . . and don't quote anything in the above.
|
|
|
|
SubArray=( ${AnArray[@]:11:1} ${AnArray[@]:6:5} )
|
|
# This array has six elements:
|
|
#+ SubArray=( [0]=${AnArray[11]} [1]=${AnArray[6]} [2]=${AnArray[7]}
|
|
# [3]=${AnArray[8]} [4]=${AnArray[9]} [5]=${AnArray[10]} )
|
|
#
|
|
# Arrays in Bash are (circularly) linked lists
|
|
#+ of type string (char *).
|
|
# So, this isn't actually a nested array,
|
|
#+ but it's functionally similar.
|
|
|
|
echo "Current directory and date of last status change:"
|
|
echo "${SubArray[@]}"
|
|
|
|
exit 0</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></P
|
|
><P
|
|
>--</P
|
|
><P
|
|
>Embedded arrays in combination with <A
|
|
HREF="bashver2.html#VARREFNEW"
|
|
>indirect references</A
|
|
> create some fascinating
|
|
possibilities</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYINDIR"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EMBARR"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-12. Embedded arrays and indirect references</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# embedded-arrays.sh
|
|
# Embedded arrays and indirect references.
|
|
|
|
# This script by Dennis Leeuw.
|
|
# Used with permission.
|
|
# Modified by document author.
|
|
|
|
|
|
ARRAY1=(
|
|
VAR1_1=value11
|
|
VAR1_2=value12
|
|
VAR1_3=value13
|
|
)
|
|
|
|
ARRAY2=(
|
|
VARIABLE="test"
|
|
STRING="VAR1=value1 VAR2=value2 VAR3=value3"
|
|
ARRAY21=${ARRAY1[*]}
|
|
) # Embed ARRAY1 within this second array.
|
|
|
|
function print () {
|
|
OLD_IFS="$IFS"
|
|
IFS=$'\n' # To print each array element
|
|
#+ on a separate line.
|
|
TEST1="ARRAY2[*]"
|
|
local ${!TEST1} # See what happens if you delete this line.
|
|
# Indirect reference.
|
|
# This makes the components of $TEST1
|
|
#+ accessible to this function.
|
|
|
|
|
|
# Let's see what we've got so far.
|
|
echo
|
|
echo "\$TEST1 = $TEST1" # Just the name of the variable.
|
|
echo; echo
|
|
echo "{\$TEST1} = ${!TEST1}" # Contents of the variable.
|
|
# That's what an indirect
|
|
#+ reference does.
|
|
echo
|
|
echo "-------------------------------------------"; echo
|
|
echo
|
|
|
|
|
|
# Print variable
|
|
echo "Variable VARIABLE: $VARIABLE"
|
|
|
|
# Print a string element
|
|
IFS="$OLD_IFS"
|
|
TEST2="STRING[*]"
|
|
local ${!TEST2} # Indirect reference (as above).
|
|
echo "String element VAR2: $VAR2 from STRING"
|
|
|
|
# Print an array element
|
|
TEST2="ARRAY21[*]"
|
|
local ${!TEST2} # Indirect reference (as above).
|
|
echo "Array element VAR1_1: $VAR1_1 from ARRAY21"
|
|
}
|
|
|
|
print
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# As the author of the script notes,
|
|
#+ "you can easily expand it to create named-hashes in bash."
|
|
# (Difficult) exercise for the reader: implement this.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
><A
|
|
NAME="PRIMES0"
|
|
></A
|
|
></P
|
|
><P
|
|
>Arrays enable implementing a shell script version of the
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>Sieve of Eratosthenes</I
|
|
>. 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.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EX68"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-13. The Sieve of Eratosthenes</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# sieve.sh (ex68.sh)
|
|
|
|
# Sieve of Eratosthenes
|
|
# Ancient algorithm for finding prime numbers.
|
|
|
|
# This runs a couple of orders of magnitude slower
|
|
#+ than the equivalent program written in C.
|
|
|
|
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. Why?
|
|
|
|
|
|
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
|
|
# 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
|
|
|
|
|
|
}
|
|
|
|
|
|
# ==============================================
|
|
# main ()
|
|
# Invoke the functions sequentially.
|
|
initialize
|
|
sift
|
|
print_primes
|
|
# This is what they call structured programming.
|
|
# ==============================================
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
|
|
|
|
# -------------------------------------------------------- #
|
|
# Code below line will not execute, because of 'exit.'
|
|
|
|
# This improved version of the Sieve, by Stephane Chazelas,
|
|
#+ executes somewhat faster.
|
|
|
|
# Must invoke with command-line argument (limit of primes).
|
|
|
|
UPPER_LIMIT=$1 # From command-line.
|
|
let SPLIT=UPPER_LIMIT/2 # Halfway to max number.
|
|
|
|
Primes=( '' $(seq $UPPER_LIMIT) )
|
|
|
|
i=1
|
|
until (( ( i += 1 ) > SPLIT )) # Need check only halfway.
|
|
do
|
|
if [[ -n ${Primes[i]} ]]
|
|
then
|
|
t=$i
|
|
until (( ( t += i ) > UPPER_LIMIT ))
|
|
do
|
|
Primes[t]=
|
|
done
|
|
fi
|
|
done
|
|
echo ${Primes[*]}
|
|
|
|
exit $?</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="EX68A"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-14. The Sieve of Eratosthenes, Optimized</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# Optimized Sieve of Eratosthenes
|
|
# Script by Jared Martin, with very minor changes by ABS Guide author.
|
|
# Used in ABS Guide with permission (thanks!).
|
|
|
|
# Based on script in Advanced Bash Scripting Guide.
|
|
# http://tldp.org/LDP/abs/html/arrays.html#PRIMES0 (ex68.sh).
|
|
|
|
# http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf (reference)
|
|
# Check results against http://primes.utm.edu/lists/small/1000.txt
|
|
|
|
# Necessary but not sufficient would be, e.g.,
|
|
# (($(sieve 7919 | wc -w) == 1000)) && echo "7919 is the 1000th prime"
|
|
|
|
UPPER_LIMIT=${1:?"Need an upper limit of primes to search."}
|
|
|
|
Primes=( '' $(seq ${UPPER_LIMIT}) )
|
|
|
|
typeset -i i t
|
|
Primes[i=1]='' # 1 is not a prime.
|
|
until (( ( i += 1 ) > (${UPPER_LIMIT}/i) )) # Need check only ith-way.
|
|
do # Why?
|
|
if ((${Primes[t=i*(i-1), i]}))
|
|
# Obscure, but instructive, use of arithmetic expansion in subscript.
|
|
then
|
|
until (( ( t += i ) > ${UPPER_LIMIT} ))
|
|
do Primes[t]=; done
|
|
fi
|
|
done
|
|
|
|
# echo ${Primes[*]}
|
|
echo # Change to original script for pretty-printing (80-col. display).
|
|
printf "%8d" ${Primes[*]}
|
|
echo; echo
|
|
|
|
exit $?</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>Compare these array-based prime number generators with
|
|
alternatives that do not use arrays, <A
|
|
HREF="contributed-scripts.html#PRIMES"
|
|
>Example A-15</A
|
|
>,
|
|
and <A
|
|
HREF="mathc.html#PRIMES2"
|
|
>Example 16-46</A
|
|
>.</P
|
|
><P
|
|
>--</P
|
|
><P
|
|
>Arrays lend themselves, to some extent, to emulating data
|
|
structures for which Bash has no native support.</P
|
|
><P
|
|
><A
|
|
NAME="STACKEX0"
|
|
></A
|
|
></P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="STACKEX"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-15. Emulating a push-down stack</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# stack.sh: push-down stack simulation
|
|
|
|
# Similar to the CPU stack, a push-down stack stores data items
|
|
#+ sequentially, but releases them in reverse order, last-in first-out.
|
|
|
|
|
|
BP=100 # Base Pointer of stack array.
|
|
# Begin at element 100.
|
|
|
|
SP=$BP # Stack Pointer.
|
|
# Initialize it to "base" (bottom) of stack.
|
|
|
|
Data= # Contents of stack location.
|
|
# Must use global variable,
|
|
#+ because of limitation on function return range.
|
|
|
|
|
|
# 100 Base pointer <-- Base Pointer
|
|
# 99 First data item
|
|
# 98 Second data item
|
|
# ... More data
|
|
# Last data item <-- Stack pointer
|
|
|
|
|
|
declare -a stack
|
|
|
|
|
|
push() # Push item on stack.
|
|
{
|
|
if [ -z "$1" ] # Nothing to push?
|
|
then
|
|
return
|
|
fi
|
|
|
|
let "SP -= 1" # Bump stack pointer.
|
|
stack[$SP]=$1
|
|
|
|
return
|
|
}
|
|
|
|
pop() # Pop item off stack.
|
|
{
|
|
Data= # Empty out data item.
|
|
|
|
if [ "$SP" -eq "$BP" ] # Stack empty?
|
|
then
|
|
return
|
|
fi # This also keeps SP from getting past 100,
|
|
#+ i.e., prevents a runaway stack.
|
|
|
|
Data=${stack[$SP]}
|
|
let "SP += 1" # Bump stack pointer.
|
|
return
|
|
}
|
|
|
|
status_report() # Find out what's happening.
|
|
{
|
|
echo "-------------------------------------"
|
|
echo "REPORT"
|
|
echo "Stack Pointer = $SP"
|
|
echo "Just popped \""$Data"\" off the stack."
|
|
echo "-------------------------------------"
|
|
echo
|
|
}
|
|
|
|
|
|
# =======================================================
|
|
# Now, for some fun.
|
|
|
|
echo
|
|
|
|
# See if you can pop anything off empty stack.
|
|
pop
|
|
status_report
|
|
|
|
echo
|
|
|
|
push garbage
|
|
pop
|
|
status_report # Garbage in, garbage out.
|
|
|
|
value1=23; push $value1
|
|
value2=skidoo; push $value2
|
|
value3=LAST; push $value3
|
|
|
|
pop # LAST
|
|
status_report
|
|
pop # skidoo
|
|
status_report
|
|
pop # 23
|
|
status_report # Last-in, first-out!
|
|
|
|
# Notice how the stack pointer decrements with each push,
|
|
#+ and increments with each pop.
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# =======================================================
|
|
|
|
|
|
# Exercises:
|
|
# ---------
|
|
|
|
# 1) Modify the "push()" function to permit pushing
|
|
# + multiple element on the stack with a single function call.
|
|
|
|
# 2) Modify the "pop()" function to permit popping
|
|
# + multiple element from the stack with a single function call.
|
|
|
|
# 3) Add error checking to the critical functions.
|
|
# That is, return an error code, depending on
|
|
# + successful or unsuccessful completion of the operation,
|
|
# + and take appropriate action.
|
|
|
|
# 4) Using this script as a starting point,
|
|
# + write a stack-based 4-function calculator.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
>Fancy manipulation of array <SPAN
|
|
CLASS="QUOTE"
|
|
>"subscripts"</SPAN
|
|
> may require
|
|
intermediate variables. For projects involving this, again consider
|
|
using a more powerful programming language, such as Perl or C.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="QFUNCTION"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-16. Complex array application:
|
|
<EM
|
|
>Exploring a weird mathematical series</EM
|
|
></B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
|
|
# Douglas Hofstadter's notorious "Q-series":
|
|
|
|
# Q(1) = Q(2) = 1
|
|
# Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), for n>2
|
|
|
|
# This is a "chaotic" integer series with strange
|
|
#+ and unpredictable behavior.
|
|
# The first 20 terms of the series are:
|
|
# 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12
|
|
|
|
# See Hofstadter's book, _Goedel, Escher, Bach: An Eternal Golden Braid_,
|
|
#+ p. 137, ff.
|
|
|
|
|
|
LIMIT=100 # Number of terms to calculate.
|
|
LINEWIDTH=20 # Number of terms printed per line.
|
|
|
|
Q[1]=1 # First two terms of series are 1.
|
|
Q[2]=1
|
|
|
|
echo
|
|
echo "Q-series [$LIMIT terms]:"
|
|
echo -n "${Q[1]} " # Output first two terms.
|
|
echo -n "${Q[2]} "
|
|
|
|
for ((n=3; n <= $LIMIT; n++)) # C-like loop expression.
|
|
do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n>2
|
|
# Need to break the expression into intermediate terms,
|
|
#+ since Bash doesn't handle complex array arithmetic very well.
|
|
|
|
let "n1 = $n - 1" # n-1
|
|
let "n2 = $n - 2" # n-2
|
|
|
|
t0=`expr $n - ${Q[n1]}` # n - Q[n-1]
|
|
t1=`expr $n - ${Q[n2]}` # n - Q[n-2]
|
|
|
|
T0=${Q[t0]} # Q[n - Q[n-1]]
|
|
T1=${Q[t1]} # Q[n - Q[n-2]]
|
|
|
|
Q[n]=`expr $T0 + $T1` # Q[n - Q[n-1]] + Q[n - Q[n-2]]
|
|
echo -n "${Q[n]} "
|
|
|
|
if [ `expr $n % $LINEWIDTH` -eq 0 ] # Format output.
|
|
then # ^ modulo
|
|
echo # Break lines into neat chunks.
|
|
fi
|
|
|
|
done
|
|
|
|
echo
|
|
|
|
exit 0
|
|
|
|
# This is an iterative implementation of the Q-series.
|
|
# The more intuitive recursive implementation is left as an exercise.
|
|
# Warning: calculating this series recursively takes a VERY long time
|
|
#+ via a script. C/C++ would be orders of magnitude faster.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>--</P
|
|
><P
|
|
><A
|
|
NAME="ARRAYMULTIDIM"
|
|
></A
|
|
></P
|
|
><P
|
|
>Bash supports only one-dimensional arrays, though a little
|
|
trickery permits simulating multi-dimensional ones.</P
|
|
><DIV
|
|
CLASS="EXAMPLE"
|
|
><A
|
|
NAME="TWODIM"
|
|
></A
|
|
><P
|
|
><B
|
|
>Example 27-17. Simulating a two-dimensional array, then tilting it</B
|
|
></P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>#!/bin/bash
|
|
# twodim.sh: Simulating a two-dimensional array.
|
|
|
|
# A one-dimensional array consists of a single row.
|
|
# A two-dimensional array stores rows sequentially.
|
|
|
|
Rows=5
|
|
Columns=5
|
|
# 5 X 5 Array.
|
|
|
|
declare -a alpha # char alpha [Rows] [Columns];
|
|
# Unnecessary declaration. Why?
|
|
|
|
load_alpha ()
|
|
{
|
|
local rc=0
|
|
local index
|
|
|
|
for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y
|
|
do # Use different symbols if you like.
|
|
local row=`expr $rc / $Columns`
|
|
local column=`expr $rc % $Rows`
|
|
let "index = $row * $Rows + $column"
|
|
alpha[$index]=$i
|
|
# alpha[$row][$column]
|
|
let "rc += 1"
|
|
done
|
|
|
|
# Simpler would be
|
|
#+ declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y )
|
|
#+ but this somehow lacks the "flavor" of a two-dimensional array.
|
|
}
|
|
|
|
print_alpha ()
|
|
{
|
|
local row=0
|
|
local index
|
|
|
|
echo
|
|
|
|
while [ "$row" -lt "$Rows" ] # Print out in "row major" order:
|
|
do #+ columns vary,
|
|
#+ while row (outer loop) remains the same.
|
|
local column=0
|
|
|
|
echo -n " " # Lines up "square" array with rotated one.
|
|
|
|
while [ "$column" -lt "$Columns" ]
|
|
do
|
|
let "index = $row * $Rows + $column"
|
|
echo -n "${alpha[index]} " # alpha[$row][$column]
|
|
let "column += 1"
|
|
done
|
|
|
|
let "row += 1"
|
|
echo
|
|
|
|
done
|
|
|
|
# The simpler equivalent is
|
|
# echo ${alpha[*]} | xargs -n $Columns
|
|
|
|
echo
|
|
}
|
|
|
|
filter () # Filter out negative array indices.
|
|
{
|
|
|
|
echo -n " " # Provides the tilt.
|
|
# Explain how.
|
|
|
|
if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]]
|
|
then
|
|
let "index = $1 * $Rows + $2"
|
|
# Now, print it rotated.
|
|
echo -n " ${alpha[index]}"
|
|
# alpha[$row][$column]
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rotate () # Rotate the array 45 degrees --
|
|
{ #+ "balance" it on its lower lefthand corner.
|
|
local row
|
|
local column
|
|
|
|
for (( row = Rows; row > -Rows; row-- ))
|
|
do # Step through the array backwards. Why?
|
|
|
|
for (( column = 0; column < Columns; column++ ))
|
|
do
|
|
|
|
if [ "$row" -ge 0 ]
|
|
then
|
|
let "t1 = $column - $row"
|
|
let "t2 = $column"
|
|
else
|
|
let "t1 = $column"
|
|
let "t2 = $column + $row"
|
|
fi
|
|
|
|
filter $t1 $t2 # Filter out negative array indices.
|
|
# What happens if you don't do this?
|
|
done
|
|
|
|
echo; echo
|
|
|
|
done
|
|
|
|
# Array rotation inspired by examples (pp. 143-146) in
|
|
#+ "Advanced C Programming on the IBM PC," by Herbert Mayer
|
|
#+ (see bibliography).
|
|
# This just goes to show that much of what can be done in C
|
|
#+ can also be done in shell scripting.
|
|
|
|
}
|
|
|
|
|
|
#--------------- Now, let the show begin. ------------#
|
|
load_alpha # Load the array.
|
|
print_alpha # Print it out.
|
|
rotate # Rotate it 45 degrees counterclockwise.
|
|
#-----------------------------------------------------#
|
|
|
|
exit 0
|
|
|
|
# This is a rather contrived, not to mention inelegant simulation.
|
|
|
|
# Exercises:
|
|
# ---------
|
|
# 1) Rewrite the array loading and printing functions
|
|
# in a more intuitive and less kludgy fashion.
|
|
#
|
|
# 2) Figure out how the array rotation functions work.
|
|
# Hint: think about the implications of backwards-indexing an array.
|
|
#
|
|
# 3) Rewrite this script to handle a non-square array,
|
|
# such as a 6 X 4 one.
|
|
# Try to minimize "distortion" when the array is rotated.</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
>A two-dimensional array is essentially equivalent to a
|
|
one-dimensional one, but with additional addressing modes
|
|
for referencing and manipulating the individual elements by
|
|
<I
|
|
CLASS="FIRSTTERM"
|
|
>row</I
|
|
> and <I
|
|
CLASS="FIRSTTERM"
|
|
>column</I
|
|
>
|
|
position.</P
|
|
><P
|
|
>For an even more elaborate example of simulating a
|
|
two-dimensional array, see <A
|
|
HREF="contributed-scripts.html#LIFESLOW"
|
|
>Example A-10</A
|
|
>.</P
|
|
><P
|
|
>--</P
|
|
><P
|
|
>For more interesting scripts using arrays, see:
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="commandsub.html#AGRAM2"
|
|
>Example 12-3</A
|
|
></P
|
|
></LI
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="mathc.html#PRIMES2"
|
|
>Example 16-46</A
|
|
></P
|
|
></LI
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="contributed-scripts.html#HASHEX2"
|
|
>Example A-22</A
|
|
></P
|
|
></LI
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="contributed-scripts.html#HOMEWORK"
|
|
>Example A-44</A
|
|
></P
|
|
></LI
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="contributed-scripts.html#QKY"
|
|
>Example A-41</A
|
|
></P
|
|
></LI
|
|
><LI
|
|
><P
|
|
><A
|
|
HREF="contributed-scripts.html#NIM"
|
|
>Example A-42</A
|
|
></P
|
|
></LI
|
|
></UL
|
|
>
|
|
</P
|
|
></DIV
|
|
><DIV
|
|
CLASS="NAVFOOTER"
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"><TABLE
|
|
SUMMARY="Footer navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="list-cons.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="index.html"
|
|
ACCESSKEY="H"
|
|
>Home</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="ivr.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
>List Constructs</TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="part5.html"
|
|
ACCESSKEY="U"
|
|
>Up</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
>Indirect References</TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></BODY
|
|
></HTML
|
|
> |