This commit is contained in:
gferg 2005-06-05 14:39:50 +00:00
parent a748bd3591
commit 95201486ee
24 changed files with 572 additions and 274 deletions

View File

@ -6,6 +6,71 @@
http://personal.riverusers.com/~thegrendel/Change.log http://personal.riverusers.com/~thegrendel/Change.log
------------------------------------------------------------------------ ------------------------------------------------------------------------
Version 3.5
Boxberry release, 06/05/05
1) In "Indirect References to Variables" section of "Variables Revisited" chapter:
Added "ugly kludge" sidebar.
2) In "Variable Assignment" section of "Introduction to Variables and
Parameters" chapter:
After "ex16.sh" example, noted that $(...) construct is a form of
command substitution.
3) In "Special Variable Types" section of "Introduction to Variables and
Parameters" chapter:
In note about remedies for missing command-line parameters,
added discussion about using parameter substitution,
rather than adding extra characters to variable names.
(Thank you, Fabian Kreutz.)
4) In "Command Substitution" chapter:
At note at end of chapter, added mention that nesting is possible
with $(...) construct, and added "agram2.sh" example script.
5) In "Internal Commands and Builtins" chapter:
At "exec" entry, added footnote and streamlined text.
6) In "File and Archiving Commands" section of "External Commands" Chapter:
At "more/less" entry, added "testing command sequence" usage example.
At "mktemp" entry, added usage example and elaborated existing example.
7) In "Communications Commands" section of "External Commands" chapter:
At "dig" entry, added "spam-lookup.sh" example.
8) In "Text Processing" section of "External Commands" Chapter:
At "tail" entry, added usage example of combining "head" and "tail"
to extract a specific line or lines from a text file.
9) In "System and Administrative Commands" chapter:
At "umask" entry, added "rot13a.sh" example script.
10) In "Complex Functions and Function Complexities" section of "Functions"
chapter:
Added in-line example ("days in month") of capturing function "echo"
as "return value."
11) In the "Shell Wrappers" section of "Miscellany" chapter:
Added "pr-asc.sh" example script.
12) Changed "Files" chapter into an appendix.
13) In the "Sed and Awk Micro-primer" appendix:
More examples of the awk "print" command.
14) In "Contributed Scripts" appendix:
Added "whx.sh" script.
(Thank you, Walter Dnes.)
15) Miscellaneous fixups and stylistic changes in the text and various
scripts.
Fixed up three misspellings of "permissible"!
Updated morethan.org site address.
Version 3.4 Version 3.4
Teaberry release, 05/08/05 Teaberry release, 05/08/05
@ -259,7 +324,8 @@ Blueberry release, 02/06/05
3) In "Colorizing Scripts" section of "Miscellany" chapter: 3) In "Colorizing Scripts" section of "Miscellany" chapter:
Added link to Henry/teikedvl's utility for creating colorized scripts. Added link to Henry/teikedvl's utility for creating colorized scripts.
4) In "Complex Functions and Function Complexities" chapter: 4) In "Complex Functions and Function Complexities" section of "Functions"
chapter:
Added "func-cmdlinearg.sh" example script to clear up confusion Added "func-cmdlinearg.sh" example script to clear up confusion
about command-line args passed to a script. about command-line args passed to a script.

View File

@ -22,21 +22,21 @@ require no fixups to execute as expected.
Scripts needing to be altered: Scripts needing to be altered:
----------------------------- -----------------------------
Du.sh Du.sh (line 19)
encryptedpw.sh (lines 27 and 35) encryptedpw.sh (lines 27 and 35)
ex57.sh ex57.sh (comment in line 8)
ex70.sh ex70.sh (line 3)
ex71.sh ex71.sh (line 7)
ex71a.sh ex71a.sh (line 8)
ex71b.sh ex71b.sh (line 22)
logevents.sh logevents.sh (lines 29, 36-39, 44, 53, 55, 60, 64)
m4.sh m4.sh (line 8)
pb.sh pw.sh (comment in line 4)
pw.sh read-r.sh (lines 5, 6, 19, 26)
read-r.sh rnd.sh (comments in lines 38, 55, 64)
rnd.sh rot13.sh (comment, line 6)
rot13.sh rot13a.sh (comment, line 5)
here-function.sh here-function.sh (line 16)
avoid-subshell.sh (lines 24, 25, and 33) avoid-subshell.sh (lines 24, 25, and 33)
usb.sh (line 28) usb.sh (line 28)
prepend.sh (lines 18 and 28) prepend.sh (lines 18 and 28)
@ -59,3 +59,5 @@ Hash.lib, (comments in lines lines 103 and 116: & --> &)
(comment in line 3: < --> &lt;, > --> &gt;) (comment in line 3: < --> &lt;, > --> &gt;)
hash-example.sh (comment in line 3: < --> &lt;, > --> &gt;) hash-example.sh (comment in line 3: < --> &lt;, > --> &gt;)
quote-fetch.sh (comment in line 26: &amp; --> &) quote-fetch.sh (comment in line 26: &amp; --> &)
ftpget.sh (comment in line 28)
whx.sh (comment in line 230)

View File

