old-www/LDP/abs/html/internal.html

5640 lines
86 KiB
HTML
Raw Permalink Blame History

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>Internal Commands and Builtins</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="Commands"
HREF="part4.html"><LINK
REL="PREVIOUS"
TITLE="Commands"
HREF="part4.html"><LINK
REL="NEXT"
TITLE="Job Control Commands"
HREF="x9644.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="part4.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
></TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="x9644.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="CHAPTER"
><H1
><A
NAME="INTERNAL"
></A
>Chapter 15. Internal Commands and Builtins</H1
><P
><A
NAME="BUILTINREF"
></A
>A <I
CLASS="FIRSTTERM"
>builtin</I
>
is a <B
CLASS="COMMAND"
>command</B
> contained within the Bash tool
set, literally <I
CLASS="FIRSTTERM"
>built in</I
>. This is either
for performance reasons -- builtins execute faster than external
commands, which usually require <I
CLASS="FIRSTTERM"
>forking off</I
>
<A
NAME="AEN8607"
HREF="#FTN.AEN8607"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
>
a separate process -- or because a particular builtin needs
direct access to the shell internals.</P
><P
><A
NAME="FORKREF"
></A
></P
><TABLE
CLASS="SIDEBAR"
BORDER="1"
CELLPADDING="5"
><TR
><TD
><DIV
CLASS="SIDEBAR"
><A
NAME="AEN8611"
></A
><P
></P
><P
><A
NAME="PARENTREF"
></A
>When a command or
the shell itself initiates (or
<I
CLASS="FIRSTTERM"
>spawns</I
>) a new
subprocess to carry out a task, this is called
<I
CLASS="FIRSTTERM"
>forking</I
>. This new process
is the <I
CLASS="FIRSTTERM"
>child</I
>, and the process
that <I
CLASS="FIRSTTERM"
>forked</I
> it off is the
<I
CLASS="FIRSTTERM"
>parent</I
>. While the <I
CLASS="FIRSTTERM"
>child
process</I
> is doing its work, the
<I
CLASS="FIRSTTERM"
>parent process</I
> is still
executing.</P
><P
>Note that while a <I
CLASS="FIRSTTERM"
>parent
process</I
> gets the <I
CLASS="FIRSTTERM"
>process
ID</I
> of the <I
CLASS="FIRSTTERM"
>child
process</I
>, and can thus pass arguments to it,
<EM
>the reverse is not true</EM
>. <A
HREF="gotchas.html#PARCHILDPROBREF"
>This can create problems
that are subtle and hard to track down.</A
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="SPAWNSCR"
></A
><P
><B
>Example 15-1. A script that spawns multiple instances of itself</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# spawn.sh
PIDS=$(pidof sh $0) # Process IDs of the various instances of this script.
P_array=( $PIDS ) # Put them in an array (why?).
echo $PIDS # Show process IDs of parent and child processes.
let "instances = ${#P_array[*]} - 1" # Count elements, less 1.
# Why subtract 1?
echo "$instances instance(s) of this script running."
echo "[Hit Ctl-C to exit.]"; echo
sleep 1 # Wait.
sh $0 # Play it again, Sam.
exit 0 # Not necessary; script will never get to here.
# Why not?
# After exiting with a Ctl-C,
#+ do all the spawned instances of the script die?
# If so, why?
# Note:
# ----
# Be careful not to run this script too long.
# It will eventually eat up too many system resources.
# Is having a script spawn multiple instances of itself
#+ an advisable scripting technique.
# Why or why not?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="BLTINFRK"
></A
></P
><P
>Generally, a Bash <I
CLASS="FIRSTTERM"
>builtin</I
>
does not fork a subprocess when it executes within
a script. An external system command or filter in
a script usually <EM
>will</EM
> fork a
subprocess.</P
><P
></P
></DIV
></TD
></TR
></TABLE
><P
>A builtin may be a synonym to a system command of the same
name, but Bash reimplements it internally. For example,
the Bash <B
CLASS="COMMAND"
>echo</B
> command is not the same as
<TT
CLASS="FILENAME"
>/bin/echo</TT
>, although their behavior is
almost identical.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
echo "This line uses the \"echo\" builtin."
/bin/echo "This line uses the /bin/echo system command."</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><A
NAME="KEYWORDREF"
></A
>A <I
CLASS="FIRSTTERM"
>keyword</I
>
is a <I
CLASS="FIRSTTERM"
>reserved</I
> word, token or
operator. Keywords have a special meaning to the shell,
and indeed are the building blocks of the shell's
syntax. As examples, <I
CLASS="FIRSTTERM"
>for</I
>,
<I
CLASS="FIRSTTERM"
>while</I
>, <I
CLASS="FIRSTTERM"
>do</I
>,
and <I
CLASS="FIRSTTERM"
>!</I
> are keywords. Similar to a <A
HREF="internal.html#BUILTINREF"
>builtin</A
>, a keyword is hard-coded into
Bash, but unlike a <I
CLASS="FIRSTTERM"
>builtin</I
>, a keyword is
not in itself a command, but <EM
>a subunit of a command
construct</EM
>.
<A
NAME="AEN8650"
HREF="#FTN.AEN8650"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>
</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="INTIO1"
></A
>I/O</B
></P
><DL
><DT
><A
NAME="ECHOREF"
></A
><B
CLASS="COMMAND"
>echo</B
></DT
><DD
><P
>prints (to <TT
CLASS="FILENAME"
>stdout</TT
>) an expression
or variable (see <A
HREF="varsubn.html#EX9"
>Example 4-1</A
>).
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>echo Hello
echo $a</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>An <B
CLASS="COMMAND"
>echo</B
> requires the
<TT
CLASS="OPTION"
>-e</TT
> option to print escaped characters. See
<A
HREF="escapingsection.html#ESCAPED"
>Example 5-2</A
>.</P
><P
>Normally, each <B
CLASS="COMMAND"
>echo</B
> command prints
a terminal newline, but the <TT
CLASS="OPTION"
>-n</TT
> option
suppresses this.</P
><P
><A
NAME="ECHOGREPREF"
></A
></P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>An <B
CLASS="COMMAND"
>echo</B
> can be used to feed a
sequence of commands down a pipe.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]]
then
echo "$VAR contains the substring sequence \"txt\""
fi</PRE
></FONT
></TD
></TR
></TABLE
></P
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="ECHOCS"
></A
></P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>An <B
CLASS="COMMAND"
>echo</B
>, in combination with
<A
HREF="commandsub.html#COMMANDSUBREF"
>command substitution</A
>
can set a variable.</P
><P
><TT
CLASS="USERINPUT"
><B
>a=`echo
"HELLO" | tr A-Z a-z`</B
></TT
></P
><P
>See also <A
HREF="textproc.html#LOWERCASE"
>Example 16-22</A
>, <A
HREF="moreadv.html#EX57"
>Example 16-3</A
>, <A
HREF="mathc.html#MONTHLYPMT"
>Example 16-47</A
>, and <A
HREF="mathc.html#BASE"
>Example 16-48</A
>.</P
></TD
></TR
></TABLE
></DIV
><P
>Be aware that <B
CLASS="COMMAND"
>echo `command`</B
>
deletes any linefeeds that the output
of <TT
CLASS="REPLACEABLE"
><I
>command</I
></TT
>
generates.</P
><P
>The <A
HREF="internalvariables.html#IFSREF"
>$IFS</A
> (internal field
separator) variable normally contains
<SPAN
CLASS="TOKEN"
>\n</SPAN
> (linefeed) as one of its set of
<A
HREF="special-chars.html#WHITESPACEREF"
>whitespace</A
>
characters. Bash therefore splits the output of
<TT
CLASS="REPLACEABLE"
><I
>command</I
></TT
> at linefeeds
into arguments to <B
CLASS="COMMAND"
>echo</B
>. Then
<B
CLASS="COMMAND"
>echo</B
> outputs these arguments,
separated by spaces.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>ls -l /usr/share/apps/kjezz/sounds</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>-rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au
-rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au</TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>echo `ls -l /usr/share/apps/kjezz/sounds`</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root ...</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
> So, how can we embed a linefeed within an
<A
HREF="internal.html#ECHOREF"
>echoed</A
> character string?
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Embedding a linefeed?
echo "Why doesn't this string \n split on two lines?"
# Doesn't split.
# Let's try something else.
echo
echo $"A line of text containing
a linefeed."
# Prints as two distinct lines (embedded linefeed).
# But, is the "$" variable prefix really necessary?
echo
echo "This string splits
on two lines."
# No, the "$" is not needed.
echo
echo "---------------"
echo
echo -n $"Another line of text containing
a linefeed."
# Prints as two distinct lines (embedded linefeed).
# Even the -n option fails to suppress the linefeed here.
echo
echo
echo "---------------"
echo
echo
# However, the following doesn't work as expected.
# Why not? Hint: Assignment to a variable.
string1=$"Yet another line of text containing
a linefeed (maybe)."
echo $string1
# Yet another line of text containing a linefeed (maybe).
# ^
# Linefeed becomes a space.
# Thanks, Steve Parker, for pointing this out.</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><A
NAME="BINECHO"
></A
></P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>This command is a shell builtin, and not the same as
<TT
CLASS="FILENAME"
>/bin/echo</TT
>, although its behavior is
similar.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>type -a echo</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>echo is a shell builtin
echo is /bin/echo</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="PRINTFREF"
></A
><B
CLASS="COMMAND"
>printf</B
></DT
><DD
><P
>The <B
CLASS="COMMAND"
>printf</B
>, formatted print, command is an
enhanced <B
CLASS="COMMAND"
>echo</B
>. It is a limited variant
of the <I
CLASS="FIRSTTERM"
>C</I
> language
<TT
CLASS="FUNCTION"
>printf()</TT
> library function, and its
syntax is somewhat different.</P
><P
><B
CLASS="COMMAND"
>printf</B
> <TT
CLASS="REPLACEABLE"
><I
>format-string</I
></TT
>... <TT
CLASS="REPLACEABLE"
><I
>parameter</I
></TT
>... </P
><P
>This is the Bash <I
CLASS="FIRSTTERM"
>builtin</I
> version
of the <TT
CLASS="FILENAME"
>/bin/printf</TT
> or
<TT
CLASS="FILENAME"
>/usr/bin/printf</TT
> command. See the
<B
CLASS="COMMAND"
>printf</B
> <A
HREF="basic.html#MANREF"
>manpage</A
> (of the system command)
for in-depth coverage.</P
><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
>Older versions of Bash may not support
<B
CLASS="COMMAND"
>printf</B
>.</P
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="EX47"
></A
><P
><B
>Example 15-2. <I
CLASS="FIRSTTERM"
>printf</I
> in action</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# printf demo
declare -r PI=3.14159265358979 # Read-only variable, i.e., a constant.
declare -r DecimalConstant=31373
Message1="Greetings,"
Message2="Earthling."
echo
printf "Pi to 2 decimal places = %1.2f" $PI
echo
printf "Pi to 9 decimal places = %1.9f" $PI # It even rounds off correctly.
printf "\n" # Prints a line feed,
# Equivalent to 'echo' . . .
printf "Constant = \t%d\n" $DecimalConstant # Inserts tab (\t).
printf "%s %s \n" $Message1 $Message2
echo
# ==========================================#
# Simulation of C function, sprintf().
# Loading a variable with a formatted string.
echo
Pi12=$(printf "%1.12f" $PI)
echo "Pi to 12 decimal places = $Pi12" # Roundoff error!
Msg=`printf "%s %s \n" $Message1 $Message2`
echo $Msg; echo $Msg
# As it happens, the 'sprintf' function can now be accessed
#+ as a loadable module to Bash,
#+ but this is not portable.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Formatting error messages is a useful application of
<B
CLASS="COMMAND"
>printf</B
></P
><P
> <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>E_BADDIR=85
var=nonexistent_directory
error()
{
printf "$@" &#62;&#38;2
# Formats positional params passed, and sends them to stderr.
echo
exit $E_BADDIR
}
cd $var || error $"Can't cd to %s." "$var"
# Thanks, S.C.</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>See also <A
HREF="assortedtips.html#PROGRESSBAR"
>Example 36-17</A
>.</P
></DD
><DT
><A
NAME="READREF"
></A
><B
CLASS="COMMAND"
>read</B
></DT
><DD
><P
><SPAN
CLASS="QUOTE"
>"Reads"</SPAN
> the value
of a variable from <TT
CLASS="FILENAME"
>stdin</TT
>, that
is, interactively fetches input from the keyboard. The
<TT
CLASS="OPTION"
>-a</TT
> option lets <B
CLASS="COMMAND"
>read</B
>
get array variables (see <A
HREF="arrays.html#EX67"
>Example 27-6</A
>).</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX36"
></A
><P
><B
>Example 15-3. Variable assignment, using <I
CLASS="FIRSTTERM"
>read</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# "Reading" variables.
echo -n "Enter the value of variable 'var1': "
# The -n option to echo suppresses newline.
read var1
# Note no '$' in front of var1, since it is being set.
echo "var1 = $var1"
echo
# A single 'read' statement can set multiple variables.
echo -n "Enter the values of variables 'var2' and 'var3' "
echo =n "(separated by a space or tab): "
read var2 var3
echo "var2 = $var2 var3 = $var3"
# If you input only one value,
#+ the other variable(s) will remain unset (null).
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>A <B
CLASS="COMMAND"
>read</B
> without an associated variable
assigns its input to the dedicated variable <A
HREF="internalvariables.html#REPLYREF"
>$REPLY</A
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="READNOVAR"
></A
><P
><B
>Example 15-4. What happens when <I
CLASS="FIRSTTERM"
>read</I
> has no
variable</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# read-novar.sh
echo
# -------------------------- #
echo -n "Enter a value: "
read var
echo "\"var\" = "$var""
# Everything as expected here.
# -------------------------- #
echo
# ------------------------------------------------------------------- #
echo -n "Enter another value: "
read # No variable supplied for 'read', therefore...
#+ Input to 'read' assigned to default variable, $REPLY.
var="$REPLY"
echo "\"var\" = "$var""
# This is equivalent to the first code block.
# ------------------------------------------------------------------- #
echo
echo "========================="
echo
# This example is similar to the "reply.sh" script.
# However, this one shows that $REPLY is available
#+ even after a 'read' to a variable in the conventional way.
# ================================================================= #
# In some instances, you might wish to discard the first value read.
# In such cases, simply ignore the $REPLY variable.
{ # Code block.
read # Line 1, to be discarded.
read line2 # Line 2, saved in variable.
} &#60;$0
echo "Line 2 of this script is:"
echo "$line2" # # read-novar.sh
echo # #!/bin/bash line discarded.
# See also the soundcard-on.sh script.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Normally, inputting a <TT
CLASS="USERINPUT"
><B
>\</B
></TT
>
suppresses a newline during input to
a <B
CLASS="COMMAND"
>read</B
>. The <TT
CLASS="OPTION"
>-r</TT
>
option causes an inputted <TT
CLASS="USERINPUT"
><B
>\</B
></TT
> to be
interpreted literally.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="READR"
></A
><P
><B
>Example 15-5. Multi-line input to <I
CLASS="FIRSTTERM"
>read</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
echo
echo "Enter a string terminated by a \\, then press &#60;ENTER&#62;."
echo "Then, enter a second string (no \\ this time), and again press &#60;ENTER&#62;."
read var1 # The "\" suppresses the newline, when reading $var1.
# first line \
# second line
echo "var1 = $var1"
# var1 = first line second line
# For each line terminated by a "\"
#+ you get a prompt on the next line to continue feeding characters into var1.
echo; echo
echo "Enter another string terminated by a \\ , then press &#60;ENTER&#62;."
read -r var2 # The -r option causes the "\" to be read literally.
# first line \
echo "var2 = $var2"
# var2 = first line \
# Data entry terminates with the first &#60;ENTER&#62;.
echo
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="READOPTIONS"
></A
></P
><P
>The <B
CLASS="COMMAND"
>read</B
> command has some interesting
options that permit echoing a prompt and even reading keystrokes
without hitting <B
CLASS="KEYCAP"
>ENTER</B
>.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Read a keypress without hitting ENTER.
read -s -n1 -p "Hit a key " keypress
echo; echo "Keypress was "\"$keypress\""."
# -s option means do not echo input.
# -n N option means accept only N characters of input.
# -p option means echo the following prompt before reading input.
# Using these options is tricky, since they need to be in the correct order.</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
><A
NAME="READARROW"
></A
></P
><P
>The <TT
CLASS="OPTION"
>-n</TT
> option to <B
CLASS="COMMAND"
>read</B
>
also allows detection of the <B
CLASS="KEYCAP"
>arrow keys</B
>
and certain of the other unusual keys.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="ARROWDETECT"
></A
><P
><B
>Example 15-6. Detecting the arrow keys</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# arrow-detect.sh: Detects the arrow keys, and a few more.
# Thank you, Sandro Magi, for showing me how.
# --------------------------------------------
# Character codes generated by the keypresses.
arrowup='\[A'
arrowdown='\[B'
arrowrt='\[C'
arrowleft='\[D'
insert='\[2'
delete='\[3'
# --------------------------------------------
SUCCESS=0
OTHER=65
echo -n "Press a key... "
# May need to also press ENTER if a key not listed above pressed.
read -n3 key # Read 3 characters.
echo -n "$key" | grep "$arrowup" #Check if character code detected.
if [ "$?" -eq $SUCCESS ]
then
echo "Up-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
echo "Down-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Right-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Left-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Insert\" key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Delete\" key pressed."
exit $SUCCESS
fi
echo " Some other key pressed."
exit $OTHER
# ========================================= #
# Mark Alexander came up with a simplified
#+ version of the above script (Thank you!).
# It eliminates the need for grep.
#!/bin/bash
uparrow=$'\x1b[A'
downarrow=$'\x1b[B'
leftarrow=$'\x1b[D'
rightarrow=$'\x1b[C'
read -s -n3 -p "Hit an arrow key: " x
case "$x" in
$uparrow)
echo "You pressed up-arrow"
;;
$downarrow)
echo "You pressed down-arrow"
;;
$leftarrow)
echo "You pressed left-arrow"
;;
$rightarrow)
echo "You pressed right-arrow"
;;
esac
exit $?
# ========================================= #
# Antonio Macchi has a simpler alternative.
#!/bin/bash
while true
do
read -sn1 a
test "$a" == `echo -en "\e"` || continue
read -sn1 a
test "$a" == "[" || continue
read -sn1 a
case "$a" in
A) echo "up";;
B) echo "down";;
C) echo "right";;
D) echo "left";;
esac
done
# ========================================= #
# Exercise:
# --------
# 1) Add detection of the "Home," "End," "PgUp," and "PgDn" keys.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>The <TT
CLASS="OPTION"
>-n</TT
> option to <B
CLASS="COMMAND"
>read</B
>
will not detect the <B
CLASS="KEYCAP"
>ENTER</B
> (newline)
key.</P
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="READTIMED"
></A
></P
><P
>The <TT
CLASS="OPTION"
>-t</TT
> option to <B
CLASS="COMMAND"
>read</B
>
permits timed input (see <A
HREF="internalvariables.html#TOUT"
>Example 9-4</A
> and <A
HREF="contributed-scripts.html#QKY"
>Example A-41</A
>).</P
><P
><A
NAME="READFD"
></A
>The <TT
CLASS="OPTION"
>-u</TT
> option
takes the <A
HREF="io-redirection.html#FDREF"
>file descriptor</A
>
of the target file.</P
><P
><A
NAME="READREDIR0"
></A
></P
><P
>The <B
CLASS="COMMAND"
>read</B
> command may also
<SPAN
CLASS="QUOTE"
>"read"</SPAN
> its variable value from a file
<A
HREF="io-redirection.html#IOREDIRREF"
>redirected</A
> to
<TT
CLASS="FILENAME"
>stdin</TT
>. If the file contains
more than one line, only the first line is assigned
to the variable. If <B
CLASS="COMMAND"
>read</B
>
has more than one parameter, then each of
these variables gets assigned a successive <A
HREF="special-chars.html#WHITESPACEREF"
>whitespace-delineated</A
>
string. Caution!</P
><DIV
CLASS="EXAMPLE"
><A
NAME="READREDIR"
></A
><P
><B
>Example 15-7. Using <I
CLASS="FIRSTTERM"
>read</I
> with
<A
HREF="io-redirection.html#IOREDIRREF"
>file redirection</A
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
read var1 &#60;data-file
echo "var1 = $var1"
# var1 set to the entire first line of the input file "data-file"
read var2 var3 &#60;data-file
echo "var2 = $var2 var3 = $var3"
# Note non-intuitive behavior of "read" here.
# 1) Rewinds back to the beginning of input file.
# 2) Each variable is now set to a corresponding string,
# separated by whitespace, rather than to an entire line of text.
# 3) The final variable gets the remainder of the line.
# 4) If there are more variables to be set than whitespace-terminated strings
# on the first line of the file, then the excess variables remain empty.
echo "------------------------------------------------"
# How to resolve the above problem with a loop:
while read line
do
echo "$line"
done &#60;data-file
# Thanks, Heiner Steven for pointing this out.
echo "------------------------------------------------"
# Use $IFS (Internal Field Separator variable) to split a line of input to
# "read", if you do not want the default to be whitespace.
echo "List of all users:"
OIFS=$IFS; IFS=: # /etc/passwd uses ":" for field separator.
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done &#60;/etc/passwd # I/O redirection.
IFS=$OIFS # Restore original $IFS.
# This code snippet also by Heiner Steven.
# Setting the $IFS variable within the loop itself
#+ eliminates the need for storing the original $IFS
#+ in a temporary variable.
# Thanks, Dim Segebart, for pointing this out.
echo "------------------------------------------------"
echo "List of all users:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done &#60;/etc/passwd # I/O redirection.
echo
echo "\$IFS still $IFS"
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
><A
NAME="PIPEREADREF0"
></A
></P
><P
><A
HREF="special-chars.html#PIPEREF"
>Piping</A
> output
to a <I
CLASS="FIRSTTERM"
>read</I
>, using <A
HREF="internal.html#ECHOREF"
>echo</A
> to set variables <A
HREF="gotchas.html#BADREAD0"
>will fail</A
>.</P
><P
><A
NAME="READPIPEREF"
></A
>Yet, piping the output of <A
HREF="basic.html#CATREF"
>cat</A
> <EM
>seems</EM
> to
work.</P
><P
><A
NAME="WHILEREADREF"
></A
></P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>cat file1 file2 |
while read line
do
echo $line
done</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>However, as Bj<42>n Eriksson shows:</P
><DIV
CLASS="EXAMPLE"
><A
NAME="READPIPE"
></A
><P
><B
>Example 15-8. Problems reading from a pipe</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/sh
# readpipe.sh
# This example contributed by Bjon Eriksson.
### shopt -s lastpipe
last="(null)"
cat $0 |
while read line
do
echo "{$line}"
last=$line
done
echo
echo "++++++++++++++++++++++"
printf "\nAll done, last: $last\n" # The output of this line
#+ changes if you uncomment line 5.
# (Bash, version -ge 4.2 required.)
exit 0 # End of code.
# (Partial) output of script follows.
# The 'echo' supplies extra brackets.
#############################################
./readpipe.sh
{#!/bin/sh}
{last="(null)"}
{cat $0 |}
{while read line}
{do}
{echo "{$line}"}
{last=$line}
{done}
{printf "nAll done, last: $lastn"}
All done, last: (null)
The variable (last) is set within the loop/subshell
but its value does not persist outside the loop.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>The <I
CLASS="FIRSTTERM"
>gendiff</I
> script, usually
found in <TT
CLASS="FILENAME"
>/usr/bin</TT
> on
many Linux distros, pipes the output of <A
HREF="moreadv.html#FINDREF"
>find</A
> to a <I
CLASS="FIRSTTERM"
>while
read</I
> construct.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>find $1 \( -name "*$2" -o -name ".*$2" \) -print |
while read f; do
. . .</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="90%"
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
>It is possible to <I
CLASS="FIRSTTERM"
>paste</I
> text into
the input field of a <I
CLASS="FIRSTTERM"
>read</I
> (but
<EM
>not</EM
> multiple lines!). See <A
HREF="contributed-scripts.html#PADSW"
>Example A-38</A
>.</P
></TD
></TR
></TABLE
></DIV
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="INTFILESYSTEM1"
></A
>Filesystem</B
></P
><DL
><DT
><A
NAME="CDREF"
></A
><B
CLASS="COMMAND"
>cd</B
></DT
><DD
><P
>The familiar <B
CLASS="COMMAND"
>cd</B
> change directory
command finds use in scripts where execution of a command
requires being in a specified directory.</P
><P
>
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>(cd /source/directory &#38;&#38; tar cf - . ) | (cd /dest/directory &#38;&#38; tar xpvf -)</PRE
></FONT
></TD
></TR
></TABLE
>
[from the <A
HREF="special-chars.html#COXEX"
>previously cited</A
>
example by Alan Cox]</P
><P
>The <TT
CLASS="OPTION"
>-P</TT
> (physical) option to
<B
CLASS="COMMAND"
>cd</B
> causes it to ignore symbolic
links.</P
><P
><B
CLASS="COMMAND"
>cd -</B
> changes to <A
HREF="internalvariables.html#OLDPWD"
>$OLDPWD</A
>, the previous working
directory.</P
><P
><A
NAME="DOUBLESLASHREF"
></A
></P
><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
>The <B
CLASS="COMMAND"
>cd</B
> command does not function
as expected when presented with two forward slashes.
<TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>cd //</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>pwd</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>//</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
The output should, of course, be <TT
CLASS="COMPUTEROUTPUT"
>/</TT
>.
This is a problem both from the command-line and in a script.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="PWD2REF"
></A
><B
CLASS="COMMAND"
>pwd</B
></DT
><DD
><P
>Print Working Directory. This gives the user's
(or script's) current directory (see <A
HREF="internal.html#EX37"
>Example 15-9</A
>). The effect is identical to
reading the value of the builtin variable <A
HREF="internalvariables.html#PWDREF"
>$PWD</A
>.</P
></DD
><DT
><A
NAME="DIRSD"
></A
><B
CLASS="COMMAND"
>pushd</B
>, <B
CLASS="COMMAND"
>popd</B
>, <B
CLASS="COMMAND"
>dirs</B
></DT
><DD
><P
>This command set is a mechanism for bookmarking
working directories, a means of moving back and forth
through directories in an orderly manner. A pushdown <A
HREF="internalvariables.html#STACKDEFREF"
>stack</A
> is used to keep track
of directory names. Options allow various manipulations
of the directory stack.</P
><P
><A
NAME="PUSHDREF"
></A
><TT
CLASS="USERINPUT"
><B
>pushd
dir-name</B
></TT
> pushes the path
<TT
CLASS="REPLACEABLE"
><I
>dir-name</I
></TT
> onto the directory
stack (to the <I
CLASS="FIRSTTERM"
>top</I
> of the stack)
and simultaneously changes the current working directory
to <TT
CLASS="REPLACEABLE"
><I
>dir-name</I
></TT
></P
><P
><A
NAME="POPDREF"
></A
><B
CLASS="COMMAND"
>popd</B
> removes
(pops) the top directory path name off the directory stack
and simultaneously changes the current working directory
to the directory now at the <I
CLASS="FIRSTTERM"
>top</I
> of
the stack.</P
><P
><A
NAME="DIRSREF"
></A
><B
CLASS="COMMAND"
>dirs</B
> lists
the contents of the directory stack (compare this
with the <A
HREF="internalvariables.html#DIRSTACKREF"
>$DIRSTACK</A
>
variable). A successful <B
CLASS="COMMAND"
>pushd</B
>
or <B
CLASS="COMMAND"
>popd</B
> will automatically invoke
<B
CLASS="COMMAND"
>dirs</B
>.</P
><P
>Scripts that require various changes to the current
working directory without hard-coding the directory name
changes can make good use of these commands. Note that
the implicit <TT
CLASS="VARNAME"
>$DIRSTACK</TT
> array variable,
accessible from within a script, holds the contents of
the directory stack.
</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX37"
></A
><P
><B
>Example 15-9. Changing the current working directory</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
dir1=/usr/local
dir2=/var/spool
pushd $dir1
# Will do an automatic 'dirs' (list directory stack to stdout).
echo "Now in directory `pwd`." # Uses back-quoted 'pwd'.
# Now, do some stuff in directory 'dir1'.
pushd $dir2
echo "Now in directory `pwd`."
# Now, do some stuff in directory 'dir2'.
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."
# Now, do some more stuff in directory 'dir1'.
popd
echo "Now back in original working directory `pwd`."
exit 0
# What happens if you don't 'popd' -- then exit the script?
# Which directory do you end up in? Why?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="INTVAR1"
></A
>Variables</B
></P
><DL
><DT
><A
NAME="LETREF"
></A
><B
CLASS="COMMAND"
>let</B
></DT
><DD
><P
>The <B
CLASS="COMMAND"
>let</B
> command carries out
<I
CLASS="FIRSTTERM"
>arithmetic</I
> operations on variables.
<A
NAME="AEN9009"
HREF="#FTN.AEN9009"
><SPAN
CLASS="footnote"
>[3]</SPAN
></A
>
In many cases, it functions as a less complex version
of <A
HREF="moreadv.html#EXPRREF"
>expr</A
>.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX46"
></A
><P
><B
>Example 15-10. Letting <I
CLASS="FIRSTTERM"
>let</I
> do arithmetic.</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
echo
let a=11 # Same as 'a=11'
let a=a+5 # Equivalent to let "a = a + 5"
# (Double quotes and spaces make it more readable.)
echo "11 + 5 = $a" # 16
let "a &#60;&#60;= 3" # Equivalent to let "a = a &#60;&#60; 3"
echo "\"\$a\" (=16) left-shifted 3 places = $a"
# 128
let "a /= 4" # Equivalent to let "a = a / 4"
echo "128 / 4 = $a" # 32
let "a -= 5" # Equivalent to let "a = a - 5"
echo "32 - 5 = $a" # 27
let "a *= 10" # Equivalent to let "a = a * 10"
echo "27 * 10 = $a" # 270
let "a %= 8" # Equivalent to let "a = a % 8"
echo "270 modulo 8 = $a (270 / 8 = 33, remainder $a)"
# 6
# Does "let" permit C-style operators?
# Yes, just as the (( ... )) double-parentheses construct does.
let a++ # C-style (post) increment.
echo "6++ = $a" # 6++ = 7
let a-- # C-style decrement.
echo "7-- = $a" # 7-- = 6
# Of course, ++a, etc., also allowed . . .
echo
# Trinary operator.
# Note that $a is 6, see above.
let "t = a&#60;7?7:11" # True
echo $t # 7
let a++
let "t = a&#60;7?7:11" # False
echo $t # 11
exit</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="EXITVALANOMALY02"
></A
></P
><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
>The <I
CLASS="FIRSTTERM"
>let</I
> command can,
in certain contexts, return a surprising <A
HREF="exit-status.html#EXITSTATUSREF"
>exit status</A
>.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Evgeniy Ivanov points out:
var=0
echo $? # 0
# As expected.
let var++
echo $? # 1
# The command was successful, so why isn't $?=0 ???
# Anomaly!
let var++
echo $? # 0
# As expected.
# Likewise . . .
let var=0
echo $? # 1
# The command was successful, so why isn't $?=0 ???
# However, as Jeff Gorak points out,
#+ this is part of the design spec for 'let' . . .
# "If the last ARG evaluates to 0, let returns 1;
# let returns 0 otherwise." ['help let']</PRE
></FONT
></TD
></TR
></TABLE
></P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="EVALREF"
></A
><B
CLASS="COMMAND"
>eval</B
></DT
><DD
><P
><TT
CLASS="USERINPUT"
><B
>eval arg1 [arg2] ... [argN]</B
></TT
></P
><P
>Combines the arguments in an expression or list of
expressions and <TT
CLASS="REPLACEABLE"
><I
>evaluates</I
></TT
> them.
Any variables within the expression are expanded. The
net result is to <B
CLASS="COMMAND"
>convert a string into a
command</B
>.</P
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="90%"
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"
>eval</B
> command can be used for
code generation from the command-line or within a script.
</P
></TD
></TR
></TABLE
></DIV
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>command_string="ps ax"</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>process="ps ax"</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>eval "$command_string" | grep "$process"</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>26973 pts/3 R+ 0:00 grep --color ps ax
26974 pts/3 R+ 0:00 ps ax</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><A
NAME="EVALFORCED"
></A
></P
><P
>Each invocation of <I
CLASS="FIRSTTERM"
>eval</I
> forces
a re-<EM
>evaluation</EM
> of its arguments.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>a='$b'
b='$c'
c=d
echo $a # $b
# First level.
eval echo $a # $c
# Second level.
eval eval echo $a # d
# Third level.
# Thank you, E. Choroba.</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
><A
NAME="EVALEFF"
></A
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX43"
></A
><P
><B
>Example 15-11. Showing the effect of <I
CLASS="FIRSTTERM"
>eval</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Exercising "eval" ...
y=`eval ls -l` # Similar to y=`ls -l`
echo $y #+ but linefeeds removed because "echoed" variable is unquoted.
echo
echo "$y" # Linefeeds preserved when variable is quoted.
echo; echo
y=`eval df` # Similar to y=`df`
echo $y #+ but linefeeds removed.
# When LF's not preserved, it may make it easier to parse output,
#+ using utilities such as "awk".
echo
echo "==========================================================="
echo
eval "`seq 3 | sed -e 's/.*/echo var&#38;=ABCDEFGHIJ/'`"
# var1=ABCDEFGHIJ
# var2=ABCDEFGHIJ
# var3=ABCDEFGHIJ
echo
echo "==========================================================="
echo
# Now, showing how to do something useful with "eval" . . .
# (Thank you, E. Choroba!)
version=3.4 # Can we split the version into major and minor
#+ part in one command?
echo "version = $version"
eval major=${version/./;minor=} # Replaces '.' in version by ';minor='
# The substitution yields '3; minor=4'
#+ so eval does minor=4, major=3
echo Major: $major, minor: $minor # Major: 3, minor: 4</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="ARRCHOICE0"
></A
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="ARRCHOICE"
></A
><P
><B
>Example 15-12. Using <I
CLASS="FIRSTTERM"
>eval</I
> to select
among variables</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# arr-choice.sh
# Passing arguments to a function to select
#+ one particular variable out of a group.
arr0=( 10 11 12 13 14 15 )
arr1=( 20 21 22 23 24 25 )
arr2=( 30 31 32 33 34 35 )
# 0 1 2 3 4 5 Element number (zero-indexed)
choose_array ()
{
eval array_member=\${arr${array_number}[element_number]}
# ^ ^^^^^^^^^^^^
# Using eval to construct the name of a variable,
#+ in this particular case, an array name.
echo "Element $element_number of array $array_number is $array_member"
} # Function can be rewritten to take parameters.
array_number=0 # First array.
element_number=3
choose_array # 13
array_number=2 # Third array.
element_number=4
choose_array # 34
array_number=3 # Null array (arr3 not allocated).
element_number=4
choose_array # (null)
# Thank you, Antonio Macchi, for pointing this out.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="ECHOPARAMS"
></A
><P
><B
>Example 15-13. <I
CLASS="FIRSTTERM"
>Echoing</I
> the
<I
CLASS="FIRSTTERM"
>command-line parameters</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# echo-params.sh
# Call this script with a few command-line parameters.
# For example:
# sh echo-params.sh first second third fourth fifth
params=$# # Number of command-line parameters.
param=1 # Start at first command-line param.
while [ "$param" -le "$params" ]
do
echo -n "Command-line parameter "
echo -n \$$param # Gives only the *name* of variable.
# ^^^ # $1, $2, $3, etc.
# Why?
# \$ escapes the first "$"
#+ so it echoes literally,
#+ and $param dereferences "$param" . . .
#+ . . . as expected.
echo -n " = "
eval echo \$$param # Gives the *value* of variable.
# ^^^^ ^^^ # The "eval" forces the *evaluation*
#+ of \$$
#+ as an indirect variable reference.
(( param ++ )) # On to the next.
done
exit $?
# =================================================
$ sh echo-params.sh first second third fourth fifth
Command-line parameter $1 = first
Command-line parameter $2 = second
Command-line parameter $3 = third
Command-line parameter $4 = fourth
Command-line parameter $5 = fifth</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="EX44"
></A
><P
><B
>Example 15-14. Forcing a log-off</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Killing ppp to force a log-off.
# For dialup connection, of course.
# Script should be run as root user.
SERPORT=ttyS3
# Depending on the hardware and even the kernel version,
#+ the modem port on your machine may be different --
#+ /dev/ttyS1 or /dev/ttyS2.
killppp="eval kill -9 `ps ax | awk '/ppp/ { print $1 }'`"
# -------- process ID of ppp -------
$killppp # This variable is now a command.
# The following operations must be done as root user.
chmod 666 /dev/$SERPORT # Restore r+w permissions, or else what?
# Since doing a SIGKILL on ppp changed the permissions on the serial port,
#+ we restore permissions to previous state.
rm /var/lock/LCK..$SERPORT # Remove the serial port lock file. Why?
exit $?
# Exercises:
# ---------
# 1) Have script check whether root user is invoking it.
# 2) Do a check on whether the process to be killed
#+ is actually running before attempting to kill it.
# 3) Write an alternate version of this script based on 'fuser':
#+ if [ fuser -s /dev/modem ]; then . . .</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="ROT14"
></A
><P
><B
>Example 15-15. A version of <I
CLASS="FIRSTTERM"
>rot13</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# A version of "rot13" using 'eval'.
# Compare to "rot13.sh" example.
setvar_rot_13() # "rot13" scrambling
{
local varname=$1 varvalue=$2
eval $varname='$(echo "$varvalue" | tr a-z n-za-m)'
}
setvar_rot_13 var "foobar" # Run "foobar" through rot13.
echo $var # sbbone
setvar_rot_13 var "$var" # Run "sbbone" through rot13.
# Back to original variable.
echo $var # foobar
# This example by Stephane Chazelas.
# Modified by document author.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Here is another example of using
<I
CLASS="FIRSTTERM"
>eval</I
> to
<EM
>evaluate</EM
> a complex expression,
this one from an earlier version of YongYe's <A
HREF="https://github.com/yongye/shell/blob/master/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"
>eval ${1}+=\"${x} ${y} \"</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><A
HREF="contributed-scripts.html#SAMORSE"
>Example A-53</A
> uses
<I
CLASS="FIRSTTERM"
>eval</I
> to convert <A
HREF="arrays.html#ARRAYREF"
>array</A
> elements into a command
list.</P
><P
>The <I
CLASS="FIRSTTERM"
>eval</I
> command occurs
in the older version of <A
HREF="ivr.html#IVRREF"
>indirect
referencing</A
>.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>eval var=\$$var</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="90%"
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 <I
CLASS="FIRSTTERM"
>eval</I
> command can
be used to <A
HREF="bashver3.html#BRACEEXPREF3"
>parameterize
<I
CLASS="FIRSTTERM"
>brace expansion</I
></A
>.</P
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="EVALRISK"
></A
></P
><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
>The <B
CLASS="COMMAND"
>eval</B
> command can be
risky, and normally should be avoided when there
exists a reasonable alternative. An <TT
CLASS="USERINPUT"
><B
>eval
$COMMANDS</B
></TT
> executes the contents of
<TT
CLASS="REPLACEABLE"
><I
>COMMANDS</I
></TT
>, which may
contain such unpleasant surprises as <B
CLASS="COMMAND"
>rm -rf
*</B
>. Running an <B
CLASS="COMMAND"
>eval</B
> on
unfamiliar code written by persons unknown is living
dangerously.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="SETREF"
></A
><B
CLASS="COMMAND"
>set</B
></DT
><DD
><P
>The <B
CLASS="COMMAND"
>set</B
> command changes
the value of internal script variables/options. One use for
this is to toggle <A
HREF="options.html#OPTIONSREF"
>option
flags</A
> which help determine the behavior of the
script. Another application for it is to reset the <A
HREF="internalvariables.html#POSPARAMREF"
>positional parameters</A
> that
a script sees as the result of a command (<TT
CLASS="USERINPUT"
><B
>set
`command`</B
></TT
>). The script can then parse the
<A
HREF="special-chars.html#FIELDREF"
>fields</A
> of the command
output.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX34"
></A
><P
><B
>Example 15-16. Using <I
CLASS="FIRSTTERM"
>set</I
> with positional
parameters</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# ex34.sh
# Script "set-test"
# Invoke this script with three command-line parameters,
# for example, "sh ex34.sh one two three".
echo
echo "Positional parameters before set \`uname -a\` :"
echo "Command-line argument #1 = $1"
echo "Command-line argument #2 = $2"
echo "Command-line argument #3 = $3"
set `uname -a` # Sets the positional parameters to the output
# of the command `uname -a`
echo
echo +++++
echo $_ # +++++
# Flags set in script.
echo $- # hB
# Anomalous behavior?
echo
echo "Positional parameters after set \`uname -a\` :"
# $1, $2, $3, etc. reinitialized to result of `uname -a`
echo "Field #1 of 'uname -a' = $1"
echo "Field #2 of 'uname -a' = $2"
echo "Field #3 of 'uname -a' = $3"
echo \#\#\#
echo $_ # ###
echo
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>More fun with positional parameters.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="REVPOSPARAMS"
></A
><P
><B
>Example 15-17. Reversing the positional parameters</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# revposparams.sh: Reverse positional parameters.
# Script by Dan Jacobson, with stylistic revisions by document author.
set a\ b c d\ e;
# ^ ^ Spaces escaped
# ^ ^ Spaces not escaped
OIFS=$IFS; IFS=:;
# ^ Saving old IFS and setting new one.
echo
until [ $# -eq 0 ]
do # Step through positional parameters.
echo "### k0 = "$k"" # Before
k=$1:$k; # Append each pos param to loop variable.
# ^
echo "### k = "$k"" # After
echo
shift;
done
set $k # Set new positional parameters.
echo -
echo $# # Count of positional parameters.
echo -
echo
for i # Omitting the "in list" sets the variable -- i --
#+ to the positional parameters.
do
echo $i # Display new positional parameters.
done
IFS=$OIFS # Restore IFS.
# Question:
# Is it necessary to set an new IFS, internal field separator,
#+ in order for this script to work properly?
# What happens if you don't? Try it.
# And, why use the new IFS -- a colon -- in line 17,
#+ to append to the loop variable?
# What is the purpose of this?
exit 0
$ ./revposparams.sh
### k0 =
### k = a b
### k0 = a b
### k = c a b
### k0 = c a b
### k = d e c a b
-
3
-
d e
c
a b</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Invoking <B
CLASS="COMMAND"
>set</B
> without any options or
arguments simply lists all the <A
HREF="othertypesv.html#ENVREF"
>environmental</A
> and other variables
that have been initialized.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>set</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>AUTHORCOPY=/home/bozo/posts
BASH=/bin/bash
BASH_VERSION=$'2.05.8(1)-release'
...
XAUTHORITY=/home/bozo/.Xauthority
_=/etc/bashrc
variable22=abc
variable23=xzy</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>Using <B
CLASS="COMMAND"
>set</B
> with the <TT
CLASS="OPTION"
>--</TT
>
option explicitly assigns the contents of a variable to
the positional parameters. If no variable follows the
<TT
CLASS="OPTION"
>--</TT
> it <I
CLASS="FIRSTTERM"
>unsets</I
>
the positional parameters.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="SETPOS"
></A
><P
><B
>Example 15-18. Reassigning the positional parameters</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
variable="one two three four five"
set -- $variable
# Sets positional parameters to the contents of "$variable".
first_param=$1
second_param=$2
shift; shift # Shift past first two positional params.
# shift 2 also works.
remaining_params="$*"
echo
echo "first parameter = $first_param" # one
echo "second parameter = $second_param" # two
echo "remaining parameters = $remaining_params" # three four five
echo; echo
# Again.
set -- $variable
first_param=$1
second_param=$2
echo "first parameter = $first_param" # one
echo "second parameter = $second_param" # two
# ======================================================
set --
# Unsets positional parameters if no variable specified.
first_param=$1
second_param=$2
echo "first parameter = $first_param" # (null value)
echo "second parameter = $second_param" # (null value)
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>See also <A
HREF="loops1.html#EX22A"
>Example 11-2</A
> and <A
HREF="extmisc.html#EX33A"
>Example 16-56</A
>.</P
></DD
><DT
><A
NAME="UNSETREF"
></A
><B
CLASS="COMMAND"
>unset</B
></DT
><DD
><P
>The <B
CLASS="COMMAND"
>unset</B
> command deletes a
shell variable, effectively setting it to
<I
CLASS="FIRSTTERM"
>null</I
>. Note that this command does
not affect positional parameters.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>unset PATH</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>echo $PATH</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>&#13;</TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
></PRE
></FONT
></TD
></TR
></TABLE
>
</P
><DIV
CLASS="EXAMPLE"
><A
NAME="UNS"
></A
><P
><B
>Example 15-19. <SPAN
CLASS="QUOTE"
>"Unsetting"</SPAN
> a variable</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# unset.sh: Unsetting a variable.
variable=hello # Initialized.
echo "variable = $variable"
unset variable # Unset.
# In this particular context,
#+ same effect as: variable=
echo "(unset) variable = $variable" # $variable is null.
if [ -z "$variable" ] # Try a string-length test.
then
echo "\$variable has zero length."
fi
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>In most contexts, an <I
CLASS="FIRSTTERM"
>undeclared</I
>
variable and one that has been <I
CLASS="FIRSTTERM"
>unset</I
>
are equivalent. However, the <A
HREF="parameter-substitution.html#UNDDR"
> ${parameter:-default}</A
> parameter substitution
construct can distinguish between the two.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="EXPORTREF"
></A
><B
CLASS="COMMAND"
>export</B
></DT
><DD
><P
><A
NAME="EXPORTREF2"
></A
></P
><P
>The <B
CLASS="COMMAND"
>export</B
>
<A
NAME="AEN9199"
HREF="#FTN.AEN9199"
><SPAN
CLASS="footnote"
>[4]</SPAN
></A
>
command makes available variables to all child processes
of the running script or shell. One important use
of the <B
CLASS="COMMAND"
>export</B
> command is in <A
HREF="files.html#FILESREF1"
>startup files</A
>, to initialize
and make accessible <A
HREF="othertypesv.html#ENVREF"
>environmental
variables</A
> to subsequent user processes.</P
><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
>Unfortunately, <A
HREF="gotchas.html#PARCHILDPROBREF"
> there is no way to export variables back to the parent
process</A
>, to the process that called or invoked the
script or shell.</P
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="EXPORTAWK"
></A
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="COLTOTALER3"
></A
><P
><B
>Example 15-20. Using <I
CLASS="FIRSTTERM"
>export</I
> to pass a variable to an
embedded <I
CLASS="FIRSTTERM"
>awk</I
> script</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Yet another version of the "column totaler" script (col-totaler.sh)
#+ that adds up a specified column (of numbers) in the target file.
# This uses the environment to pass a script variable to 'awk' . . .
#+ and places the awk script in a variable.
ARGS=2
E_WRONGARGS=85
if [ $# -ne "$ARGS" ] # Check for proper number of command-line args.
then
echo "Usage: `basename $0` filename column-number"
exit $E_WRONGARGS
fi
filename=$1
column_number=$2
#===== Same as original script, up to this point =====#
export column_number
# Export column number to environment, so it's available for retrieval.
# -----------------------------------------------
awkscript='{ total += $ENVIRON["column_number"] }
END { print total }'
# Yes, a variable can hold an awk script.
# -----------------------------------------------
# Now, run the awk script.
awk "$awkscript" "$filename"
# Thanks, Stephane Chazelas.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="TIP"
><P
></P
><TABLE
CLASS="TIP"
WIDTH="90%"
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
>It is possible to initialize and export
variables in the same operation, as in <B
CLASS="COMMAND"
>export
var1=xxx</B
>.</P
><P
>However, as Greg Keraunen points out, in certain
situations this may have a different effect than
setting a variable, then exporting it.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>export var=(a b); echo ${var[0]}</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>(a b)</TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>var=(a b); export var; echo ${var[0]}</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>a</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>A variable to be exported may require special
treatment. See <A
HREF="sample-bashrc.html#BASHPROF"
>Example M-2</A
>.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="DECLARE2REF"
></A
><B
CLASS="COMMAND"
>declare</B
>, <B
CLASS="COMMAND"
>typeset</B
></DT
><DD
><P
>The <A
HREF="declareref.html"
>declare</A
> and
<A
HREF="declareref.html"
>typeset</A
> commands specify
and/or restrict properties of variables.</P
></DD
><DT
><A
NAME="READONLYREF"
></A
><B
CLASS="COMMAND"
>readonly</B
></DT
><DD
><P
>Same as <A
HREF="declareref.html"
>declare -r</A
>,
sets a variable as read-only, or, in effect, as a
constant. Attempts to change the variable fail with
an error message. This is the shell analog of the
<I
CLASS="FIRSTTERM"
>C</I
> language <B
CLASS="COMMAND"
>const</B
>
type qualifier.</P
></DD
><DT
><A
NAME="GETOPTSX"
></A
><B
CLASS="COMMAND"
>getopts</B
></DT
><DD
><P
>This powerful tool parses command-line arguments passed
to the script. This is the Bash analog of the <A
HREF="extmisc.html#GETOPTY"
>getopt</A
> external command and the
<I
CLASS="FIRSTTERM"
>getopt</I
> library function familiar to
<I
CLASS="FIRSTTERM"
>C</I
> programmers. It permits passing
and concatenating multiple options
<A
NAME="AEN9289"
HREF="#FTN.AEN9289"
><SPAN
CLASS="footnote"
>[5]</SPAN
></A
>
and associated arguments to a script (for
example <TT
CLASS="USERINPUT"
><B
>scriptname -abc -e
/usr/local</B
></TT
>).</P
><P
><A
NAME="GETOPTSOPT"
></A
></P
><P
>The <B
CLASS="COMMAND"
>getopts</B
> construct uses two implicit
variables. <TT
CLASS="VARNAME"
>$OPTIND</TT
> is the argument
pointer (<I
CLASS="WORDASWORD"
>OPTion INDex</I
>)
and <TT
CLASS="VARNAME"
>$OPTARG</TT
> (<I
CLASS="WORDASWORD"
>OPTion
ARGument</I
>) the (optional) argument attached
to an option. A colon following the option name in the
declaration tags that option as having an associated
argument.</P
><P
>A <B
CLASS="COMMAND"
>getopts</B
> construct usually comes
packaged in a <A
HREF="loops1.html#WHILELOOPREF"
>while
loop</A
>, which processes the options and
arguments one at a time, then increments the implicit
<TT
CLASS="VARNAME"
>$OPTIND</TT
> variable to point to the
next.</P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
> <P
></P
><OL
TYPE="1"
><LI
><P
>The arguments passed from the command-line to
the script must be preceded by a
dash (<TT
CLASS="OPTION"
>-</TT
>). It is the
prefixed <TT
CLASS="OPTION"
>-</TT
> that lets
<B
CLASS="COMMAND"
>getopts</B
> recognize command-line
arguments as <I
CLASS="FIRSTTERM"
>options</I
>.
In fact, <B
CLASS="COMMAND"
>getopts</B
> will not process
arguments without the prefixed <TT
CLASS="OPTION"
>-</TT
>,
and will terminate option processing at the first
argument encountered lacking them.</P
></LI
><LI
><P
>The <B
CLASS="COMMAND"
>getopts</B
> template
differs slightly from the standard <A
HREF="loops1.html#WHILELOOPREF"
>while loop</A
>, in that
it lacks condition brackets.</P
></LI
><LI
><P
>The <B
CLASS="COMMAND"
>getopts</B
> construct is a highly
functional replacement for the traditional
<A
HREF="extmisc.html#GETOPTY"
>getopt</A
> external
command.</P
></LI
></OL
>
</P
></TD
></TR
></TABLE
></DIV
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>while getopts ":abcde:fg" Option
# Initial declaration.
# a, b, c, d, e, f, and g are the options (flags) expected.
# The : after option 'e' shows it will have an argument passed with it.
do
case $Option in
a ) # Do something with variable 'a'.
b ) # Do something with variable 'b'.
...
e) # Do something with 'e', and also with $OPTARG,
# which is the associated argument passed with option 'e'.
...
g ) # Do something with variable 'g'.
esac
done
shift $(($OPTIND - 1))
# Move argument pointer to next.
# All this is not nearly as complicated as it looks &#60;grin&#62;.</PRE
></FONT
></TD
></TR
></TABLE
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX33"
></A
><P
><B
>Example 15-21. Using <I
CLASS="FIRSTTERM"
>getopts</I
> to read the
options/arguments passed to a script</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# ex33.sh: Exercising getopts and OPTIND
# Script modified 10/09/03 at the suggestion of Bill Gradwohl.
# Here we observe how 'getopts' processes command-line arguments to script.
# The arguments are parsed as "options" (flags) and associated arguments.
# Try invoking this script with:
# 'scriptname -mn'
# 'scriptname -oq qOption' (qOption can be some arbitrary string.)
# 'scriptname -qXXX -r'
#
# 'scriptname -qr'
#+ - Unexpected result, takes "r" as the argument to option "q"
# 'scriptname -q -r'
#+ - Unexpected result, same as above
# 'scriptname -mnop -mnop' - Unexpected result
# (OPTIND is unreliable at stating where an option came from.)
#
# If an option expects an argument ("flag:"), then it will grab
#+ whatever is next on the command-line.
NO_ARGS=0
E_OPTERROR=85
if [ $# -eq "$NO_ARGS" ] # Script invoked with no command-line args?
then
echo "Usage: `basename $0` options (-mnopqrs)"
exit $E_OPTERROR # Exit and explain usage.
# Usage: scriptname -options
# Note: dash (-) necessary
fi
while getopts ":mnopq:rs" Option
do
case $Option in
m ) echo "Scenario #1: option -m- [OPTIND=${OPTIND}]";;
n | o ) echo "Scenario #2: option -$Option- [OPTIND=${OPTIND}]";;
p ) echo "Scenario #3: option -p- [OPTIND=${OPTIND}]";;
q ) echo "Scenario #4: option -q-\
with argument \"$OPTARG\" [OPTIND=${OPTIND}]";;
# Note that option 'q' must have an associated argument,
#+ otherwise it falls through to the default.
r | s ) echo "Scenario #5: option -$Option-";;
* ) echo "Unimplemented option chosen.";; # Default.
esac
done
shift $(($OPTIND - 1))
# Decrements the argument pointer so it points to next argument.
# $1 now references the first non-option item supplied on the command-line
#+ if one exists.
exit $?
# As Bill Gradwohl states,
# "The getopts mechanism allows one to specify: scriptname -mnop -mnop
#+ but there is no reliable way to differentiate what came
#+ from where by using OPTIND."
# There are, however, workarounds.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="INTSCRBEH1"
></A
>Script Behavior</B
></P
><DL
><DT
><A
NAME="SOURCEREF"
></A
><B
CLASS="COMMAND"
>source</B
>, <SPAN
CLASS="TOKEN"
>.</SPAN
> (<A
HREF="special-chars.html#DOTREF"
>dot</A
> command)</DT
><DD
><P
>This command, when invoked from the command-line,
executes a script. Within a script, a
<TT
CLASS="USERINPUT"
><B
>source file-name</B
></TT
>
loads the file <TT
CLASS="FILENAME"
>file-name</TT
>.
<I
CLASS="FIRSTTERM"
>Sourcing</I
> a file (dot-command)
<I
CLASS="FIRSTTERM"
>imports</I
>
code into the script, appending to the script (same effect
as the <TT
CLASS="USERINPUT"
><B
>#include</B
></TT
> directive in a
<I
CLASS="FIRSTTERM"
>C</I
> program). The net result is the
same as if the <SPAN
CLASS="QUOTE"
>"sourced"</SPAN
> lines of code were
physically present in the body of the script. This is useful
in situations when multiple scripts use a common data file
or function library.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX38"
></A
><P
><B
>Example 15-22. <SPAN
CLASS="QUOTE"
>"Including"</SPAN
> a data file</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# Note that this example must be invoked with bash, i.e., bash ex38.sh
#+ not sh ex38.sh !
. data-file # Load a data file.
# Same effect as "source data-file", but more portable.
# The file "data-file" must be present in current working directory,
#+ since it is referred to by its basename.
# Now, let's reference some data from that file.
echo "variable1 (from data-file) = $variable1"
echo "variable3 (from data-file) = $variable3"
let "sum = $variable2 + $variable4"
echo "Sum of variable2 + variable4 (from data-file) = $sum"
echo "message1 (from data-file) is \"$message1\""
# Escaped quotes
echo "message2 (from data-file) is \"$message2\""
print_message This is the message-print function in the data-file.
exit $?</PRE
></FONT
></TD
></TR
></TABLE
><P
>File <TT
CLASS="FILENAME"
>data-file</TT
> for <A
HREF="internal.html#EX38"
>Example 15-22</A
>, above. Must be present in same
directory.</P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># This is a data file loaded by a script.
# Files of this type may contain variables, functions, etc.
# It loads with a 'source' or '.' command from a shell script.
# Let's initialize some variables.
variable1=23
variable2=474
variable3=5
variable4=97
message1="Greetings from *** line $LINENO *** of the data file!"
message2="Enough for now. Goodbye."
print_message ()
{ # Echoes any message passed to it.
if [ -z "$1" ]
then
return 1 # Error, if argument missing.
fi
echo
until [ -z "$1" ]
do # Step through arguments passed to function.
echo -n "$1" # Echo args one at a time, suppressing line feeds.
echo -n " " # Insert spaces between words.
shift # Next one.
done
echo
return 0
}</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>If the <I
CLASS="FIRSTTERM"
>sourced</I
> file is itself
an executable script, then it will run, then return
control to the script that called it. A
<I
CLASS="FIRSTTERM"
>sourced</I
> executable script may use a
<A
HREF="complexfunct.html#RETURNREF"
>return</A
> for this
purpose.</P
><P
><A
NAME="SOURCEPARAMS"
></A
></P
><P
> Arguments may be (optionally) passed to the
<I
CLASS="FIRSTTERM"
>sourced</I
> file as <A
HREF="othertypesv.html#POSPARAMREF1"
>positional parameters</A
>.
<TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>source $filename $arg1 arg2</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>It is even possible for a script to
<I
CLASS="FIRSTTERM"
>source</I
> itself, though this does not
seem to have any practical applications.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="SELFSOURCE"
></A
><P
><B
>Example 15-23. A (useless) script that sources itself</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# self-source.sh: a script sourcing itself "recursively."
# From "Stupid Script Tricks," Volume II.
MAXPASSCNT=100 # Maximum number of execution passes.
echo -n "$pass_count "
# At first execution pass, this just echoes two blank spaces,
#+ since $pass_count still uninitialized.
let "pass_count += 1"
# Assumes the uninitialized variable $pass_count
#+ can be incremented the first time around.
# This works with Bash and pdksh, but
#+ it relies on non-portable (and possibly dangerous) behavior.
# Better would be to initialize $pass_count to 0 before incrementing.
while [ "$pass_count" -le $MAXPASSCNT ]
do
. $0 # Script "sources" itself, rather than calling itself.
# ./$0 (which would be true recursion) doesn't work here. Why?
done
# What occurs here is not actually recursion,
#+ since the script effectively "expands" itself, i.e.,
#+ generates a new section of code
#+ with each pass through the 'while' loop',
# with each 'source' in line 20.
#
# Of course, the script interprets each newly 'sourced' "#!" line
#+ as a comment, and not as the start of a new script.
echo
exit 0 # The net effect is counting from 1 to 100.
# Very impressive.
# Exercise:
# --------
# Write a script that uses this trick to actually do something useful.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="EXITREF"
></A
><B
CLASS="COMMAND"
>exit</B
></DT
><DD
><P
>Unconditionally terminates a script.
<A
NAME="AEN9393"
HREF="#FTN.AEN9393"
><SPAN
CLASS="footnote"
>[6]</SPAN
></A
>
The <B
CLASS="COMMAND"
>exit</B
> command may optionally take an
integer argument, which is returned to the shell as
the <A
HREF="exit-status.html#EXITSTATUSREF"
>exit status</A
>
of the script. It is good practice to end all but the
simplest scripts with an <TT
CLASS="USERINPUT"
><B
>exit 0</B
></TT
>,
indicating a successful run.</P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>If a script terminates with an <B
CLASS="COMMAND"
>exit</B
>
lacking an argument, the exit status of the script is the exit
status of the last command executed in the script, not counting
the <B
CLASS="COMMAND"
>exit</B
>. This is equivalent to an
<B
CLASS="COMMAND"
>exit $?</B
>.</P
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>An <B
CLASS="COMMAND"
>exit</B
> command may also be used to
terminate a <A
HREF="subshells.html#SUBSHELLSREF"
>subshell</A
>.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="EXECREF"
></A
><B
CLASS="COMMAND"
>exec</B
></DT
><DD
><P
> This shell builtin replaces the current process with
a specified command. Normally, when the shell encounters
a command, it <A
HREF="internal.html#FORKREF"
>forks off</A
> a
child process to actually execute the command. Using the
<B
CLASS="COMMAND"
>exec</B
> builtin, the shell does not fork,
and the command <I
CLASS="FIRSTTERM"
>exec</I
>'ed replaces
the shell. When used in a script, therefore, it forces an
exit from the script when the <B
CLASS="COMMAND"
>exec</B
>'ed
command terminates.
<A
NAME="AEN9425"
HREF="#FTN.AEN9425"
><SPAN
CLASS="footnote"
>[7]</SPAN
></A
>
</P
><DIV
CLASS="EXAMPLE"
><A
NAME="EX54"
></A
><P
><B
>Example 15-24. Effects of <I
CLASS="FIRSTTERM"
>exec</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
exec echo "Exiting \"$0\" at line $LINENO." # Exit from script here.
# $LINENO is an internal Bash variable set to the line number it's on.
# ----------------------------------
# The following lines never execute.
echo "This echo fails to echo."
exit 99 # This script will not exit here.
# Check exit value after script terminates
#+ with an 'echo $?'.
# It will *not* be 99.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="SELFEXEC"
></A
><P
><B
>Example 15-25. A script that <I
CLASS="FIRSTTERM"
>exec's</I
> itself</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# self-exec.sh
# Note: Set permissions on this script to 555 or 755,
# then call it with ./self-exec.sh or sh ./self-exec.sh.
echo
echo "This line appears ONCE in the script, yet it keeps echoing."
echo "The PID of this instance of the script is still $$."
# Demonstrates that a subshell is not forked off.
echo "==================== Hit Ctl-C to exit ===================="
sleep 1
exec $0 # Spawns another instance of this same script
#+ that replaces the previous one.
echo "This line will never echo!" # Why not?
exit 99 # Will not exit here!
# Exit code will not be 99!</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>An <B
CLASS="COMMAND"
>exec</B
> also serves to <A
HREF="x17974.html#USINGEXECREF"
>reassign
file descriptors</A
>. For example, <TT
CLASS="USERINPUT"
><B
>exec
&#60;zzz-file</B
></TT
> replaces <TT
CLASS="FILENAME"
>stdin</TT
>
with the file <TT
CLASS="FILENAME"
>zzz-file</TT
>.</P
><DIV
CLASS="NOTE"
><P
></P
><TABLE
CLASS="NOTE"
WIDTH="90%"
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
>The <TT
CLASS="OPTION"
>-exec</TT
> option to
<A
HREF="moreadv.html#FINDREF"
>find</A
> is
<TT
CLASS="REPLACEABLE"
><I
>not</I
></TT
> the same as the
<B
CLASS="COMMAND"
>exec</B
> shell builtin.</P
></TD
></TR
></TABLE
></DIV
></DD
><DT
><A
NAME="SHOPTREF"
></A
><B
CLASS="COMMAND"
>shopt</B
></DT
><DD
><P
>This command permits changing <I
CLASS="FIRSTTERM"
>shell
options</I
> on the fly (see <A
HREF="aliases.html#AL"
>Example 25-1</A
>
and <A
HREF="aliases.html#UNAL"
>Example 25-2</A
>). It often appears in the Bash
<A
HREF="files.html#FILESREF1"
>startup files</A
>, but also has
its uses in scripts. Needs <A
HREF="bashver2.html#BASH2REF"
>version
2</A
> or later of Bash.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>shopt -s cdspell
# Allows minor misspelling of directory names with 'cd'
# Option -s sets, -u unsets.
cd /hpme # Oops! Mistyped '/home'.
pwd # /home
# The shell corrected the misspelling.</PRE
></FONT
></TD
></TR
></TABLE
></P
></DD
><DT
><B
CLASS="COMMAND"
>caller</B
><A
NAME="CALLERREF"
></A
></DT
><DD
><P
>Putting a <B
CLASS="COMMAND"
>caller</B
> command
inside a <A
HREF="functions.html#FUNCTIONREF"
>function</A
>
echoes to <TT
CLASS="FILENAME"
>stdout</TT
> information about
the <I
CLASS="FIRSTTERM"
>caller</I
> of that function.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
function1 ()
{
# Inside function1 ().
caller 0 # Tell me about it.
}
function1 # Line 9 of script.
# 9 main test.sh
# ^ Line number that the function was called from.
# ^^^^ Invoked from "main" part of script.
# ^^^^^^^ Name of calling script.
caller 0 # Has no effect because it's not inside a function.</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>A <B
CLASS="COMMAND"
>caller</B
> command can also return
<I
CLASS="FIRSTTERM"
>caller</I
> information from a script <A
HREF="internal.html#SOURCEREF"
>sourced</A
> within another
script. Analogous to a function, this is a <SPAN
CLASS="QUOTE"
>"subroutine
call."</SPAN
></P
><P
>You may find this command useful in debugging.</P
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
><A
NAME="INTCOMMAND1"
></A
>Commands</B
></P
><DL
><DT
><A
NAME="TRUEREF"
></A
><B
CLASS="COMMAND"
>true</B
></DT
><DD
><P
>A command that returns a successful
(<SPAN
CLASS="RETURNVALUE"
>zero</SPAN
>) <A
HREF="exit-status.html#EXITSTATUSREF"
>exit status</A
>, but does
nothing else.
</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>true</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>echo $?</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>0</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Endless loop
while true # alias for ":"
do
operation-1
operation-2
...
operation-n
# Need a way to break out of loop or script will hang.
done</PRE
></FONT
></TD
></TR
></TABLE
></P
></DD
><DT
><A
NAME="FALSEREF"
></A
><B
CLASS="COMMAND"
>false</B
></DT
><DD
><P
>A command that returns an unsuccessful <A
HREF="exit-status.html#EXITSTATUSREF"
>exit status</A
>,
but does nothing else.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>false</B
></TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>echo $?</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>1</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Testing "false"
if false
then
echo "false evaluates \"true\""
else
echo "false evaluates \"false\""
fi
# false evaluates "false"
# Looping while "false" (null loop)
while false
do
# The following code will not execute.
operation-1
operation-2
...
operation-n
# Nothing happens!
done </PRE
></FONT
></TD
></TR
></TABLE
></P
></DD
><DT
><A
NAME="TYPEREF"
></A
><B
CLASS="COMMAND"
>type [cmd]</B
></DT
><DD
><P
>Similar to the <A
HREF="filearchiv.html#WHICHREF"
>which</A
> external command,
<B
CLASS="COMMAND"
>type cmd</B
> identifies
<SPAN
CLASS="QUOTE"
>"cmd."</SPAN
> Unlike <B
CLASS="COMMAND"
>which</B
>,
<B
CLASS="COMMAND"
>type</B
> is a Bash builtin. The useful
<TT
CLASS="OPTION"
>-a</TT
> option to <B
CLASS="COMMAND"
>type</B
>
identifies <TT
CLASS="REPLACEABLE"
><I
>keywords</I
></TT
>
and <TT
CLASS="REPLACEABLE"
><I
>builtins</I
></TT
>, and also locates
system commands with identical names.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>type '['</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>[ is a shell builtin</TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>type -a '['</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>[ is a shell builtin
[ is /usr/bin/[</TT
>
<TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>type type</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>type is a shell builtin</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
><P
>The <B
CLASS="COMMAND"
>type</B
> command can be useful
for <A
HREF="special-chars.html#DEVNULLREDIRECT"
>testing whether a
certain command exists</A
>.</P
></DD
><DT
><A
NAME="HASHCMDREF"
></A
><B
CLASS="COMMAND"
>hash [cmds]</B
></DT
><DD
><P
>Records the <I
CLASS="FIRSTTERM"
>path</I
>
name of specified commands -- in the shell <I
CLASS="FIRSTTERM"
>hash
table</I
>
<A
NAME="AEN9591"
HREF="#FTN.AEN9591"
><SPAN
CLASS="footnote"
>[8]</SPAN
></A
>
-- so the shell or script will not need to search the
<A
HREF="internalvariables.html#PATHREF"
>$PATH</A
> on subsequent calls to those
commands. When <B
CLASS="COMMAND"
>hash</B
> is called with no
arguments, it simply lists the commands that have been hashed.
The <TT
CLASS="OPTION"
>-r</TT
> option resets the hash table.</P
></DD
><DT
><A
NAME="BINDREF"
></A
><B
CLASS="COMMAND"
>bind</B
></DT
><DD
><P
>The <B
CLASS="COMMAND"
>bind</B
> builtin displays or modifies
<I
CLASS="FIRSTTERM"
>readline</I
>
<A
NAME="AEN9621"
HREF="#FTN.AEN9621"
><SPAN
CLASS="footnote"
>[9]</SPAN
></A
>
key bindings.</P
></DD
><DT
><A
NAME="HELPREF"
></A
><B
CLASS="COMMAND"
>help</B
></DT
><DD
><P
>Gets a short usage summary of a shell builtin. This is
the counterpart to <A
HREF="filearchiv.html#WHATISREF"
>whatis</A
>,
but for builtins. The display of <I
CLASS="FIRSTTERM"
>help</I
>
information got a much-needed update in the <A
HREF="bashver4.html#BASH4REF"
>version 4 release</A
> of Bash.</P
><P
> <TABLE
BORDER="1"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="SCREEN"
><TT
CLASS="PROMPT"
>bash$ </TT
><TT
CLASS="USERINPUT"
><B
>help exit</B
></TT
>
<TT
CLASS="COMPUTEROUTPUT"
>exit: exit [n]
Exit the shell with a status of N. If N is omitted, the exit status
is that of the last command executed.</TT
>
</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></DD
></DL
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN8607"
HREF="internal.html#AEN8607"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>As Nathan Coulter points out, "while forking a
process is a low-cost operation, executing a new program in
the newly-forked child process adds more
overhead."</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN8650"
HREF="internal.html#AEN8650"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>An exception to this is the <A
HREF="timedate.html#TIMREF"
>time</A
> command, listed in the
official Bash documentation as a keyword (<SPAN
CLASS="QUOTE"
>"reserved
word"</SPAN
>).</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9009"
HREF="internal.html#AEN9009"
><SPAN
CLASS="footnote"
>[3]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Note that <I
CLASS="FIRSTTERM"
>let</I
>
<A
HREF="gotchas.html#LETBAD"
>cannot be used
for setting <I
CLASS="FIRSTTERM"
>string</I
>
variables.</A
></P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9199"
HREF="internal.html#AEN9199"
><SPAN
CLASS="footnote"
>[4]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>To <I
CLASS="FIRSTTERM"
>Export</I
>
information is to make it available in a more general context.
See also <A
HREF="subshells.html#SCOPEREF"
>scope</A
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9289"
HREF="internal.html#AEN9289"
><SPAN
CLASS="footnote"
>[5]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>An <I
CLASS="FIRSTTERM"
>option</I
> is an
argument that acts as a flag, switching script behaviors
on or off. The argument associated with a particular
option indicates the behavior that the option (flag)
switches on or off.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9393"
HREF="internal.html#AEN9393"
><SPAN
CLASS="footnote"
>[6]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Technically, an
<B
CLASS="COMMAND"
>exit</B
> only terminates the
process (or shell) in which it is running,
<EM
>not</EM
> the <I
CLASS="FIRSTTERM"
>parent
process</I
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9425"
HREF="internal.html#AEN9425"
><SPAN
CLASS="footnote"
>[7]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Unless the <B
CLASS="COMMAND"
>exec</B
> is used
to <A
HREF="x17974.html#USINGEXECREF"
>reassign file
descriptors</A
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9591"
HREF="internal.html#AEN9591"
><SPAN
CLASS="footnote"
>[8]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
><A
NAME="HASHREF"
></A
></P
><P
><I
CLASS="FIRSTTERM"
>Hashing</I
> is a method of
creating lookup keys for data stored in a table. The
<EM
>data items themselves</EM
> are
<SPAN
CLASS="QUOTE"
>"scrambled"</SPAN
> to create keys, using one of
a number of simple mathematical
<I
CLASS="FIRSTTERM"
>algorithms</I
> (methods, or
recipes).</P
><P
>An advantage of <I
CLASS="FIRSTTERM"
>hashing</I
> is that
it is fast. A disadvantage is that
<I
CLASS="FIRSTTERM"
>collisions</I
> -- where a single key
maps to more than one data item -- are possible.</P
><P
>For examples of hashing see <A
HREF="contributed-scripts.html#HASHLIB"
>Example A-20</A
> and
<A
HREF="contributed-scripts.html#HASHEXAMPLE"
>Example A-21</A
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN9621"
HREF="internal.html#AEN9621"
><SPAN
CLASS="footnote"
>[9]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
><A
NAME="READLINEREF"
></A
>The
<I
CLASS="FIRSTTERM"
>readline</I
> library is what
Bash uses for reading input in an
interactive shell.</P
></TD
></TR
></TABLE
><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="part4.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="x9644.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Commands</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="part4.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Job Control Commands</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>