5640 lines
86 KiB
HTML
5640 lines
86 KiB
HTML
<!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 "$@" >&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.
|
||
} <$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 <ENTER>."
|
||
echo "Then, enter a second string (no \\ this time), and again press <ENTER>."
|
||
|
||
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 <ENTER>."
|
||
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 <ENTER>.
|
||
|
||
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 <data-file
|
||
echo "var1 = $var1"
|
||
# var1 set to the entire first line of the input file "data-file"
|
||
|
||
read var2 var3 <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 <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 </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 </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 && tar cf - . ) | (cd /dest/directory && 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 <<= 3" # Equivalent to let "a = a << 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<7?7:11" # True
|
||
echo $t # 7
|
||
|
||
let a++
|
||
let "t = a<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&=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"
|
||
> </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 <grin>.</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
|
||
<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
|
||
> |