688 lines
11 KiB
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 && command-2 && command-3 && ... 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} ]] && ((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" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && \
|
|
# ^^ ^^ ^^
|
|
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 && \
|
|
# ^^^^^^^^^^^^ condition #1
|
|
echo "Usage: `basename $0` $ARGS argument(s)" && 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=$@ && [ -z "$arg1" ] && 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" ] && 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"
|
|
># ==> The following snippets from the /etc/rc.d/init.d/single
|
|
#+==> script by Miquel van Smoorenburg
|
|
#+==> illustrate use of "and" and "or" lists.
|
|
# ==> "Arrowed" comments added by document author.
|
|
|
|
[ -x /usr/bin/clear ] && /usr/bin/clear
|
|
# ==> If /usr/bin/clear exists, then invoke it.
|
|
# ==> Checking for the existence of a command before calling it
|
|
#+==> avoids error messages and other awkward consequences.
|
|
|
|
# ==> . . .
|
|
|
|
# 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
|
|
# ==> If corresponding file in $PWD *not* found,
|
|
#+==> 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" ] && continue
|
|
# ==> Set script name, but don't execute it yet.
|
|
$i start
|
|
done
|
|
|
|
# ==> . . .</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 && true || echo false # false
|
|
|
|
# Same result as
|
|
( false && true ) || echo false # false
|
|
# But NOT
|
|
false && ( 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
|
|
> |