@ -140,6 +140,7 @@ Uncomment line below to generate index.
<!ENTITY lookup SYSTEM "lookup.sh"> <!ENTITY lookup SYSTEM "lookup.sh">
<!ENTITY arglist SYSTEM "arglist.sh"> <!ENTITY arglist SYSTEM "arglist.sh">
<!ENTITY rot13 SYSTEM "rot13.sh"> <!ENTITY rot13 SYSTEM "rot13.sh">
<!ENTITY rot13a SYSTEM "rot13a.sh">
<!ENTITY rot14 SYSTEM "rot14.sh"> <!ENTITY rot14 SYSTEM "rot14.sh">
<!ENTITY filecomp SYSTEM "file-comparison.sh"> <!ENTITY filecomp SYSTEM "file-comparison.sh">
<!ENTITY adddrv SYSTEM "add-drive.sh"> <!ENTITY adddrv SYSTEM "add-drive.sh">
@ -260,6 +261,7 @@ Uncomment line below to generate index.
<!ENTITY factr SYSTEM "factr.sh"> <!ENTITY factr SYSTEM "factr.sh">
<!ENTITY cannon SYSTEM "cannon.sh"> <!ENTITY cannon SYSTEM "cannon.sh">
<!ENTITY agram SYSTEM "agram.sh"> <!ENTITY agram SYSTEM "agram.sh">
<!ENTITY agram2 SYSTEM "agram2.sh">
<!ENTITY poem SYSTEM "poem.sh"> <!ENTITY poem SYSTEM "poem.sh">
<!ENTITY soundex SYSTEM "soundex.sh"> <!ENTITY soundex SYSTEM "soundex.sh">
<!ENTITY tempfilename SYSTEM "tempfile-name.sh"> <!ENTITY tempfilename SYSTEM "tempfile-name.sh">
@ -322,6 +324,9 @@ Uncomment line below to generate index.
<!ENTITY hashlib SYSTEM "Hash.lib"> <!ENTITY hashlib SYSTEM "Hash.lib">
<!ENTITY hashexample SYSTEM "hash-example.sh"> <!ENTITY hashexample SYSTEM "hash-example.sh">
<!ENTITY getoptsimple SYSTEM "getopt-simple.sh"> <!ENTITY getoptsimple SYSTEM "getopt-simple.sh">
<!ENTITY prasc SYSTEM "pr-asc.sh">
<!ENTITY whx SYSTEM "whx.sh">
<!ENTITY spamlookup SYSTEM "spam-lookup.sh">
<!ENTITY gen0data SYSTEM "gen0"> <!ENTITY gen0data SYSTEM "gen0">
<!ENTITY cdll SYSTEM "cdll"> <!ENTITY cdll SYSTEM "cdll">
<!ENTITY bashrc SYSTEM "bashrc"> <!ENTITY bashrc SYSTEM "bashrc">
@ -342,19 +347,12 @@ Uncomment line below to generate index.
</affiliation> </affiliation>
</author> </author>
<releaseinfo>3.4</releaseinfo> <releaseinfo>3.5</releaseinfo>
<pubdate>08 May 2005</pubdate> <pubdate>05 June 2005</pubdate>
<revhistory> <revhistory>
<revision>
<revnumber>3.1</revnumber>
<date>14 Nov 2004</date>
<authorinitials>mc</authorinitials>
<revremark>'BAYBERRY' release: Bugfix update.</revremark>
</revision>
<revision> <revision>
<revnumber>3.2</revnumber> <revnumber>3.2</revnumber>
<date>06 Feb 2005</date> <date>06 Feb 2005</date>
@ -376,6 +374,13 @@ Uncomment line below to generate index.
<revremark>'TEABERRY' release: Important Update.</revremark> <revremark>'TEABERRY' release: Important Update.</revremark>
</revision> </revision>
<revision>
<revnumber>3.5</revnumber>
<date>04 June 2005</date>
<authorinitials>mc</authorinitials>
<revremark>'BOXBERRY' release: Important Update.</revremark>
</revision>
</revhistory> </revhistory>
@ -396,7 +401,7 @@ Uncomment line below to generate index.
introduction to programming concepts.</para> introduction to programming concepts.</para>
<para><ulink <para><ulink
url="http://personal.riverusers.com/~thegrendel/abs-guide-3.4.tar.bz2"> url="http://personal.riverusers.com/~thegrendel/abs-guide-3.5.tar.bz2">
The latest update of this document</ulink>, as an archived, <link The latest update of this document</ulink>, as an archived, <link
linkend="bzipref">bzip2-ed</link> <quote>tarball</quote> linkend="bzipref">bzip2-ed</link> <quote>tarball</quote>
including both the SGML source and rendered HTML, may including both the SGML source and rendered HTML, may
@ -3047,7 +3052,9 @@ echo "$uninitialized" # 5
<para>Variable assignment using the <firstterm>$(...)</firstterm> <para>Variable assignment using the <firstterm>$(...)</firstterm>
mechanism (a newer method than <link mechanism (a newer method than <link
linkend="backquotesref">backquotes</link>)</para> linkend="backquotesref">backquotes</link>). This is
actually a form of <link linkend="commandsubref">command
substitution</link>.</para>
<para><programlisting># From /etc/rc.d/rc.local <para><programlisting># From /etc/rc.d/rc.local
R=$(cat /etc/redhat-release) R=$(cat /etc/redhat-release)
@ -3247,7 +3254,15 @@ variable1=${variable1_/_/}
if [ -z $1 ] if [ -z $1 ]
then then
exit $E_MISSING_POS_PARAM exit $E_MISSING_POS_PARAM
fi fi
# However, as Fabian Kreutz points out,
#+ the above method may have unexpected side-effects.
# A better method is parameter substitution:
# ${1:-$DefaultVal}
# See the "Parameter Substition" section
#+ in the "Variables Revisited" chapter.
</programlisting> </programlisting>
<para>---</para> <para>---</para>
@ -4832,8 +4847,9 @@ home=/home/bozo
<programlisting>if [ "$exp1" -a "$exp2" ]</programlisting> <programlisting>if [ "$exp1" -a "$exp2" ]</programlisting>
</para> </para>
<para>Refer to <xref linkend="andor"> and <xref linkend="twodim"> <para>Refer to <xref linkend="andor">, <xref linkend="twodim">,
to see compound comparison operators in action.</para> and <xref linkend="whx"> to see compound comparison operators
in action.</para>
</sect1> <!-- Comparison operators (binary) --> </sect1> <!-- Comparison operators (binary) -->
@ -8238,6 +8254,13 @@ echo $? # 1
(see <xref linkend="ex78">) makes indirect referencing more (see <xref linkend="ex78">) makes indirect referencing more
intuitive.</para></caution> intuitive.</para></caution>
<sidebar>
<para>Bash does not support pointer arithmetic, and this severely
limits the usefulness of indirect referencing. In fact, indirect
referencing in a scripting language is an ugly kludge.</para>
</sidebar>
</sect1> <!-- Indirect References to Variables --> </sect1> <!-- Indirect References to Variables -->
@ -8416,16 +8439,17 @@ rnumber=$(((RANDOM%30/3+1)*3))
<title>Loops and Branches</title> <title>Loops and Branches</title>
<para>Operations on code blocks are the key to structured, organized <para>Operations on code blocks are the key to structured and organized
shell scripts. Looping and branching constructs provide the tools for shell scripts. Looping and branching constructs provide the tools for
accomplishing this.</para> accomplishing this.</para>
<sect1 id="loops1"> <sect1 id="loops1">
<title>Loops</title> <title>Loops</title>
<para>A <firstterm>loop</firstterm> is a block of code that iterates <para>A <firstterm>loop</firstterm> is a block of code that
(repeats) a list of commands as long as the <firstterm>loop <firstterm>iterates</firstterm> (repeats) a list of commands
control condition</firstterm> is true.</para> as long as the <firstterm>loop control condition</firstterm>
is true.</para>
<variablelist id="forloopref"> <variablelist id="forloopref">
@ -8469,11 +8493,11 @@ rnumber=$(((RANDOM%30/3+1)*3))
<replaceable>list</replaceable>.</para></note> <replaceable>list</replaceable>.</para></note>
<para><programlisting>for arg in "$var1" "$var2" "$var3" ... "$varN" <para><programlisting>for arg in "$var1" "$var2" "$var3" ... "$varN"
# In pass 1 of the loop, $arg = $var1 # In pass 1 of the loop, arg = $var1
# In pass 2 of the loop, $arg = $var2 # In pass 2 of the loop, arg = $var2
# In pass 3 of the loop, $arg = $var3 # In pass 3 of the loop, arg = $var3
# ... # ...
# In pass N of the loop, $arg = $varN # In pass N of the loop, arg = $varN
# Arguments in [list] quoted to prevent possible word splitting.</programlisting></para> # Arguments in [list] quoted to prevent possible word splitting.</programlisting></para>
@ -8501,11 +8525,11 @@ rnumber=$(((RANDOM%30/3+1)*3))
<note><para>Each <userinput>[list]</userinput> element <note><para>Each <userinput>[list]</userinput> element
may contain multiple parameters. This is useful when may contain multiple parameters. This is useful when
processing parameters in groups. In such cases, use the processing parameters in groups. In such cases,
<command>set</command> command (see <xref linkend="ex34">) use the <link linkend="setref">set</link> command
to force parsing of each <userinput>[list]</userinput> (see <xref linkend="ex34">) to force parsing of each
element and assignment of each component to the positional <userinput>[list]</userinput> element and assignment of
parameters.</para></note> each component to the positional parameters.</para></note>
<example id="ex22a"> <example id="ex22a">
<title><command>for</command> loop with two parameters in each <title><command>for</command> loop with two parameters in each
@ -10193,16 +10217,19 @@ shift $(($OPTIND - 1))
</indexterm> </indexterm>
<listitem> <listitem>
<para>This shell builtin replaces the current process with <para>
This shell builtin replaces the current process with
a specified command. Normally, when the shell encounters a specified command. Normally, when the shell encounters
a command, it <link linkend="forkref">forks off</link> a a command, it <link linkend="forkref">forks off</link> a
child process to actually execute the command. Using the child process to actually execute the command. Using the
<command>exec</command> builtin, the shell does not fork, <command>exec</command> builtin, the shell does not fork,
and the command exec'ed replaces the shell. When used in and the command exec'ed replaces the shell. When used in
a script, therefore, it forces an exit from the script when a script, therefore, it forces an exit from the script when
the <command>exec</command>'ed command terminates. For this the <command>exec</command>'ed command terminates.
reason, if an <command>exec</command> appears in a script, <footnote><para>Unless the <command>exec</command> is used
it would probably be the final command.</para> to <link linkend="usingexecref">reassign file
descriptors</link>.</para></footnote>
</para>
<example id="ex54"> <example id="ex54">
<title>Effects of <command>exec</command></title> <title>Effects of <command>exec</command></title>
@ -10214,11 +10241,11 @@ shift $(($OPTIND - 1))
<programlisting>&selfexec;</programlisting> <programlisting>&selfexec;</programlisting>
</example> </example>
<para>An <command>exec</command> also serves to reassign <link <para>An <command>exec</command> also serves to <link
linkend="fdref">file descriptors</link>. <userinput>exec linkend="usingexecref">reassign
file descriptors</link>. For example, <userinput>exec
&lt;zzz-file</userinput> replaces <filename>stdin</filename> &lt;zzz-file</userinput> replaces <filename>stdin</filename>
with the file <filename>zzz-file</filename> (see <xref with the file <filename>zzz-file</filename>.</para>
linkend="redir1">).</para>
<note><para>The <option>-exec</option> option to <note><para>The <option>-exec</option> option to
<link linkend="findref">find</link> is <link linkend="findref">find</link> is
@ -11392,12 +11419,12 @@ chmod u+s filename
<listitem> <listitem>
<para>-exec <replaceable>COMMAND</replaceable> \;</para> <para>-exec <replaceable>COMMAND</replaceable> \;</para>
<para>Carries out <replaceable>COMMAND</replaceable> on <para>Carries out <replaceable>COMMAND</replaceable> on
each file that <command>find</command> matches. each file that <command>find</command> matches. The
The command sequence terminates with <token>\;</token> command sequence terminates with <token>;</token> (the
(the <quote>;</quote> is escaped to make certain <quote>;</quote> is <link linkend="escp">escaped</link> to
the shell passes it to <command>find</command> make certain the shell passes it to <command>find</command>
literally).</para> literally, without interpreting it as a special character).</para>
<para> <para>
<screen><prompt>bash$ </prompt><userinput>find ~/ -name '*.txt'</userinput> <screen><prompt>bash$ </prompt><userinput>find ~/ -name '*.txt'</userinput>
@ -12380,6 +12407,21 @@ done
<programlisting>&ex12;</programlisting> <programlisting>&ex12;</programlisting>
</example> </example>
<tip>
<para>To list a specific line of a text file,
<link linkend="piperef">pipe</link> the output of
<command>head</command> to <command>tail -1</command>.
For example <userinput>head -8 database.txt | tail
-1</userinput> lists the 8th line of the file
<filename>database.txt</filename>.</para>
<para>To set a variable to a given block of a text file:
<programlisting>var=$(head -$m $filename | tail -$n)
# filename = name of file
# m = from beginning of file, number of lines to end of block
# n = number of lines to set variable to (trim from end of block)</programlisting></para>
</tip>
<para>See also <xref linkend="ex41">, <xref linkend="ex52"> and <para>See also <xref linkend="ex41">, <xref linkend="ex52"> and
<xref linkend="online">.</para> <xref linkend="online">.</para>
@ -14501,15 +14543,39 @@ gzip -cd patchXX.gz | patch -p0
<secondary>filename</secondary> <secondary>filename</secondary>
</indexterm> </indexterm>
<listitem> <listitem>
<para>Create a temporary file with a <quote>unique</quote> <para>Create a <firstterm>temporary file</firstterm>
filename.</para>
<footnote><para>Creates a temporary
<emphasis>directory</emphasis> when invoked with the
<option>-d</option> option.</para></footnote>
with a <quote>unique</quote> filename. When invoked
from the command line without additional arguments,
it creates a zero-length file in the <filename
class="directory">/tmp</filename> directory.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>mktemp</userinput>
<computeroutput>/tmp/tmp.zzsvql3154</computeroutput>
</screen>
</para>
<para><programlisting>PREFIX=filename <para><programlisting>PREFIX=filename
tempfile=`mktemp $PREFIX.XXXXXX` tempfile=`mktemp $PREFIX.XXXXXX`
# ^^^^^^ Need at least 6 placeholders # ^^^^^^ Need at least 6 placeholders
#+ in the filename template. #+ in the filename template.
# If no filename template supplied,
#+ "tmp.XXXXXXXXXX" is the default.
echo "tempfile name = $tempfile" echo "tempfile name = $tempfile"
# tempfile name = filename.QA2ZpY # tempfile name = filename.QA2ZpY
# or something similar...</programlisting></para> # or something similar...
# Creates a file of that name in the current working directory
#+ with 600 file permissions.
# A "umask 177" is therefore unnecessary,
# but it's good programming practice anyhow.</programlisting></para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -14606,9 +14672,24 @@ echo "tempfile name = $tempfile"
<secondary>less</secondary> <secondary>less</secondary>
</indexterm> </indexterm>
<listitem> <listitem>
<para>Pagers that display a text file or stream to <para>Pagers that display a text file or stream to
<filename>stdout</filename>, one screenful at a time. <filename>stdout</filename>, one screenful at a time.
These may be used to filter the output of a script.</para> These may be used to filter the output of
<filename>stdout</filename> . . . or of a script.</para>
<para>
An interesting application of <command>more</command>
is to <quote>test drive</quote> a command sequence,
to forestall potentially unpleasant consequences.
<programlisting>ls /home/bozo | awk '{print "rm -rf " $1}' | more
# ^^^^
# Testing the effect of the following (disastrous) command line:
# ls /home/bozo | awk '{print "rm -rf " $1}' | sh
# Hand off to the shell to execute . . . ^^</programlisting>
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -14752,8 +14833,13 @@ echo "tempfile name = $tempfile"
</screen> </screen>
</para> </para>
<example id="spamlookup">
<title>Finding out where to report a spammer</title>
<programlisting>&spamlookup;</programlisting>
</example>
<example id="isspammer"> <example id="isspammer">
<title>Checking a spam domain</title> <title>Analyzing a spam domain</title>
<programlisting>&isspammer;</programlisting> <programlisting>&isspammer;</programlisting>
</example> </example>
@ -14843,7 +14929,7 @@ echo "tempfile name = $tempfile"
<para>Perform a DNS (Domain Name System) lookup. <para>Perform a DNS (Domain Name System) lookup.
The <option>-h</option> option permits specifying which The <option>-h</option> option permits specifying which
particular <emphasis>whois</emphasis> server to query. See particular <emphasis>whois</emphasis> server to query. See
<xref linkend="ex18">.</para> <xref linkend="ex18"> and <xref linkend="spamlookup">.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -19119,18 +19205,20 @@ exit 0 # Will not exit here, because this script will never terminate.</pr
</indexterm> </indexterm>
<term><anchor id="umaskref"><command>umask</command></term> <term><anchor id="umaskref"><command>umask</command></term>
<listitem> <listitem>
<para>User file creation MASK. Limit the default file attributes <para>User file creation permissions
for a particular user. All files created by that user take <firstterm>mask</firstterm>. Limit the default file
on the attributes specified by <command>umask</command>. The attributes for a particular user. All files created
(octal) value passed to <command>umask</command> defines the by that user take on the attributes specified by
file permissions <emphasis>disabled</emphasis>. For example, <command>umask</command>. The (octal) value passed to
<command>umask 022</command> ensures that new files will <command>umask</command> defines the file permissions
have at most 755 permissions (777 NAND 022). <firstterm>disabled</firstterm>. For example, <command>umask
022</command> ensures that new files will have at most
755 permissions (777 NAND 022).
<footnote><para>NAND is the logical
<firstterm>not-and</firstterm> operator. Its effect
is somewhat similar to subtraction.</para></footnote>
<footnote><para>NAND is the logical <quote>not-and</quote>
operator. Its effect is somewhat similar to
subtraction.</para></footnote>
Of course, the user may later change the Of course, the user may later change the
attributes of particular files with <link attributes of particular files with <link
linkend="chmodref">chmod</link>. The usual practice linkend="chmodref">chmod</link>. The usual practice
@ -19139,6 +19227,12 @@ exit 0 # Will not exit here, because this script will never terminate.</pr
<filename>~/.bash_profile</filename> (see <xref <filename>~/.bash_profile</filename> (see <xref
linkend="files">).</para> linkend="files">).</para>
<example id="rot13a">
<title>Using <command>umask</command> to hide an output file
from prying eyes</title>
<programlisting>&rot13a;</programlisting>
</example>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -19746,6 +19840,19 @@ File_contents2=$(&lt;$file2) # Bash permits this also.</programlisting></
</screen> </screen>
</para> </para>
<para>Unlike backticks, the <command>$(...)</command> form of
command substitution permits nesting.</para>
<para><programlisting>word_count=$( wc -w $(ls -l | awk '{print $9}') )</programlisting>
</para>
<para>Or, for something a bit more elaborate . . .</para>
<example id="agram2">
<title>Finding anagrams</title>
<programlisting>&agram2;</programlisting>
</example>
</note> </note>
@ -20099,6 +20206,8 @@ exec 3>&- # Now close it for the remainder of the s
<sect1><title>Using <command>exec</command></title> <sect1><title>Using <command>exec</command></title>
<para><anchor id="usingexecref"></para>
<para>An <command>exec &lt;filename</command> command redirects <para>An <command>exec &lt;filename</command> command redirects
<filename>stdin</filename> to a file. From that point on, all <filename>stdin</filename> to a file. From that point on, all
<filename>stdin</filename> comes from that file, rather than <filename>stdin</filename> comes from that file, rather than
@ -21898,7 +22007,7 @@ f2 ()
f1 # Function "f2" is not actually called until this point, f1 # Function "f2" is not actually called until this point,
#+ although it is referenced before its definition. #+ although it is referenced before its definition.
# This is permissable. # This is permissible.
# Thanks, S.C.</programlisting> # Thanks, S.C.</programlisting>
</para> </para>
@ -21929,7 +22038,7 @@ f2 # Now, it's all right to call "f2",
<para>Function declarations can appear in unlikely places, even where a <para>Function declarations can appear in unlikely places, even where a
command would otherwise go. command would otherwise go.
<programlisting>ls -l | foo() { echo "foo"; } # Permissable, but useless. <programlisting>ls -l | foo() { echo "foo"; } # Permissible, but useless.
@ -22146,10 +22255,10 @@ echo "return value = $Return_Val" #25701</programlisting>
</para> </para>
<para>An even more elegant method is to simply have the <para>A more elegant method is to have the function
function <command>echo</command> its <quote>return <command>echo</command> its <quote>return
value to <filename>stdout</filename>,</quote> and to value to <filename>stdout</filename>,</quote> and
then capture it by <link linkend="commandsubref">command then capture it by <link linkend="commandsubref">command
substitution</link>. See the <link linkend="rvt">discussion substitution</link>. See the <link linkend="rvt">discussion
of this</link> in <xref linkend="assortedtips">.</para> of this</link> in <xref linkend="assortedtips">.</para>
@ -22158,6 +22267,32 @@ echo "return value = $Return_Val" #25701</programlisting>
<programlisting>&max2;</programlisting> <programlisting>&max2;</programlisting>
</example> </example>
<para>Here is another example of capturing a function
<quote>return value.</quote> Understanding it requires some
knowledge of <link linkend="awkref">awk</link>.
<programlisting>month_length () # Takes month number as an argument.
{ # Returns number of days in month.
monthD="31 28 31 30 31 30 31 31 30 31 30 31" # Declare as local?
echo "$monthD" | awk '{ print $'"${1}"' }' # Tricky.
# ^^^^^^^^^
# Parameter passed to function ($1 -- month number), then to awk.
# Awk sees this as "print $1 . . . print $12" (depending on month number)
# Template for passing a parameter to embedded awk script:
# $'"${script_parameter}"'
# Needs error checking for correct parameter range (1-12)
#+ and for February in leap year.
}
# ----------------------------------------------
# Usage example:
month=4 # April, for example (4th month).
days_in=$(month_length $month)
echo $days_in # 30
# ----------------------------------------------</programlisting></para>
<para>See also <xref linkend="daysbetween">.</para> <para>See also <xref linkend="daysbetween">.</para>
<para><userinput>Exercise:</userinput> Using what we have <para><userinput>Exercise:</userinput> Using what we have
@ -22194,7 +22329,7 @@ echo "return value = $Return_Val" #25701</programlisting>
<programlisting>&realname;</programlisting> <programlisting>&realname;</programlisting>
</example> </example>
<para>There is an alternative, and perhaps less confusing <para>There is an alternate, and perhaps less confusing
method of redirecting a function's method of redirecting a function's
<filename>stdin</filename>. This involves redirecting the <filename>stdin</filename>. This involves redirecting the
<filename>stdin</filename> to an embedded bracketed code <filename>stdin</filename> to an embedded bracketed code
@ -22870,7 +23005,7 @@ exit 0</programlisting></para>
<para>--</para> <para>--</para>
<para>Bash supports only one-dimensional arrays, however a little <para>Bash supports only one-dimensional arrays, though a little
trickery permits simulating multi-dimensional ones.</para> trickery permits simulating multi-dimensional ones.</para>
<example id="twodim"> <example id="twodim">
@ -22879,96 +23014,25 @@ exit 0</programlisting></para>
</example> </example>
<para>A two-dimensional array is essentially equivalent to a <para>A two-dimensional array is essentially equivalent to a
one-dimensional one, but with additional addressing modes for one-dimensional one, but with additional addressing modes
referencing and manipulating the individual elements by for referencing and manipulating the individual elements
<quote>row</quote> and <quote>column</quote> position.</para> by <emphasis>row</emphasis> and <emphasis>column</emphasis>
position.</para>
<para>For an even more elaborate example of simulating a <para>For an even more elaborate example of simulating a
two-dimensional array, see <xref linkend="lifeslow">.</para> two-dimensional array, see <xref linkend="lifeslow">.</para>
<para>--</para>
<para>For yet another interesting script using arrays, see:
<itemizedlist>
<listitem><para><xref linkend="agram2"></para></listitem>
</itemizedlist>
</para>
</chapter> <!-- Arrays --> </chapter> <!-- Arrays -->
<chapter id="files">
<title>Files</title>
<variablelist id="filesref">
<title><anchor id="filesref1">startup files</title>
<varlistentry>
<term></term>
<listitem>
<para>These files contain the aliases and <link
linkend="envref">environmental variables</link>
made available to Bash running as a user shell and to all
Bash scripts invoked after system initialization.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/profile</filename></term>
<listitem>
<para>systemwide defaults, mostly setting the environment
(all Bourne-type shells, not just Bash
<footnote><para>This does not apply to <command>csh</command>,
<command>tcsh</command>, and other shells not related to or
descended from the classic Bourne shell
(<command>sh</command>).</para></footnote>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/bashrc</filename></term>
<listitem>
<para>systemwide functions and <link
linkend="aliasref">aliases</link> for Bash</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_profile</filename></term>
<listitem>
<para>user-specific Bash environmental default settings,
found in each user's home directory (the local counterpart
to <filename>/etc/profile</filename>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bashrc</filename></term>
<listitem>
<para>user-specific Bash init file, found in each user's home
directory (the local counterpart to
<filename>/etc/bashrc</filename>). Only interactive
shells and user scripts read this file. See
<xref linkend="sample-bashrc"> for a sample
<filename>.bashrc</filename> file.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="logoutfileref">
<title><anchor id="logoutfileref1">logout file</title>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_logout</filename></term>
<listitem>
<para>user-specific instruction file, found in
each user's home directory. Upon exit from a login (Bash)
shell, the commands in this file execute.</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Files -->
<chapter id="devproc"> <chapter id="devproc">
<title>/dev and /proc</title> <title>/dev and /proc</title>
@ -23260,7 +23324,7 @@ fi</programlisting></para>
<term>Uses of <filename>/dev/null</filename></term> <term>Uses of <filename>/dev/null</filename></term>
<listitem> <listitem>
<para>Think of <filename>/dev/null</filename> as a <quote>black <para>Think of <filename>/dev/null</filename> as a <quote>black
hole</quote>. It is the nearest equivalent to a hole.</quote> It is the nearest equivalent to a
write-only file. Everything written to it disappears write-only file. Everything written to it disappears
forever. Attempts to read or output from it result in forever. Attempts to read or output from it result in
nothing. Nevertheless, <filename>/dev/null</filename> nothing. Nevertheless, <filename>/dev/null</filename>
@ -23302,7 +23366,7 @@ cat /dev/null > /var/log/wtmp</programlisting>
<para>Automatically emptying the contents of a logfile <para>Automatically emptying the contents of a logfile
(especially good for dealing with those nasty (especially good for dealing with those nasty
<quote>cookies</quote> sent by Web commercial sites):</para> <quote>cookies</quote> sent by commercial Web sites):</para>
<example id="cookies"> <example id="cookies">
<title>Hiding the cookie jar</title> <title>Hiding the cookie jar</title>
@ -24367,8 +24431,8 @@ while [ "$index" -le "$MAXVAL" ]
... ...
E_NOTFOUND=75 # Uppercase for an errorcode, E_NOTFOUND=75 # Uppercase for an errorcode,
# and name begins with "E_". # +and name begins with "E_".
if [ ! -e "$filename" ] if [ ! -e "$filename" ]
then then
echo "File $filename not found." echo "File $filename not found."
@ -24393,7 +24457,7 @@ favorite_number=$?
echo $favorite_number echo $favorite_number
_uservariable=23 # Permissable, but not recommended. _uservariable=23 # Permissible, but not recommended.
# It's better for user-defined variables not to start with an underscore. # It's better for user-defined variables not to start with an underscore.
# Leave that for system variables.</programlisting> # Leave that for system variables.</programlisting>
</para> </para>
@ -24652,8 +24716,13 @@ fi</programlisting>
<programlisting>&loggingwrapper;</programlisting> <programlisting>&loggingwrapper;</programlisting>
</example> </example>
<example id="coltotaler"> <example id="prasc">
<title> A <command>shell wrapper</command> around an awk script</title> <title> A <command>shell wrapper</command> around an awk script</title>
<programlisting>&prasc;</programlisting>
</example>
<example id="coltotaler">
<title> A <command>shell wrapper</command> around another awk script</title>
<programlisting>&coltotaler;</programlisting> <programlisting>&coltotaler;</programlisting>
</example> </example>
@ -25319,8 +25388,8 @@ exit 0</programlisting>
} }
newstring=`capitalize_ichar "each sentence should start with a capital letter."` newstring=`capitalize_ichar "every sentence should start with a capital letter."`
echo "$newstring" # Each sentence should start with a capital letter.</programlisting> echo "$newstring" # Every sentence should start with a capital letter.</programlisting>
</para> </para>
<para>It is even possible for a function to <quote>return</quote> <para>It is even possible for a function to <quote>return</quote>
@ -25375,7 +25444,7 @@ echo "$newstring" # Each sentence should start with a capital letter.</
<varname>$PATH</varname> and <command>umask</command>. <varname>$PATH</varname> and <command>umask</command>.
<programlisting>#!/bin/bash <programlisting>#!/bin/bash
PATH=/bin:/usr/bin:/usr/local/bin ; export PATH PATH=/bin:/usr/bin:/usr/local/bin ; export PATH
umask 022 umask 022 # Files that the script creates will have 755 permission.
# Thanks to Ian D. Allen, for this tip.</programlisting></para> # Thanks to Ian D. Allen, for this tip.</programlisting></para>
</listitem> </listitem>
@ -25814,6 +25883,9 @@ else
# Or, ask for corrected input. # Or, ask for corrected input.
fi</programlisting></para> fi</programlisting></para>
<para>For another example of using the <command>=~</command>
operator, see <xref linkend="whx">.</para>
</listitem> </listitem>
@ -26108,14 +26180,14 @@ fi</programlisting></para>
Chris Martin, Lee Maschmeyer, Bruno Haible, Wilbert Berendsen, Chris Martin, Lee Maschmeyer, Bruno Haible, Wilbert Berendsen,
Sebastien Godard, Bj&ouml;n Eriksson, John MacDonald, Joshua Sebastien Godard, Bj&ouml;n Eriksson, John MacDonald, Joshua
Tschida, Troy Engel, Manfred Schwarb, Amit Singh, Bill Gradwohl, Tschida, Troy Engel, Manfred Schwarb, Amit Singh, Bill Gradwohl,
David Lombard, Jason Parker, Steve Parker, Bruce W. Clare, David Lombard, Jason Parker, Steve Parker, Bruce W. Clare, William
William Park, Vernia Damiano, Mihai Maties, Jeremy Impson, Park, Vernia Damiano, Mihai Maties, Jeremy Impson, Ken Fuchs,
Ken Fuchs, Frank Wang, Sylvain Fourmanoit, Matthew Walker, Frank Wang, Sylvain Fourmanoit, Matthew Walker, Kenny Stauffer,
Kenny Stauffer, Filip Moritz, Andrzej Stefanski, Daniel Albers, Filip Moritz, Andrzej Stefanski, Daniel Albers, Stefano Palmeri,
Stefano Palmeri, Nils Radtke, Jeroen Domburg, Alfredo Pironti, Nils Radtke, Jeroen Domburg, Alfredo Pironti, Phil Braham,
Phil Braham, Bruno de Oliveira Schneider, Stefano Falsetto, Bruno de Oliveira Schneider, Stefano Falsetto, Chris Morgan,
Chris Morgan, Linc Fessenden, Mariusz Gniazdowski, and David Walter Dnes, Linc Fessenden, Fabian Kreutz, Mariusz Gniazdowski,
Lawyer (himself an author of four HOWTOs).</para> and David Lawyer (himself an author of four HOWTOs).</para>
<para>My gratitude to <ulink url="mailto:chet@po.cwru.edu">Chet <para>My gratitude to <ulink url="mailto:chet@po.cwru.edu">Chet
Ramey</ulink> and Brian Fox for writing <firstterm>Bash</firstterm>, Ramey</ulink> and Brian Fox for writing <firstterm>Bash</firstterm>,
@ -27128,6 +27200,15 @@ fi</programlisting></para>
<programlisting>&isspammer2;</programlisting> <programlisting>&isspammer2;</programlisting>
</example> </example>
<para>Another anti-spam script.</para>
<example id="whx">
<title>Spammer Hunt</title>
<programlisting>&whx;</programlisting>
</example>
<para><quote>Little Monster's</quote> front end to <link <para><quote>Little Monster's</quote> front end to <link
linkend="wgetref">wget</link>.</para> linkend="wgetref">wget</link>.</para>
@ -28089,6 +28170,7 @@ pattern=BEGIN
<listitem><para><xref linkend="lifeslow"></para></listitem> <listitem><para><xref linkend="lifeslow"></para></listitem>
<listitem><para><xref linkend="selfdocument"></para></listitem> <listitem><para><xref linkend="selfdocument"></para></listitem>
<listitem><para><xref linkend="dictlookup"></para></listitem> <listitem><para><xref linkend="dictlookup"></para></listitem>
<listitem><para><xref linkend="whx"></para></listitem>
</orderedlist> </orderedlist>
</para> </para>
@ -28111,7 +28193,7 @@ pattern=BEGIN
for shell scripting.</para> for shell scripting.</para>
<para>Awk breaks each line of input passed to it into <para>Awk breaks each line of input passed to it into
<emphasis>fields</emphasis>. By default, a field is <firstterm>fields</firstterm>. By default, a field is
a string of consecutive characters separated by <link a string of consecutive characters separated by <link
linkend="whitespaceref">whitespace</link>, though there are linkend="whitespaceref">whitespace</link>, though there are
options for changing the delimiter. Awk parses and operates on options for changing the delimiter. Awk parses and operates on
@ -28122,7 +28204,14 @@ pattern=BEGIN
<para>Strong quoting (single quotes) and curly brackets enclose <para>Strong quoting (single quotes) and curly brackets enclose
segments of awk code within a shell script.</para> segments of awk code within a shell script.</para>
<para><programlisting>awk '{print $3}' $filename <para><programlisting>echo one two | awk '{print $1}'
# one
echo one two | awk '{print $2}'
# two
awk '{print $3}' $filename
# Prints field #3 of file $filename to stdout. # Prints field #3 of file $filename to stdout.
awk '{print $1 $5 $6}' $filename awk '{print $1 $5 $6}' $filename
@ -28169,6 +28258,7 @@ awk '{print $1 $5 $6}' $filename
<listitem><para><xref linkend="substringex"></para></listitem> <listitem><para><xref linkend="substringex"></para></listitem>
<listitem><para><xref linkend="sumproduct"></para></listitem> <listitem><para><xref linkend="sumproduct"></para></listitem>
<listitem><para><xref linkend="userlist"></para></listitem> <listitem><para><xref linkend="userlist"></para></listitem>
<listitem><para><xref linkend="prasc"></para></listitem>
</orderedlist> </orderedlist>
</para> </para>
@ -28204,79 +28294,82 @@ awk '{print $1 $5 $6}' $filename
<tbody> <tbody>
<row> <row>
<entry><option>1</option></entry> <entry><option>1</option></entry>
<entry>catchall for general errors</entry> <entry>Catchall for general errors</entry>
<entry>let "var1 = 1/0"</entry> <entry>let "var1 = 1/0"</entry>
<entry>miscellaneous errors, such as <quote>divide by <entry>Miscellaneous errors, such as <quote>divide by
zero</quote></entry> zero</quote></entry>
</row> </row>
<row> <row>
<entry><option>2</option></entry> <entry><option>2</option></entry>
<entry>misuse of shell builtins, according to Bash documentation</entry> <entry>Misuse of shell builtins (according to Bash documentation)</entry>
<entry></entry> <entry></entry>
<entry>Seldom seen, usually defaults to exit code 1</entry> <entry>Seldom seen, usually defaults to exit code
<errorcode>1</errorcode></entry>
</row> </row>
<row> <row>
<entry><option>126</option></entry> <entry><option>126</option></entry>
<entry>command invoked cannot execute</entry> <entry>Command invoked cannot execute</entry>
<entry></entry> <entry></entry>
<entry>permission problem or command is not an executable</entry> <entry>Permission problem or command is not an executable</entry>
</row> </row>
<row> <row>
<entry><option>127</option></entry> <entry><option>127</option></entry>
<entry><quote>command not found</quote></entry> <entry><quote>command not found</quote></entry>
<entry></entry> <entry></entry>
<entry>possible problem with <varname>$PATH</varname> or a typo</entry> <entry>Possible problem with <varname>$PATH</varname> or a typo</entry>
</row> </row>
<row> <row>
<entry><option>128</option></entry> <entry><option>128</option></entry>
<entry>invalid argument to <link linkend="exitcommandref">exit</link></entry> <entry>Invalid argument to <link linkend="exitcommandref">exit</link></entry>
<entry>exit 3.14159</entry> <entry>exit 3.14159</entry>
<entry><command>exit</command> takes only integer args in the <entry><command>exit</command> takes only integer args in the
range 0 - 255 (see footnote)</entry> range <returnvalue>0 - 255</returnvalue> (see
footnote)</entry>
</row> </row>
<row> <row>
<entry><option>128+n</option></entry> <entry><option>128+n</option></entry>
<entry>fatal error signal <quote>n</quote></entry> <entry>Fatal error signal <quote>n</quote></entry>
<entry><command>kill -9</command> <varname>$PPID</varname> of script</entry> <entry><command>kill -9</command> <varname>$PPID</varname> of script</entry>
<entry><userinput>$?</userinput> returns 137 (128 + 9)</entry> <entry><userinput>$?</userinput> returns
<errorcode>137</errorcode> (128 + 9)</entry>
</row> </row>
<row> <row>
<entry><option>130</option></entry> <entry><option>130</option></entry>
<entry>script terminated by Control-C</entry> <entry>Script terminated by Control-C</entry>
<entry></entry> <entry></entry>
<entry>Control-C is fatal error signal 2, (130 = 128 + 2, <entry>Control-C is fatal error signal
see above)</entry> <errorcode>2</errorcode>, (130 = 128 + 2, see above)</entry>
</row> </row>
<row> <row>
<entry><option>255*</option></entry> <entry><option>255*</option></entry>
<entry>exit status out of range</entry> <entry>Exit status out of range</entry>
<entry>exit -1</entry> <entry>exit <returnvalue>-1</returnvalue></entry>
<entry><command>exit</command> takes only integer args in the <entry><command>exit</command> takes only integer args in the
range 0 - 255</entry> range <errorcode>0 - 255</errorcode></entry>
</row> </row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
<para>According to the table, exit codes <returnvalue>1 - 2, 126 - <para>According to the above table, exit codes <errorcode>1 - 2,
165, and 255</returnvalue> 126 - 165, and 255</errorcode>
<footnote><para>Out of range exit values can result in <footnote><para>Out of range exit values can result in
unexpected exit codes. An exit value greater than unexpected exit codes. An exit value greater
<returnvalue>255</returnvalue> returns an exit code modulo than <errorcode>255</errorcode> returns an exit
256. For example, <command>exit 3809</command> gives an code <link linkend="moduloref">modulo</link>
exit code of <errorcode>225</errorcode> (3809 % 256 = <errorcode>256</errorcode>. For example, <command>exit
225).</para></footnote> 3809</command> gives an exit code of <errorcode>225</errorcode>
(3809 % 256 = 225).</para></footnote>
have special meanings, and should therefore be avoided as have special meanings, and should therefore be avoided for
user-specified exit parameters. Ending a script with <command>exit user-specified exit parameters. Ending a script with <command>exit
127</command> would certainly cause confusion when troubleshooting 127</command> would certainly cause confusion when troubleshooting
(is the error a <quote>command not found</quote> or a (is the error code a <quote>command not found</quote> or a
user-defined one?). However, many scripts use an <command>exit user-defined one?). However, many scripts use an <command>exit
1</command> as a general bailout upon error. Since exit code 1</command> as a general bailout upon error. Since exit code
<returnvalue>1</returnvalue> signifies so many possible errors, <errorcode>1</errorcode> signifies so many possible errors,
this might not add any additional ambiguity, but, on the other this probably would not be helpful in debugging.</para>
hand, it probably would not be very informative either.</para>
<para>There has been an attempt to systematize exit status numbers <para>There has been an attempt to systematize exit status numbers
(see <filename (see <filename
@ -28565,6 +28658,88 @@ exit 0</programlisting>
<!-- End Standard Command-Line Options appendix --> <!-- End Standard Command-Line Options appendix -->
<appendix id="files">
<title> Important Files</title>
<variablelist id="filesref">
<title><anchor id="filesref1">startup files</title>
<varlistentry>
<term></term>
<listitem>
<para>These files contain the aliases and <link
linkend="envref">environmental variables</link>
made available to Bash running as a user shell and to all
Bash scripts invoked after system initialization.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/profile</filename></term>
<listitem>
<para>systemwide defaults, mostly setting the environment
(all Bourne-type shells, not just Bash
<footnote><para>This does not apply to <command>csh</command>,
<command>tcsh</command>, and other shells not related to or
descended from the classic Bourne shell
(<command>sh</command>).</para></footnote>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/bashrc</filename></term>
<listitem>
<para>systemwide functions and <link
linkend="aliasref">aliases</link> for Bash</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_profile</filename></term>
<listitem>
<para>user-specific Bash environmental default settings,
found in each user's home directory (the local counterpart
to <filename>/etc/profile</filename>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bashrc</filename></term>
<listitem>
<para>user-specific Bash init file, found in each user's home
directory (the local counterpart to
<filename>/etc/bashrc</filename>). Only interactive
shells and user scripts read this file. See
<xref linkend="sample-bashrc"> for a sample
<filename>.bashrc</filename> file.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="logoutfileref">
<title><anchor id="logoutfileref1">logout file</title>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_logout</filename></term>
<listitem>
<para>user-specific instruction file, found in
each user's home directory. Upon exit from a login (Bash)
shell, the commands in this file execute.</para>
</listitem>
</varlistentry>
</variablelist>
</appendix>
<!-- End Files appendix -->
<appendix id="systemdirs"> <appendix id="systemdirs">
<title>Important System Directories</title> <title>Important System Directories</title>
@ -30571,6 +30746,11 @@ fairly detailed rundown on the Playfair Cipher and its solution methods.</progra
<entry>08 May 2005</entry> <entry>08 May 2005</entry>
<entry>TEABERRY release: Bugfixes, stylistic revisions.</entry> <entry>TEABERRY release: Bugfixes, stylistic revisions.</entry>
</row> </row>
<row>
<entry><option>3.5</option></entry>
<entry>05 Jun 2005</entry>
<entry>BOXBERRY release: Bugfixes, some materials added.</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
@ -30597,7 +30777,7 @@ fairly detailed rundown on the Playfair Cipher and its solution methods.</progra
also mirrors the <emphasis>ABS Guide</emphasis>.</para> also mirrors the <emphasis>ABS Guide</emphasis>.</para>
<para>Yet another mirror site for this document is <para>Yet another mirror site for this document is
<ulink url="ftp://ftp.morethan.org">the morethan.org ftp site</ulink>.</para> <ulink url="http://www.morethan.org">morethan.org</ulink>.</para>
</appendix> <!-- Mirror Sites appendix --> </appendix> <!-- Mirror Sites appendix -->

View File

@ -3,13 +3,15 @@
# Find anagrams of... # Find anagrams of...
LETTERSET=etaoinshrdlu LETTERSET=etaoinshrdlu
FILTER='.......' # How many letters minimum?
# 1234567
anagram "$LETTERSET" | # Find all anagrams of the letterset... anagram "$LETTERSET" | # Find all anagrams of the letterset...
grep '.......' | # With at least 7 letters, grep "$FILTER" | # With at least 7 letters,
grep '^is' | # starting with 'is' grep '^is' | # starting with 'is'
grep -v 's$' | # no plurals grep -v 's$' | # no plurals
grep -v 'ed$' # no past tense verbs grep -v 'ed$' # no past tense verbs
# Possible to add many combinations of conditions. # Possible to add many combinations of conditions and filters.
# Uses "anagram" utility # Uses "anagram" utility
#+ that is part of the author's "yawl" word list package. #+ that is part of the author's "yawl" word list package.
@ -18,8 +20,20 @@ grep -v 'ed$' # no past tense verbs
exit 0 # End of code. exit 0 # End of code.
bash$ sh agram.sh bash$ sh agram.sh
islander islander
isolate isolate
isolead isolead
isotheral isotheral
# Exercises:
# ---------
# Modify this script to take the LETTERSET as a command-line parameter.
# Parameterize the filters in lines 11 - 13 (as with $FILTER),
#+ so that they can be specified by passing arguments to a function.
# For a slightly different approach to anagramming,
#+ see the agram2.sh script.

View File

@ -4,8 +4,8 @@ ARGS=1 # Number of arguments expected.
E_BADARGS=65 # Exit value if incorrect number of args passed. E_BADARGS=65 # Exit value if incorrect number of args passed.
test $# -ne $ARGS && echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS test $# -ne $ARGS && echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS
# If condition-1 true (wrong number of args passed to script), # If condition 1 tests true (wrong number of args passed to script),
# then the rest of the line executes, and script terminates. #+ then the rest of the line executes, and script terminates.
# Line below executes only if the above test fails. # Line below executes only if the above test fails.
echo "Correct number of arguments passed to this script." echo "Correct number of arguments passed to this script."

View File

@ -14,7 +14,7 @@ var1=2367
echo "var1 declared as $var1" echo "var1 declared as $var1"
var1=var1+1 # Integer declaration eliminates the need for 'let'. var1=var1+1 # Integer declaration eliminates the need for 'let'.
echo "var1 incremented by 1 is $var1." echo "var1 incremented by 1 is $var1."
# Attempt to change variable declared as integer # Attempt to change variable declared as integer.
echo "Attempting to change var1 to floating point value, 2367.1." echo "Attempting to change var1 to floating point value, 2367.1."
var1=2367.1 # Results in error message, with no change to variable. var1=2367.1 # Results in error message, with no change to variable.
echo "var1 is still $var1" echo "var1 is still $var1"

View File

@ -31,8 +31,8 @@ echo "Random number less than $RANGE --- $number"
echo echo
# If you need a random int greater than a lower bound, # If you need a random integer greater than a lower bound,
# then set up a test to discard all numbers below that. #+ then set up a test to discard all numbers below that.
FLOOR=200 FLOOR=200
@ -56,6 +56,7 @@ echo "Random number between $FLOOR and $RANGE --- $number"
echo echo
# Generate binary choice, that is, "true" or "false" value. # Generate binary choice, that is, "true" or "false" value.
BINARY=2 BINARY=2
T=1 T=1
@ -74,12 +75,13 @@ fi
echo echo
# Generate toss of the dice. # Generate a toss of the dice.
SPOTS=6 # Modulo 6 gives range 0 - 5. SPOTS=6 # Modulo 6 gives range 0 - 5.
# Incrementing by 1 gives desired range of 1 - 6. # Incrementing by 1 gives desired range of 1 - 6.
# Thanks, Paulo Marcel Coelho Aragao, for the simplification. # Thanks, Paulo Marcel Coelho Aragao, for the simplification.
die1=0 die1=0
die2=0 die2=0
# Would it be better to just set SPOTS=7 and not add 1? Why or why not?
# Tosses each die separately, and so gives correct odds. # Tosses each die separately, and so gives correct odds.
@ -88,6 +90,7 @@ die2=0
# Which arithmetic operation, above, has greater precedence -- # Which arithmetic operation, above, has greater precedence --
#+ modulo (%) or addition (+)? #+ modulo (%) or addition (+)?
let "throw = $die1 + $die2" let "throw = $die1 + $die2"
echo "Throw of the dice = $throw" echo "Throw of the dice = $throw"
echo echo

View File

@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
echo echo
# Equivalent to:
while [ "$var1" != "end" ] # while test "$var1" != "end" while [ "$var1" != "end" ] # while test "$var1" != "end"
do # also works. do
echo "Input variable #1 (end to exit) " echo "Input variable #1 (end to exit) "
read var1 # Not 'read $var1' (why?). read var1 # Not 'read $var1' (why?).
echo "variable #1 = $var1" # Need quotes because of "#". echo "variable #1 = $var1" # Need quotes because of "#" . . .
# If input is 'end', echoes it here. # If input is 'end', echoes it here.
# Does not test for termination condition until top of loop. # Does not test for termination condition until top of loop.
echo echo

View File

@ -16,3 +16,7 @@ tr a-z A-Z <"$1"
# Thanks, S.C. # Thanks, S.C.
exit 0 exit 0
# Exercise:
# Rewrite this script to give the option of changing a file
#+ to *either* upper or lowercase.

View File

@ -6,14 +6,14 @@
# Try it with one two-word quoted command line parameter, # Try it with one two-word quoted command line parameter,
# ./scriptname "Mortimer Jones" # ./scriptname "Mortimer Jones"
CMDLINEPARAM=1 # Expect at least command line parameter. CMDLINEPARAM=1 # Expect at least command line parameter.
if [ $# -ge $CMDLINEPARAM ] if [ $# -ge $CMDLINEPARAM ]
then then
NAME=$1 # If more than one command line param, NAME=$1 # If more than one command line param,
# then just take the first. #+ then just take the first.
else else
NAME="John Doe" # Default, if no command line parameter. NAME="John Doe" # Default, if no command line parameter.
fi fi
RESPONDENT="the author of this fine script" RESPONDENT="the author of this fine script"

View File

@ -15,3 +15,6 @@ echo "Second line in $File is:"
echo "$line2" echo "$line2"
exit 0 exit 0
# Now, how do you parse the separate fields of each line?
# Hint: use awk.

View File

@ -2,6 +2,7 @@
# $Id$ # $Id$
# Script to perform batch anonymous ftp. Essentially converts a list of # Script to perform batch anonymous ftp. Essentially converts a list of
# of command line arguments into input to ftp. # of command line arguments into input to ftp.
# ==> This script is nothing but a shell wrapper around "ftp" . . .
# Simple, and quick - written as a companion to ftplist # Simple, and quick - written as a companion to ftplist
# -h specifies the remote host (default prep.ai.mit.edu) # -h specifies the remote host (default prep.ai.mit.edu)
# -d specifies the remote directory to cd to - you can provide a sequence # -d specifies the remote directory to cd to - you can provide a sequence
@ -24,8 +25,7 @@
# Obviously, the sequence of the options is important, since the equivalent # Obviously, the sequence of the options is important, since the equivalent
# commands are executed by ftp in corresponding order # commands are executed by ftp in corresponding order
# #
# Mark Moraes (moraes@csri.toronto.edu), Feb 1, 1989 # Mark Moraes &lt;moraes@csri.toronto.edu&gt;, Feb 1, 1989
# ==> Angle brackets changed to parens, so Docbook won't get indigestion.
# #
@ -35,6 +35,8 @@
# export PATH # export PATH
# ==> Above 2 lines from original script probably superfluous. # ==> Above 2 lines from original script probably superfluous.
E_BADARGS=65
TMPFILE=/tmp/ftp.$$ TMPFILE=/tmp/ftp.$$
# ==> Creates temp file, using process id of script ($$) # ==> Creates temp file, using process id of script ($$)
# ==> to construct filename. # ==> to construct filename.
@ -51,10 +53,11 @@ set -f # So we can use globbing in -m
set x `getopt vh:d:c:m:f: $*` set x `getopt vh:d:c:m:f: $*`
if [ $? != 0 ]; then if [ $? != 0 ]; then
echo $usage echo $usage
exit 65 exit $E_BADARGS
fi fi
shift shift
trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15 trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15
# ==> Delete tempfile in case of abnormal exit from script.
echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}" echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}"
# ==> Added quotes (recommended in complex echoes). # ==> Added quotes (recommended in complex echoes).
echo binary >> ${TMPFILE} echo binary >> ${TMPFILE}
@ -74,17 +77,19 @@ do
echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;; echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;;
--) shift; break;; --) shift; break;;
esac esac
# ==> 'lcd' and 'mget' are ftp commands. See "man ftp" . . .
done done
if [ $# -ne 0 ]; then if [ $# -ne 0 ]; then
echo $usage echo $usage
exit 65 # ==> Changed from "exit 2" to conform with standard. exit $E_BADARGS
# ==> Changed from "exit 2" to conform with style standard.
fi fi
if [ x${verbflag} != x ]; then if [ x${verbflag} != x ]; then
ftpflags="${ftpflags} -v" ftpflags="${ftpflags} -v"
fi fi
if [ x${remhost} = x ]; then if [ x${remhost} = x ]; then
remhost=prep.ai.mit.edu remhost=prep.ai.mit.edu
# ==> Rewrite to match your favorite ftp site. # ==> Change to match appropriate ftp site.
fi fi
echo quit >> ${TMPFILE} echo quit >> ${TMPFILE}
# ==> All commands saved in tempfile. # ==> All commands saved in tempfile.

View File

@ -12,7 +12,9 @@
# Slightly modified and commented by ABS author. # Slightly modified and commented by ABS author.
#=================================================================# #=================================================================#
# The Tower of Hanoi is an old mathematical puzzle. # The Tower of Hanoi is a mathematical puzzle attributed to
#+ Edouard Lucas, a nineteenth-century French mathematician.
#
# There are three vertical posts set in a base. # There are three vertical posts set in a base.
# The first post has a set of annular rings stacked on it. # The first post has a set of annular rings stacked on it.
# These rings are flat disks with a hole drilled out of the center, # These rings are flat disks with a hole drilled out of the center,

View File

@ -38,23 +38,23 @@ No_WSP=$'\x0A'$'\x0D'
# Field separator for dotted decimal ip addresses # Field separator for dotted decimal ip addresses
ADR_IFS=${No_WSP}'.' ADR_IFS=${No_WSP}'.'
# Get the dns text resource record # Get the dns text resource record.
# get_txt &lt;error_code&gt; &lt;list_query&gt; # get_txt &lt;error_code&gt; &lt;list_query&gt;
get_txt() { get_txt() {
# parse $1 by assignment at the dots # Parse $1 by assignment at the dots.
local -a dns local -a dns
IFS=$ADR_IFS IFS=$ADR_IFS
dns=( $1 ) dns=( $1 )
IFS=$WSP_IFS IFS=$WSP_IFS
if [ "${dns[0]}" == '127' ] if [ "${dns[0]}" == '127' ]
then then
# see if there is a reason # See if there is a reason.
echo $(dig +short $2 -t txt) echo $(dig +short $2 -t txt)
fi fi
} }
# Get the dns address resource record # Get the dns address resource record.
# chk_adr &lt;rev_dns&gt; &lt;list_server&gt; # chk_adr &lt;rev_dns&gt; &lt;list_server&gt;
chk_adr() { chk_adr() {
local reply local reply
@ -64,7 +64,7 @@ chk_adr() {
server=${1}${2} server=${1}${2}
reply=$( dig +short ${server} ) reply=$( dig +short ${server} )
# if reply might be an error code . . . # If reply might be an error code . . .
if [ ${#reply} -gt 6 ] if [ ${#reply} -gt 6 ]
then then
reason=$(get_txt ${reply} ${server} ) reason=$(get_txt ${reply} ${server} )

View File

@ -3,7 +3,7 @@
# $Id$ # $Id$
# Above line is RCS info. # Above line is RCS info.
# The latest version of this script is available from ftp://ftp.morethan.org. # The latest version of this script is available from http://www.morethan.org.
# #
# Spammer-identification # Spammer-identification
# by Michael S. Zick # by Michael S. Zick
@ -172,7 +172,7 @@ pend_dummy() { : ; }
pend_init() { pend_init() {
unset _pending_[@] unset _pending_[@]
pend_func pend_stop_mark pend_func pend_stop_mark
_pend_hook_='pend_dummy' # Debug only _pend_hook_='pend_dummy' # Debug only.
} }
# Discard the top function on the stack. # Discard the top function on the stack.
@ -211,7 +211,7 @@ pend_release() {
_top_=${#_pending_[@]}-1 _top_=${#_pending_[@]}-1
_pend_current_=${_pending_[$_top_]} _pend_current_=${_pending_[$_top_]}
unset _pending_[$_top_] unset _pending_[$_top_]
$_pend_hook_ # Debug only $_pend_hook_ # Debug only.
eval $_pend_current_ eval $_pend_current_
done done
} }
@ -969,7 +969,7 @@ name_fixup(){
IFS=${WSP_IFS} IFS=${WSP_IFS}
_nf_end=${#_nf_tmp[@]} _nf_end=${#_nf_tmp[@]}
case ${_nf_end} in case ${_nf_end} in
0) # No dots, only dots 0) # No dots, only dots.
echo echo
return 1 return 1
;; ;;

View File

@ -1,10 +1,10 @@
#! /bin/bash #!/bin/bash
# #
# Changes every filename in working directory to all lowercase. # Changes every filename in working directory to all lowercase.
# #
# Inspired by a script of John Dubois, # Inspired by a script of John Dubois,
# which was translated into into Bash by Chet Ramey, #+ which was translated into Bash by Chet Ramey,
# and considerably simplified by Mendel Cooper, author of this document. #+ and considerably simplified by the author of the ABS Guide.
for filename in * # Traverse all files in directory. for filename in * # Traverse all files in directory.
@ -17,7 +17,7 @@ do
fi fi
done done
exit 0 exit $?
# Code below this line will not execute because of "exit". # Code below this line will not execute because of "exit".
@ -25,7 +25,6 @@ exit 0
# To run it, delete script above line. # To run it, delete script above line.
# The above script will not work on filenames containing blanks or newlines. # The above script will not work on filenames containing blanks or newlines.
# Stephane Chazelas therefore suggests the following alternative: # Stephane Chazelas therefore suggests the following alternative:
@ -41,4 +40,4 @@ do n=`echo "$filename/" | tr '[:upper:]' '[:lower:]'`
# Checks if filename already lowercase. # Checks if filename already lowercase.
done done
exit 0 exit $?

View File

@ -34,9 +34,11 @@ echo $retval # Echoes (to stdout), rather than returning value.
return_val=$(max2 33001 33997) return_val=$(max2 33001 33997)
# ^^^^ Function name
# ^^^^^ ^^^^^ Params passed
# This is actually a form of command substitution: # This is actually a form of command substitution:
#+ treating a function as if it were a command, #+ treating a function as if it were a command,
#+ and assigning the stdout of the function to the variable 'return_val.' #+ and assigning the stdout of the function to the variable "return_val."
# ========================= OUTPUT ======================== # ========================= OUTPUT ========================

View File

@ -1,6 +1,8 @@
#!/bin/bash #!/bin/bash
# logon.sh: A quick 'n dirty script to check whether you are on-line yet. # logon.sh: A quick 'n dirty script to check whether you are on-line yet.
umask 177 # Make sure temp files are not world readable.
TRUE=1 TRUE=1
LOGFILE=/var/log/messages LOGFILE=/var/log/messages
@ -8,6 +10,9 @@ LOGFILE=/var/log/messages
#+ (as root, chmod 644 /var/log/messages). #+ (as root, chmod 644 /var/log/messages).
TEMPFILE=temp.$$ TEMPFILE=temp.$$
# Create a "unique" temp file name, using process id of the script. # Create a "unique" temp file name, using process id of the script.
# Using 'mktemp' is an alternative.
# For example:
# TEMPFILE=`mktemp temp.XXXXXX`
KEYWORD=address KEYWORD=address
# At logon, the line "remote IP address xxx.xxx.xxx.xxx" # At logon, the line "remote IP address xxx.xxx.xxx.xxx"
# appended to /var/log/messages. # appended to /var/log/messages.

View File

@ -2,7 +2,7 @@
# pb.sh: phone book # pb.sh: phone book
# Written by Rick Boivie, and used with permission. # Written by Rick Boivie, and used with permission.
# Modifications by document author. # Modifications by ABS Guide author.
MINARGS=1 # Script needs at least one argument. MINARGS=1 # Script needs at least one argument.
DATAFILE=./phonebook DATAFILE=./phonebook

View File

@ -9,12 +9,12 @@
# The first 20 terms of the series are: # The first 20 terms of the series are:
# 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12 # 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12
# See Hofstadter's book, "Goedel, Escher, Bach: An Eternal Golden Braid", # See Hofstadter's book, "Goedel, Escher, Bach: An Eternal Golden Braid",
# p. 137, ff. #+ p. 137, ff.
LIMIT=100 # Number of terms to calculate LIMIT=100 # Number of terms to calculate.
LINEWIDTH=20 # Number of terms printed per line LINEWIDTH=20 # Number of terms printed per line.
Q[1]=1 # First two terms of series are 1. Q[1]=1 # First two terms of series are 1.
Q[2]=1 Q[2]=1
@ -26,8 +26,8 @@ echo -n "${Q[2]} "
for ((n=3; n <= $LIMIT; n++)) # C-like loop conditions. for ((n=3; n <= $LIMIT; n++)) # C-like loop conditions.
do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n&gt;2 do # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n&gt;2
# Need to break the expression into intermediate terms, # Need to break the expression into intermediate terms,
# since Bash doesn't handle complex array arithmetic very well. #+ since Bash doesn't handle complex array arithmetic very well.
let "n1 = $n - 1" # n-1 let "n1 = $n - 1" # n-1
let "n2 = $n - 2" # n-2 let "n2 = $n - 2" # n-2
@ -42,7 +42,7 @@ Q[n]=`expr $T0 + $T1` # Q[n - Q[n-1]] + Q[n - Q[n-2]]
echo -n "${Q[n]} " echo -n "${Q[n]} "
if [ `expr $n % $LINEWIDTH` -eq 0 ] # Format output. if [ `expr $n % $LINEWIDTH` -eq 0 ] # Format output.
then # mod then # ^ Modula operator
echo # Break lines into neat chunks. echo # Break lines into neat chunks.
fi fi
@ -54,4 +54,4 @@ exit 0
# This is an iterative implementation of the Q-series. # This is an iterative implementation of the Q-series.
# The more intuitive recursive implementation is left as an exercise. # The more intuitive recursive implementation is left as an exercise.
# Warning: calculating this series recursively takes a *very* long time. # Warning: calculating this series recursively takes a VERY long time.

View File

@ -4,15 +4,15 @@ echo
echo "Enter a string terminated by a \\, then press &lt;ENTER&gt;." echo "Enter a string terminated by a \\, then press &lt;ENTER&gt;."
echo "Then, enter a second string, and again press &lt;ENTER&gt;." echo "Then, enter a second string, and again press &lt;ENTER&gt;."
read var1 # The "\" suppresses the newline, when reading "var1". read var1 # The "\" suppresses the newline, when reading $var1.
# first line \ # first line \
# second line # second line
echo "var1 = $var1" echo "var1 = $var1"
# var1 = first line second line # var1 = first line second line
# For each line terminated by a "\", # For each line terminated by a "\"
# you get a prompt on the next line to continue feeding characters into var1. #+ you get a prompt on the next line to continue feeding characters into var1.
echo; echo echo; echo

View File

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
# Starting the script with "#!/bin/bash -r"
# runs entire script in restricted mode. # Starting the script with "#!/bin/bash -r"
#+ runs entire script in restricted mode.
echo echo

View File

@ -58,7 +58,7 @@ do #+ columns vary,
done done
# The simpler equivalent is # The simpler equivalent is
# echo ${alpha[*]} | xargs -n $Columns # echo ${alpha[*]} | xargs -n $Columns
echo echo
} }
@ -67,7 +67,7 @@ filter () # Filter out negative array indices.
{ {
echo -n " " # Provides the tilt. echo -n " " # Provides the tilt.
# Explain why. # Explain how.
if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]] if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]]
then then
@ -102,7 +102,8 @@ for (( row = Rows; row > -Rows; row-- ))
let "t2 = $column + $row" let "t2 = $column + $row"
fi fi
filter $t1 $t2 # Filter out negative array indices. Why? filter $t1 $t2 # Filter out negative array indices.
# What happens if you don't do this?
done done
echo; echo echo; echo

View File

@ -10,11 +10,22 @@ echo "Just initialized \"\$variable\" to $variable."
let "variable *= 3" let "variable *= 3"
echo "Just multiplied \"\$variable\" by 3." echo "Just multiplied \"\$variable\" by 3."
# The "trap 'commands' DEBUG" construct would be more useful exit $?
# in the context of a complex script,
# where placing multiple "echo $variable" statements might be # The "trap 'command1 . . . command2 . . .' DEBUG" construct is
# clumsy and time-consuming. #+ more appropriate in the context of a complex script,
#+ where placing multiple "echo $variable" statements might be
#+ clumsy and time-consuming.
# Thanks, Stephane Chazelas for the pointer. # Thanks, Stephane Chazelas for the pointer.
exit 0
Output of script:
VARIABLE-TRACE> $variable = ""
VARIABLE-TRACE> $variable = "29"
Just initialized "$variable" to 29.
VARIABLE-TRACE> $variable = "29"
VARIABLE-TRACE> $variable = "87"
Just multiplied "$variable" by 3.
VARIABLE-TRACE> $variable = "87"