LDP/LDP/guide/docbook/Bash-Beginners-Guide/chap10.xml

443 lines
26 KiB
XML

<chapter id="chap_10">
<title>More on variables</title>
<abstract>
<para>In this chapter, we will discuss the advanced use of variables and arguments. Upon completion, you will be able to:</para>
<para>
<itemizedlist>
<listitem><para>Declare and use an array of variables</para></listitem>
<listitem><para>Specify the sort of variable you want to use</para></listitem>
<listitem><para>Make variables read-only</para></listitem>
<listitem><para>Use <command>set</command> to assign a value to a variable</para></listitem>
</itemizedlist>
</para>
</abstract>
<sect1 id="sect_10_01"><title>Types of variables</title>
<sect2 id="sect_10_01_01"><title>General assignment of values</title>
<para>As we already saw, Bash understands many different kinds of variables or parameters. Thus far, we haven't bothered much with what kind of variables we assigned, so our variables could hold any value that we assigned to them. A simple command line example demonstrates this:</para>
<screen>
<prompt>[bob in ~]</prompt> <command><varname>VARIABLE</varname>=<parameter>12</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>$VARIABLE</varname></command>
12
<prompt>[bob in ~]</prompt> <command><varname>VARIABLE</varname>=<parameter>string</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>$VARIABLE</varname></command>
string
</screen>
<para>There are cases when you want to avoid this kind of behavior, for instance when handling telephone and other numbers. Apart from integers and variables, you may also want to specify a variable that is a constant. This is often done at the beginning of a script, when the value of the constant is declared. After that, there are only references to the constant variable name, so that when the constant needs to be changed, it only has to be done once. A variable may also be a series of variables of any type, a so-called <emphasis>array</emphasis> of variables (<varname>VAR0</varname><varname>VAR1</varname>, <varname>VAR2</varname>, ... <varname>VARN</varname>).</para>
</sect2>
<sect2 id="sect_10_01_02"><title>Using the declare built-in</title>
<para>Using a <command>declare</command> statement, we can limit the value assignment to variables.</para>
<para>The syntax for <command>declare</command> is the following:</para>
<cmdsynopsis><command>declare <option>OPTION(s)</option> <varname>VARIABLE</varname>=value</command></cmdsynopsis>
<para>The following options are used to determine the type of data the variable can hold and to assign it attributes:</para>
<table id="tab_10_01" frame="all"><title>Options to the declare built-in</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<thead>
<row><entry>Option</entry><entry>Meaning</entry></row>
</thead>
<tbody>
<row><entry><option>-a</option></entry><entry>Variable is an array.</entry></row>
<row><entry><option>-f</option></entry><entry>Use function names only.</entry></row>
<row><entry><option>-i</option></entry><entry>The variable is to be treated as an integer; arithmetic evaluation is performed when the variable is assigned a value (see <xref linkend="sect_03_04_05" />).</entry></row>
<row><entry><option>-p</option></entry><entry>Display the attributes and values of each variable. When <option>-p</option> is used, additional options are ignored.</entry></row>
<row><entry><option>-r</option></entry><entry>Make variables read-only. These variables cannot then be assigned values by subsequent assignment statements, nor can they be unset.</entry></row>
<row><entry><option>-t</option></entry><entry>Give each variable the <emphasis>trace</emphasis> attribute.</entry></row>
<row><entry><option>-x</option></entry><entry>Mark each variable for export to subsequent commands via the environment.</entry></row>
</tbody>
</tgroup>
</table>
<para>Using <option>+</option> instead of <option>-</option> turns off the attribute instead. When used in a function, <command>declare</command> creates local variables.</para>
<para>The following example shows how assignment of a type to a variable influences the value.</para>
<screen>
<prompt>[bob in ~]</prompt> <command>declare <option>-i</option> <varname>VARIABLE</varname>=<parameter>12</parameter></command>
<prompt>[bob in ~]</prompt> <command><varname>VARIABLE</varname>=<parameter>string</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>$VARIABLE</varname></command>
0
<prompt>[bob in ~]</prompt> <command>declare <option>-p</option> <varname>VARIABLE</varname></command>
declare -i VARIABLE="0"
</screen>
<para>Note that Bash has an option to declare a numeric value, but none for declaring string values. This is because, by default, if no specifications are given, a variable can hold any type of data:</para>
<screen>
<prompt>[bob in ~]</prompt> <command><varname>OTHERVAR</varname>=<parameter>blah</parameter></command>
<prompt>[bob in ~]</prompt> <command>declare <option>-p</option> <varname>OTHERVAR</varname></command>
declare -- OTHERVAR="blah"
</screen>
<para>As soon as you restrict assignment of values to a variable, it can only hold that type of data. Possible restrictions are either integer, constant or array.</para>
<para>See the Bash info pages for information on return status.</para>
</sect2>
<sect2 id="sect_10_01_03"><title>Constants</title>
<para>In Bash, constants are created by making a variable read-only. The <command>readonly</command> built-in marks each specified variable as unchangeable. The syntax is:</para>
<cmdsynopsis><command>readonly <option>OPTION</option> <varname>VARIABLE(s)</varname></command></cmdsynopsis>
<para>The values of these variables can then no longer be changed by subsequent assignment. If the <option>-f</option> option is given, each variable refers to a shell function; see <xref linkend="chap_11" />. If <option>-a</option> is specified, each variable refers to an array of variables. If no arguments are given, or if <option>-p</option> is supplied, a list of all read-only variables is displayed. Using the <option>-p</option> option, the output can be reused as input.</para>
<para>The return status is zero, unless an invalid option was specified, one of the variables or functions does not exist, or <option>-f</option> was supplied for a variable name instead of for a function name.</para>
<screen>
<prompt>[bob in ~]</prompt> <command>readonly <varname>TUX</varname>=<parameter>penguinpower</parameter></command>
<prompt>[bob in ~]</prompt> <command><varname>TUX</varname>=<parameter>Mickeysoft</parameter></command>
bash: TUX: readonly variable
</screen>
</sect2>
</sect1>
<sect1 id="sect_10_02"><title>Array variables</title>
<sect2 id="sect_10_02_01"><title>Creating arrays</title>
<para>An array is a variable containing multiple values. Any variable may be used as an array. There is no maximum limit to the size of an array, nor any requirement that member variables be indexed or assigned contiguously. Arrays are zero-based: the first element is indexed with the number 0.</para>
<para>Indirect declaration is done using the following syntax to declare a variable:</para>
<cmdsynopsis><command><varname>ARRAY[INDEXNR]</varname>=value</command></cmdsynopsis>
<para>The <emphasis>INDEXNR</emphasis> is treated as an arithmetic expression that must evaluate to a positive number.</para>
<para>Explicit declaration of an array is done using the <command>declare</command> built-in:</para>
<cmdsynopsis><command>declare <option>-a</option> <varname>ARRAYNAME</varname></command></cmdsynopsis>
<para>A declaration with an index number will also be accepted, but the index number will be ignored. Attributes to the array may be specified using the <command>declare</command> and <command>readonly</command> built-ins. Attributes apply to all variables in the array; you can't have mixed arrays.</para>
<para>Array variables may also be created using compound assignments in this format:</para>
<cmdsynopsis><command><varname>ARRAY</varname>=(value1 value2 ... valueN)</command></cmdsynopsis>
<para>Each value is then in the form of <emphasis>[indexnumber=]string</emphasis>. The index number is optional. If it is supplied, that index is assigned to it; otherwise the index of the element assigned is the number of the last index that was assigned, plus one. This format is accepted by <command>declare</command> as well. If no index numbers are supplied, indexing starts at zero.</para>
<para>Adding missing or extra members in an array is done using the syntax:</para>
<cmdsynopsis><command><varname>ARRAYNAME[indexnumber]</varname>=value</command></cmdsynopsis>
<para>Remember that the <command>read</command> built-in provides the <option>-a</option> option, which allows for reading and assigning values for member variables of an array.</para>
</sect2>
<sect2 id="sect_10_02_02"><title>Dereferencing the variables in an array</title>
<para>In order to refer to the content of an item in an array, use curly braces. This is necessary, as you can see from the following example, to bypass the shell interpretation of expansion operators. If the index number is <emphasis>@</emphasis> or <emphasis>*</emphasis>, all members of an array are referenced.</para>
<screen>
<prompt>[bob in ~]</prompt> <command><varname>ARRAY</varname>=<parameter>(one two three)</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]}</varname></command>
one two three
<prompt>[bob in ~]</prompt> <command>echo <varname>$ARRAY[*]</varname></command>
one[*]
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[2]}</varname></command>
three
<prompt>[bob in ~]</prompt> <command><varname>ARRAY[3]</varname>=<parameter>four</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]}</varname></command>
one two three four
</screen>
<para>Referring to the content of a member variable of an array without providing an index number is the same as referring to the content of the first element, the one referenced with index number zero.</para>
</sect2>
<sect2 id="sect_10_02_03"><title>Deleting array variables</title>
<para>The <command>unset</command> built-in is used to destroy arrays or member variables of an array:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>unset <varname>ARRAY[1]</varname></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]}</varname></command>
one three four
<prompt>[bob in ~]</prompt> <command>unset <varname>ARRAY</varname></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]}</varname></command>
&lt;--no output--&gt;
</screen>
</sect2>
<sect2 id="sect_10_02_04"><title>Examples of arrays</title>
<para>Practical examples of the usage of arrays are hard to find. You will find plenty of scripts that don't really do anything on your system but that do use arrays to calculate mathematical series, for instance. And that would be one of the more interesting examples...most scripts just show what you can do with an array in an oversimplified and theoretical way.</para>
<para>The reason for this dullness is that arrays are rather complex structures. You will find that most practical examples for which arrays could be used are already implemented on your system using arrays, however on a lower level, in the C programming language in which most UNIX commands are written. A good example is the Bash <command>history</command> built-in command. Those readers who are interested might check the <filename>built-ins</filename> directory in the Bash source tree and take a look at <filename>fc.def</filename>, which is processed when compiling the built-ins.</para>
<para>Another reason good examples are hard to find is that not all shells support arrays, so they break compatibility.</para>
<para>After long days of searching, I finally found this example operating at an Internet provider. It distributes Apache web server configuration files onto hosts in a web farm:</para>
<screen>
#!/bin/bash
# $Id$
# $Log$
# Revision 1.5 2004/12/06 12:27:09 tille
# changes for new domainname, minor corrections
#
# Revision 1.6 2004/10/18 18:58:06 tille
# debugging, typos removed, replaced screenshots in chap9 with screen sections.
#
# Revision 1.5 2004/06/24 14:02:48 tille
# dded tracer image
#
# Revision 1.4 2004/06/15 08:47:12 tille
# more markup, index
#
# Revision 1.3 2004/05/22 13:34:18 tille
# review for fultus
#
# Revision 1.2 2004/04/26 13:24:41 tille
# updates by tabatha
#
# Revision 1.1.1.1 2004/02/11 16:59:50 tille
# initiele bash import
#
# Revision 1.3 2003/02/05 09:52:53 mbounine
# httpd restarting added.
#
# Revision 1.2 2003/02/05 08:11:32 mbounine
# Bug fixes.
#
# Revision 1.1 2003/02/04 15:41:35 mbounine
# Script for syncing httpd config between web farm hosts.
# Initial release.
#
if [ $(whoami) != 'root' ]; then
echo "Must be root to run $0"
exit 1;
fi
if [ -z $1 ]; then
echo "Usage: $0 &lt;/path/to/httpd.conf&gt;"
exit 1
fi
httpd_conf_new=$1
httpd_conf_path="/usr/local/apache/conf"
login=htuser
farm_hosts=(web03 web04 web05 web06 web07)
for i in ${farm_hosts[@]}; do
su $login -c "scp $httpd_conf_new ${i}:${httpd_conf_path}"
su $login -c "ssh $i sudo /usr/local/apache/bin/apachectl graceful"
done
exit 0
</screen>
<para>First two tests are performed to check whether the correct user is running the script with the correct arguments. The names of the hosts that need to be configured are listed in the array <varname>farm_hosts</varname>. Then all these hosts are provided with the Apache configuration file, after which the daemon is restarted. Note the use of commands from the Secure Shell suite, encrypting the connections to remote hosts.</para>
<para>Thanks, Eugene and colleague, for this contribution.</para>
<para>Dan Richter contributed the following example. This is the problem he was confronted with:</para>
<para><quote>...In my company, we have demos on our web site, and every week someone has to test all of them. So I have a cron job that fills an array with the possible candidates, uses <command>date <option>+%W</option></command> to find the week of the year, and does a modulo operation to find the correct index. The lucky person gets notified by e-mail.</quote></para>
<para>And this was his way of solving it:</para>
<screen>
#!/bin/bash
# This is get-tester-address.sh
#
# First, we test whether bash supports arrays.
# (Support for arrays was only added recently.)
#
whotest[0]='test' || (echo 'Failure: arrays not supported in this version of
bash.' &amp;&amp; exit 2)
#
# Our list of candidates. (Feel free to add or
# remove candidates.)
#
wholist=(
'Bob Smith &lt;bob@example.com&gt;'
'Jane L. Williams &lt;jane@example.com&gt;'
'Eric S. Raymond &lt;esr@example.com&gt;'
'Larry Wall &lt;wall@example.com&gt;'
'Linus Torvalds &lt;linus@example.com&gt;'
)
#
# Count the number of possible testers.
# (Loop until we find an empty string.)
#
count=0
while [ "x${wholist[count]}" != "x" ]
do
count=$(( $count + 1 ))
done
#
# Now we calculate whose turn it is.
#
week=`date '+%W'` # The week of the year (0..53).
week=${week#0} # Remove possible leading zero.
let "index = $week % $count" # week modulo count = the lucky person
email=${wholist[index]} # Get the lucky person's e-mail address.
echo $email # Output the person's e-mail address.
</screen>
<para>This script is then used in other scripts, such as this one, which uses a <emphasis>here</emphasis> document:</para>
<screen>
email=`get-tester-address.sh` # Find who to e-mail.
hostname=`hostname` # This machine's name.
#
# Send e-mail to the right person.
#
mail $email -s '[Demo Testing]' &lt;&lt;EOF
The lucky tester this week is: $email
Reminder: the list of demos is here:
http://web.example.com:8080/DemoSites
(This e-mail was generated by $0 on ${hostname}.)
EOF
</screen>
</sect2>
</sect1>
<sect1 id="sect_10_03"><title>Operations on variables</title>
<sect2 id="sect_10_03_01"><title>Arithmetic on variables</title>
<para>We discussed this already in <xref linkend="sect_03_04_05" />.</para>
</sect2>
<sect2 id="sect_10_03_02"><title>Length of a variable</title>
<para>Using the <command>${#<varname>VAR</varname>}</command> syntax will calculate the number of characters in a variable. If <varname>VAR</varname> is <quote>*</quote> or <quote>@</quote>, this value is substituted with the number of positional parameters or number of elements in an array in general. This is demonstrated in the example below:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>$SHELL</varname></command>
/bin/bash
<prompt>[bob in ~]</prompt> <command>echo <varname>${#SHELL}</varname></command>
9
<prompt>[bob in ~]</prompt> <command><varname>ARRAY</varname>=<parameter>(one two three)</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${#ARRAY}</varname></command>
3
</screen>
</sect2>
<sect2 id="sect_10_03_03"><title>Transformations of variables</title>
<sect3 id="sect_10_03_03_01"><title>Substitution</title>
<cmdsynopsis><command>${<varname>VAR</varname>:-<parameter>WORD</parameter>}</command></cmdsynopsis>
<para>If <varname>VAR</varname> is not defined or null, the expansion of <parameter>WORD</parameter> is substituted; otherwise the value of <varname>VAR</varname> is substituted:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>${TEST:-test}</varname></command>
test
<prompt>[bob in ~]</prompt> <command>echo <varname>$TEST</varname></command>
<prompt>[bob in ~]</prompt> <command>export <varname>TEST</varname>=<parameter>a_string</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${TEST:-test}</varname></command>
a_string
<prompt>[bob in ~]</prompt> <command>echo <varname>${TEST2:-$TEST}</varname></command>
a_string
</screen>
<para>This form is often used in conditional tests, for instance in this one:</para>
<screen>
<command><parameter>[ -z "${COLUMNS:-}" ]</parameter> &amp;&amp; <varname>COLUMNS</varname>=<parameter>80</parameter></command>
</screen>
<para>It is a shorter notation for</para>
<screen>
<command>if <parameter>[ -z "${COLUMNS:-}" ]</parameter>; then
<varname>COLUMNS</varname>=<parameter>80</parameter>
fi</command>
</screen>
<para>See <xref linkend="sect_07_01_02_03" /> for more information about this type of condition testing.</para>
<para>If the hyphen (-) is replaced with the equal sign (=), the value is assigned to the parameter if it does not exist:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>$TEST2</varname></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${TEST2:=$TEST}</varname></command>
a_string
<prompt>[bob in ~]</prompt> <command>echo <varname>$TEST2</varname></command>
a_string
</screen>
<para>The following syntax tests the existence of a variable. If it is not set, the expansion of <parameter>WORD</parameter> is printed to standard out and non-interactive shells quit. A demonstration:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>cat <filename>vartest.sh</filename></command>
#!/bin/bash
# This script tests whether a variable is set. If not,
# it exits printing a message.
echo ${TESTVAR:?"There's so much I still wanted to do..."}
echo "TESTVAR is set, we can proceed."
<prompt>[bob in testdir]</prompt> <command>./vartest.sh</command>
./vartest.sh: line 6: TESTVAR: There's so much I still wanted to do...
<prompt>[bob in testdir]</prompt> <command>export <varname>TESTVAR</varname>=<parameter>present</parameter></command>
<prompt>[bob in testdir]</prompt> <command>./vartest.sh</command>
present
TESTVAR is set, we can proceed.
</screen>
<para>Using <quote>+</quote> instead of the exclamation mark sets the variable to the expansion of <parameter>WORD</parameter>; if it does not exist, nothing happens.</para>
</sect3>
<sect3 id="sect_10_03_03_02"><title>Removing substrings</title>
<para>To strip a number of characters, equal to <parameter>OFFSET</parameter>, from a variable, use this syntax:</para>
<cmdsynopsis><command>${<varname>VAR</varname>:<parameter>OFFSET</parameter>:<parameter>LENGTH</parameter>}</command></cmdsynopsis>
<para>The <parameter>LENGTH</parameter> parameter defines how many characters to keep, starting from the first character after the offset point. If <parameter>LENGTH</parameter> is omitted, the remainder of the variable content is taken:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>export <varname>STRING</varname>=<parameter>"thisisaverylongname"</parameter></command>
<prompt>[bob in ~]</prompt> <command>echo <varname>${STRING:4}</varname></command>
isaverylongname
<prompt>[bob in ~]</prompt> <command>echo <varname>${STRING:6:5}</varname></command>
avery
</screen>
<cmdsynopsis><command>${<varname>VAR</varname>#<parameter>WORD</parameter>}</command></cmdsynopsis>
<para>and</para>
<cmdsynopsis><command>${<varname>VAR</varname>##<parameter>WORD</parameter>}</command></cmdsynopsis>
<para>These constructs are used for deleting the pattern matching the expansion of <parameter>WORD</parameter> in <varname>VAR</varname>. <parameter>WORD</parameter> is expanded to produce a pattern just as in file name expansion. If the pattern matches the beginning of the expanded value of <varname>VAR</varname>, then the result of the expansion is the expanded value of <varname>VAR</varname> with the shortest matching pattern (<quote>#</quote>) or the longest matching pattern (indicated with <quote>##</quote>).</para>
<para>If <varname>VAR</varname> is <varname>*</varname> or <varname>@</varname>, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list.</para>
<para>If <varname>VAR</varname> is an array variable subscribed with <quote>*</quote> or <quote>@</quote>, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list. This is shown in the examples below:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]}</varname></command>
one two one three one four
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]#one}</varname></command>
two three four
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]#t}</varname></command>
one wo one hree one four
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]#t*}</varname></command>
one wo one hree one four
<prompt>[bob in ~]</prompt> <command>echo <varname>${ARRAY[*]##t*}</varname></command>
one one one four
</screen>
<para>The opposite effect is obtained using <quote>%</quote> and <quote>%%</quote>, as in this example below. <parameter>WORD</parameter> should match a trailing portion of string:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>$STRING</varname></command>
thisisaverylongname
<prompt>[bob in ~]</prompt> <command>echo <varname>${STRING%name}</varname></command>
thisisaverylong
</screen>
</sect3>
<sect3 id="sect_10_03_03_03"><title>Replacing parts of variable names</title>
<para>This is done using the</para>
<cmdsynopsis><command>${<varname>VAR</varname>/<parameter>PATTERN</parameter>/<parameter>STRING</parameter>}</command></cmdsynopsis>
<para>or</para>
<cmdsynopsis><command>${<varname>VAR</varname>//<parameter>PATTERN</parameter>/<parameter>STRING</parameter>}</command></cmdsynopsis>
<para>syntax. The first form replaces only the first match, the second replaces all matches of <parameter>PATTERN</parameter> with <parameter>STRING</parameter>:</para>
<screen>
<prompt>[bob in ~]</prompt> <command>echo <varname>${STRING/name/string}</varname></command>
thisisaverylongstring
</screen>
<para>More information can be found in the Bash info pages.</para>
</sect3>
</sect2>
</sect1>
<sect1 id="sect_10_04"><title>Summary</title>
<para>Normally, a variable can hold any type of data, unless variables are declared explicitly. Constant variables are set using the <command>readonly</command> built-in command.</para>
<para>An array holds a set of variables. If a type of data is declared, then all elements in the array will be set to hold only this type of data.</para>
<para>Bash features allow for substitution and transformation of variables <quote>on the fly</quote>. Standard operations include calculating the length of a variable, arithmetic on variables, substituting variable content and substituting part of the content.</para>
</sect1>
<sect1 id="sect_10_05"><title>Exercises</title>
<para>Here are some brain crackers:</para>
<orderedlist>
<listitem><para>Write a script that does the following:</para>
<itemizedlist>
<listitem><para>Display the name of the script being executed.</para></listitem>
<listitem><para>Display the first, third and tenth argument given to the script.</para></listitem>
<listitem><para>Display the total number of arguments passed to the script.</para></listitem>
<listitem><para>If there were more than three positional parameters, use <command>shift</command> to move all the values 3 places to the left.</para></listitem>
<listitem><para>Print all the values of the remaining arguments.</para></listitem>
<listitem><para>Print the number of arguments.</para></listitem>
</itemizedlist>
<para>Test with zero, one, three and over ten arguments.</para>
</listitem>
<listitem><para>Write a script that implements a simple web browser (in text mode), using <command>wget</command> and <command>links <option>-dump</option></command> to display HTML pages to the user. The user has 3 choices: enter a URL, enter <keycap>b</keycap> for back and <keycap>q</keycap> to quit. The last 10 URLs entered by the user are stored in an array, from which the user can restore the URL by using the <emphasis>back</emphasis> functionality.</para></listitem>
</orderedlist>
</sect1>
</chapter>