old-www/LDP/abs/html/list-cons.html

688 lines
11 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>List Constructs</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="Aliases"
HREF="aliases.html"><LINK
REL="NEXT"
TITLE="Arrays"
HREF="arrays.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="aliases.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="arrays.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="CHAPTER"
><H1
><A
NAME="LIST-CONS"
></A
>Chapter 26. List Constructs</H1
><P
><A
NAME="LISTCONSREF"
></A
></P
><P
>The <I
CLASS="FIRSTTERM"
>and list</I
> and <I
CLASS="FIRSTTERM"
>or
list</I
> constructs provide a means of processing a
number of commands consecutively. These can effectively replace
complex nested <A
HREF="testconstructs.html#TESTCONSTRUCTS1"
>if/then</A
>
or even <A
HREF="testbranch.html#CASEESAC1"
>case</A
> statements.</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="LCONS1"
></A
>Chaining together commands</B
></P
><DL
><DT
>and list</DT
><DD
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>command-1 &#38;&#38; command-2 &#38;&#38; command-3 &#38;&#38; ... command-n</PRE
></FONT
></TD
></TR
></TABLE
>
Each command executes in turn, provided that
the previous command has given a return value of
<TT
CLASS="REPLACEABLE"
><I
>true</I
></TT
> (zero). At the first
<TT
CLASS="REPLACEABLE"
><I
>false</I
></TT
> (non-zero) return, the
command chain terminates (the first command returning
<TT
CLASS="REPLACEABLE"
><I
>false</I
></TT
> is the last one to
execute).</P
><P
>An interesting use of a two-condition <I
CLASS="FIRSTTERM"
>and
list</I
> from an early version of YongYe's <A
HREF="http://bash.deta.in/Tetris_Game.sh"
TARGET="_top"
>Tetris
game script</A
>:</P
><P
> <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>equation()
{ # core algorithm used for doubling and halving the coordinates
[[ ${cdx} ]] &#38;&#38; ((y=cy+(ccy-cdy)${2}2))
eval ${1}+=\"${x} ${y} \"
}</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX64"
></A
><P
><B
>Example 26-1. Using an <I
CLASS="FIRSTTERM"
>and list</I
> to test
for command-line arguments</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# and list
if [ ! -z "$1" ] &#38;&#38; echo "Argument #1 = $1" &#38;&#38; [ ! -z "$2" ] &#38;&#38; \
# ^^ ^^ ^^
echo "Argument #2 = $2"
then
echo "At least 2 arguments passed to script."
# All the chained commands return true.
else
echo "Fewer than 2 arguments passed to script."
# At least one of the chained commands returns false.
fi
# Note that "if [ ! -z $1 ]" works, but its alleged equivalent,
# "if [ -n $1 ]" does not.
# However, quoting fixes this.
# if "[ -n "$1" ]" works.
# ^ ^ Careful!
# It is always best to QUOTE the variables being tested.
# This accomplishes the same thing, using "pure" if/then statements.
if [ ! -z "$1" ]
then
echo "Argument #1 = $1"
fi
if [ ! -z "$2" ]
then
echo "Argument #2 = $2"
echo "At least 2 arguments passed to script."
else
echo "Fewer than 2 arguments passed to script."
fi
# It's longer and more ponderous than using an "and list".
exit $?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="ANDLIST2"
></A
><P
><B
>Example 26-2. Another command-line arg test using an <I
CLASS="FIRSTTERM"
>and
list</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
ARGS=1 # Number of arguments expected.
E_BADARGS=85 # Exit value if incorrect number of args passed.
test $# -ne $ARGS &#38;&#38; \
# ^^^^^^^^^^^^ condition #1
echo "Usage: `basename $0` $ARGS argument(s)" &#38;&#38; exit $E_BADARGS
# ^^
# If condition #1 tests true (wrong number of args passed to script),
#+ then the rest of the line executes, and script terminates.
# Line below executes only if the above test fails.
echo "Correct number of arguments passed to this script."
exit 0
# To check exit value, do a "echo $?" after script termination.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="ANDDEFAULT"
></A
></P
><P
> Of course, an <I
CLASS="FIRSTTERM"
>and list</I
> can also
<I
CLASS="FIRSTTERM"
>set</I
> variables to a default value.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>arg1=$@ &#38;&#38; [ -z "$arg1" ] &#38;&#38; arg1=DEFAULT
# Set $arg1 to command-line arguments, if any.
# But . . . set to DEFAULT if not specified on command-line.</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></DD
><DT
><A
NAME="ORLISTREF"
></A
>or list</DT
><DD
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>command-1 || command-2 || command-3 || ... command-n</PRE
></FONT
></TD
></TR
></TABLE
>
Each command executes in turn for as long as the previous
command returns <SPAN
CLASS="RETURNVALUE"
>false</SPAN
>. At
the first <SPAN
CLASS="RETURNVALUE"
>true</SPAN
> return, the
command chain terminates (the first command returning
<SPAN
CLASS="RETURNVALUE"
>true</SPAN
> is the last one to
execute). This is obviously the inverse of the <SPAN
CLASS="QUOTE"
>"and
list"</SPAN
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX65"
></A
><P
><B
>Example 26-3. Using <I
CLASS="FIRSTTERM"
>or lists</I
> in combination
with an <I
CLASS="FIRSTTERM"
>and list</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# delete.sh, a not-so-cunning file deletion utility.
# Usage: delete filename
E_BADARGS=85
if [ -z "$1" ]
then
echo "Usage: `basename $0` filename"
exit $E_BADARGS # No arg? Bail out.
else
file=$1 # Set filename.
fi
[ ! -f "$file" ] &#38;&#38; echo "File \"$file\" not found. \
Cowardly refusing to delete a nonexistent file."
# AND LIST, to give error message if file not present.
# Note echo message continuing on to a second line after an escape.
[ ! -f "$file" ] || (rm -f $file; echo "File \"$file\" deleted.")
# OR LIST, to delete file if present.
# Note logic inversion above.
# AND LIST executes on true, OR LIST on false.
exit $?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="CAUTION"
><P
></P
><TABLE
CLASS="CAUTION"
WIDTH="90%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/caution.gif"
HSPACE="5"
ALT="Caution"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>If the first command in an <I
CLASS="FIRSTTERM"
>or
list</I
> returns <SPAN
CLASS="RETURNVALUE"
>true</SPAN
>,
it <TT
CLASS="REPLACEABLE"
><I
>will</I
></TT
> execute.</P
></TD
></TR
></TABLE
></DIV
></DD
></DL
></DIV
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># ==&#62; The following snippets from the /etc/rc.d/init.d/single
#+==&#62; script by Miquel van Smoorenburg
#+==&#62; illustrate use of "and" and "or" lists.
# ==&#62; "Arrowed" comments added by document author.
[ -x /usr/bin/clear ] &#38;&#38; /usr/bin/clear
# ==&#62; If /usr/bin/clear exists, then invoke it.
# ==&#62; Checking for the existence of a command before calling it
#+==&#62; avoids error messages and other awkward consequences.
# ==&#62; . . .
# If they want to run something in single user mode, might as well run it...
for i in /etc/rc1.d/S[0-9][0-9]* ; do
# Check if the script is there.
[ -x "$i" ] || continue
# ==&#62; If corresponding file in $PWD *not* found,
#+==&#62; then "continue" by jumping to the top of the loop.
# Reject backup files and files generated by rpm.
case "$1" in
*.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
continue;;
esac
[ "$i" = "/etc/rc1.d/S00single" ] &#38;&#38; continue
# ==&#62; Set script name, but don't execute it yet.
$i start
done
# ==&#62; . . .</PRE
></FONT
></TD
></TR
></TABLE
></P
><DIV
CLASS="IMPORTANT"
><P
></P
><TABLE
CLASS="IMPORTANT"
WIDTH="100%"
BORDER="0"
><TR
><TD
WIDTH="25"
ALIGN="CENTER"
VALIGN="TOP"
><IMG
SRC="../images/important.gif"
HSPACE="5"
ALT="Important"></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
><P
>The <A
HREF="exit-status.html#EXITSTATUSREF"
>exit
status</A
> of an <TT
CLASS="USERINPUT"
><B
>and list</B
></TT
> or an
<TT
CLASS="USERINPUT"
><B
>or list</B
></TT
> is the exit status of the last
command executed.</P
></TD
></TR
></TABLE
></DIV
><P
>Clever combinations of <I
CLASS="FIRSTTERM"
>and</I
> and
<I
CLASS="FIRSTTERM"
>or</I
> lists are possible, but the logic may
easily become convoluted and require close attention to <A
HREF="opprecedence.html#OPPRECEDENCE1"
>operator precedence rules</A
>, and
possibly extensive debugging.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>false &#38;&#38; true || echo false # false
# Same result as
( false &#38;&#38; true ) || echo false # false
# But NOT
false &#38;&#38; ( true || echo false ) # (nothing echoed)
# Note left-to-right grouping and evaluation of statements.
# It's usually best to avoid such complexities.
# Thanks, S.C.</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>See <A
HREF="contributed-scripts.html#DAYSBETWEEN"
>Example A-7</A
> and <A
HREF="fto.html#BROKENLINK"
>Example 7-4</A
> for illustrations of using <TT
CLASS="USERINPUT"
><B
>and
/ or list</B
></TT
> constructs to test variables.</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="aliases.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="arrays.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Aliases</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="part5.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Arrays</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>