LDP/LDP/guide/docbook/abs-guide/abs-guide.sgml

19511 lines
618 KiB
Plaintext
Raw Blame History

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!--
Uncomment line below to generate index.
-->
<!--
<!ENTITY indice SYSTEM "indice.sgml">
-->
<!ENTITY ex1 SYSTEM "ex1.sh">
<!ENTITY ex2 SYSTEM "ex2.sh">
<!ENTITY ex3 SYSTEM "ex3.sh">
<!ENTITY ex4 SYSTEM "ex4.sh">
<!ENTITY ex5 SYSTEM "ex5.sh">
<!ENTITY ex6 SYSTEM "ex6.sh">
<!ENTITY ex7 SYSTEM "ex7.sh">
<!ENTITY ex8 SYSTEM "ex8.sh">
<!ENTITY ex9 SYSTEM "ex9.sh">
<!ENTITY ex10 SYSTEM "ex10.sh">
<!ENTITY ex11 SYSTEM "ex11.sh">
<!ENTITY ex12 SYSTEM "ex12.sh">
<!ENTITY ex13 SYSTEM "ex13.sh">
<!ENTITY ex14 SYSTEM "ex14.sh">
<!ENTITY ex15 SYSTEM "ex15.sh">
<!ENTITY ex16 SYSTEM "ex16.sh">
<!ENTITY ex17 SYSTEM "ex17.sh">
<!ENTITY ex18 SYSTEM "ex18.sh">
<!ENTITY ex19 SYSTEM "ex19.sh">
<!ENTITY ex20 SYSTEM "ex20.sh">
<!ENTITY ex21 SYSTEM "ex21.sh">
<!ENTITY ex22 SYSTEM "ex22.sh">
<!ENTITY ex22a SYSTEM "ex22a.sh">
<!ENTITY ex23 SYSTEM "ex23.sh">
<!ENTITY ex24 SYSTEM "ex24.sh">
<!ENTITY ex25 SYSTEM "ex25.sh">
<!ENTITY ex26 SYSTEM "ex26.sh">
<!ENTITY ex26a SYSTEM "ex26a.sh">
<!ENTITY ex27 SYSTEM "ex27.sh">
<!ENTITY ex28 SYSTEM "ex28.sh">
<!ENTITY ex29 SYSTEM "ex29.sh">
<!ENTITY ex30 SYSTEM "ex30.sh">
<!ENTITY ex31 SYSTEM "ex31.sh">
<!ENTITY ex32 SYSTEM "ex32.sh">
<!ENTITY ex33 SYSTEM "ex33.sh">
<!ENTITY ex34 SYSTEM "ex34.sh">
<!ENTITY ex35 SYSTEM "ex35.sh">
<!ENTITY ex36 SYSTEM "ex36.sh">
<!ENTITY ex37 SYSTEM "ex37.sh">
<!ENTITY ex38 SYSTEM "ex38.sh">
<!ENTITY ex38bis SYSTEM "data-file">
<!ENTITY ex39 SYSTEM "ex39.sh">
<!ENTITY ex40 SYSTEM "ex40.sh">
<!ENTITY ex41 SYSTEM "ex41.sh">
<!ENTITY ex42 SYSTEM "ex42.sh">
<!ENTITY ex43 SYSTEM "ex43.sh">
<!ENTITY ex44 SYSTEM "ex44.sh">
<!ENTITY ex45 SYSTEM "ex45.sh">
<!ENTITY ex45a SYSTEM "ex45a.sh">
<!ENTITY ex46 SYSTEM "ex46.sh">
<!ENTITY ex47 SYSTEM "ex47.sh">
<!ENTITY ex48 SYSTEM "ex48.sh">
<!ENTITY ex49 SYSTEM "ex49.sh">
<!ENTITY ex50 SYSTEM "ex50.sh">
<!ENTITY ex51 SYSTEM "ex51.sh">
<!ENTITY ex52 SYSTEM "ex52.sh">
<!ENTITY ex53 SYSTEM "ex53.sh">
<!ENTITY ex54 SYSTEM "ex54.sh">
<!ENTITY ex55 SYSTEM "ex55.sh">
<!ENTITY ex56 SYSTEM "ex56.sh">
<!ENTITY ex57 SYSTEM "ex57.sh">
<!ENTITY ex58 SYSTEM "ex58.sh">
<!ENTITY ex59 SYSTEM "ex59.sh">
<!ENTITY ex60 SYSTEM "ex60.sh">
<!ENTITY ex61 SYSTEM "ex61.sh">
<!ENTITY ex62 SYSTEM "ex62.sh">
<!ENTITY ex63 SYSTEM "ex63.sh">
<!ENTITY ex64 SYSTEM "ex64.sh">
<!ENTITY ex65 SYSTEM "ex65.sh">
<!ENTITY ex66 SYSTEM "ex66.sh">
<!ENTITY ex67 SYSTEM "ex67.sh">
<!ENTITY ex68 SYSTEM "ex68.sh">
<!ENTITY ex69 SYSTEM "ex69.sh">
<!ENTITY ex70 SYSTEM "ex70.sh">
<!ENTITY ex71 SYSTEM "ex71.sh">
<!ENTITY ex71a SYSTEM "ex71a.sh">
<!ENTITY ex71b SYSTEM "ex71b.sh">
<!ENTITY ex71c SYSTEM "ex71c.sh">
<!ENTITY ex72 SYSTEM "ex72.sh">
<!ENTITY ex73 SYSTEM "ex73.sh">
<!ENTITY ex74 SYSTEM "ex74.sh">
<!ENTITY ex75 SYSTEM "ex75.sh">
<!ENTITY ex76 SYSTEM "ex76.sh">
<!ENTITY ex77 SYSTEM "ex77.sh">
<!ENTITY ex78 SYSTEM "ex78.sh">
<!ENTITY ex79 SYSTEM "ex79.sh">
<!ENTITY andor SYSTEM "and-or.sh">
<!ENTITY lnum SYSTEM "line-number.sh">
<!ENTITY manview SYSTEM "manview.sh">
<!ENTITY rfe SYSTEM "rfe.sh">
<!ENTITY behead SYSTEM "behead.sh">
<!ENTITY ftpget SYSTEM "ftpget.sh">
<!ENTITY encryptedpw SYSTEM "encryptedpw.sh">
<!ENTITY rpmcheck SYSTEM "rpm-check.sh">
<!ENTITY subshell SYSTEM "subshell.sh">
<!ENTITY lowercase SYSTEM "lowercase.sh">
<!ENTITY online SYSTEM "online.sh">
<!ENTITY reply SYSTEM "reply.sh">
<!ENTITY seconds SYSTEM "seconds.sh">
<!ENTITY numbers SYSTEM "numbers.sh">
<!ENTITY indref SYSTEM "ind-ref.sh">
<!ENTITY bubble SYSTEM "bubble.sh">
<!ENTITY paramsub SYSTEM "param-sub.sh">
<!ENTITY restricted SYSTEM "restricted.sh">
<!ENTITY pw SYSTEM "pw.sh">
<!ENTITY rn SYSTEM "rn.sh">
<!ENTITY coltotaler SYSTEM "col-totaler.sh">
<!ENTITY coltotaler2 SYSTEM "col-totaler2.sh">
<!ENTITY coltotaler3 SYSTEM "col-totaler3.sh">
<!ENTITY tmdin SYSTEM "timed-input.sh">
<!ENTITY fifo SYSTEM "fifo.sh">
<!ENTITY tree SYSTEM "tree.sh">
<!ENTITY secretpw SYSTEM "secret-pw.sh">
<!ENTITY stripc SYSTEM "strip-comments.sh">
<!ENTITY al SYSTEM "alias.sh">
<!ENTITY unal SYSTEM "unalias.sh">
<!ENTITY redir1 SYSTEM "redir1.sh">
<!ENTITY redir2 SYSTEM "redir2.sh">
<!ENTITY redir2a SYSTEM "redir2a.sh">
<!ENTITY redir3 SYSTEM "redir3.sh">
<!ENTITY redir4 SYSTEM "redir4.sh">
<!ENTITY redir5 SYSTEM "redir5.sh">
<!ENTITY wipedir SYSTEM "wipedir.sh">
<!ENTITY grp SYSTEM "grp.sh">
<!ENTITY killprocess SYSTEM "kill-process.sh">
<!ENTITY strtest SYSTEM "str-test.sh">
<!ENTITY col SYSTEM "col.sh">
<!ENTITY lookup SYSTEM "lookup.sh">
<!ENTITY arglist SYSTEM "arglist.sh">
<!ENTITY rot13 SYSTEM "rot13.sh">
<!ENTITY rot13_2 SYSTEM "rot13_2.sh">
<!ENTITY filecomp SYSTEM "file-comparison.sh">
<!ENTITY adddrv SYSTEM "add-drive.sh">
<!ENTITY whloopc SYSTEM "wh-loopc.sh">
<!ENTITY forloopc SYSTEM "for-loopc.sh">
<!ENTITY forloopcmd SYSTEM "for-loopcmd.sh">
<!ENTITY cvars SYSTEM "c-vars.sh">
<!ENTITY bingrep SYSTEM "bin-grep.sh">
<!ENTITY mailformat SYSTEM "mail-format.sh">
<!ENTITY symlinks SYSTEM "symlinks.sh">
<!ENTITY string SYSTEM "string.sh">
<!ENTITY nestedloop SYSTEM "nested-loop.sh">
<!ENTITY casecmd SYSTEM "case-cmd.sh">
<!ENTITY uns SYSTEM "unset.sh">
<!ENTITY base SYSTEM "base.sh">
<!ENTITY allprofs SYSTEM "allprofs.sh">
<!ENTITY pidid SYSTEM "pid-identifier.sh">
<!ENTITY constat SYSTEM "connect-stat.sh">
<!ENTITY subpit SYSTEM "subshell-pitfalls.sh">
<!ENTITY readredir SYSTEM "read-redir.sh">
<!ENTITY andlist2 SYSTEM "and-list2.sh">
<!ENTITY qfunction SYSTEM "q-function.sh">
<!ENTITY viewdata SYSTEM "viewdata.sh">
<!ENTITY VIEWDAT SYSTEM "VIEWDATA.BAT">
<!ENTITY what SYSTEM "what.sh">
<!ENTITY max SYSTEM "max.sh">
<!ENTITY max2 SYSTEM "max2.sh">
<!ENTITY findstring SYSTEM "findstring.sh">
<!ENTITY listglob SYSTEM "list-glob.sh">
<!ENTITY realname SYSTEM "realname.sh">
<!ENTITY escaped SYSTEM "escaped.sh">
<!ENTITY fileinfo SYSTEM "file-info.sh">
<!ENTITY weirdvars SYSTEM "weirdvars.sh">
<!ENTITY breaklevels SYSTEM "break-levels.sh">
<!ENTITY copycd SYSTEM "copy-cd.sh">
<!ENTITY arithops SYSTEM "arith-ops.sh">
<!ENTITY continuelevels SYSTEM "continue-nlevel.sh">
<!ENTITY timeout SYSTEM "timeout.sh">
<!ENTITY randomtest SYSTEM "random-test.sh">
<!ENTITY seedingrandom SYSTEM "seeding-random.sh">
<!ENTITY pattmatching SYSTEM "patt-matching.sh">
<!ENTITY isalpha SYSTEM "isalpha.sh">
<!ENTITY rnd SYSTEM "rnd.sh">
<!ENTITY du SYSTEM "du.sh">
<!ENTITY refparams SYSTEM "ref-params.sh">
<!ENTITY primes SYSTEM "primes.sh">
<!ENTITY vartrace SYSTEM "vartrace.sh">
<!ENTITY amiroot SYSTEM "am-i-root.sh">
<!ENTITY twodim SYSTEM "twodim.sh">
<!ENTITY arithtests SYSTEM "arith-tests.sh">
<!ENTITY incompat SYSTEM "incompat.sh">
<!ENTITY ifsh SYSTEM "ifs.sh">
<!ENTITY ifsempty SYSTEM "ifs-empty.sh">
<!ENTITY logevents SYSTEM "logevents.sh">
<!ENTITY keypress SYSTEM "keypress.sh">
<!ENTITY ddkeypress SYSTEM "dd-keypress.sh">
<!ENTITY objoriented SYSTEM "obj-oriented.sh">
<!ENTITY emptyarray SYSTEM "empty-array.sh">
<!ENTITY length SYSTEM "length.sh">
<!ENTITY monthlypmt SYSTEM "monthlypmt.sh">
<!ENTITY derpm SYSTEM "de-rpm.sh">
<!ENTITY blotout SYSTEM "blot-out.sh">
<!ENTITY readr SYSTEM "read-r.sh">
<!ENTITY cryptoquote SYSTEM "crypto-quote.sh">
<!ENTITY erase SYSTEM "erase.sh">
<!ENTITY returntest SYSTEM "return-test.sh">
<!ENTITY daysbetween SYSTEM "days-between.sh">
<!ENTITY varmatch SYSTEM "var-match.sh">
<!ENTITY bashrc SYSTEM "bashrc">
]>
<book>
<bookinfo>
<title>Advanced Bash-Scripting Guide</title>
<subtitle>A complete guide to shell scripting, using Bash</subtitle>
<author>
<firstname>Mendel</firstname>
<surname>Cooper</surname>
<affiliation>
<orgname>Brindlesoft</orgname>
<address><email>thegrendel@theriver.com</email></address>
</affiliation>
</author>
<pubdate>03 September 2001</pubdate>
<revhistory>
<revision>
<revnumber>0.1</revnumber>
<date>14 June 2000</date>
<authorinitials>mc</authorinitials>
<revremark>Initial release.</revremark>
</revision>
<revision>
<revnumber>0.2</revnumber>
<date>30 October 2000</date>
<authorinitials>mc</authorinitials>
<revremark>Bugs fixed, plus much additional material and more example
scripts.</revremark>
</revision>
<revision>
<revnumber>0.3</revnumber>
<date>12 February 2001</date>
<authorinitials>mc</authorinitials>
<revremark>Another major update.</revremark>
</revision>
<revision>
<revnumber>0.4</revnumber>
<date>08 July 2001</date>
<authorinitials>mc</authorinitials>
<revremark>More bugfixes, much more material, more
scripts - a complete revision and expansion of the book.</revremark>
</revision>
<revision>
<revnumber>0.5</revnumber>
<date>03 September 2001</date>
<authorinitials>mc</authorinitials>
<revremark>Major update. Bugfixes, material added, chapters and
sections reorganized.</revremark>
</revision>
</revhistory>
<abstract>
<para>This is an major update of version 0.4. The main feature
of this release is a major reorganization of the document
into parts and chapters (just like a <quote>real</quote>
book). Of course, more bugs have been swatted, and additional
material and example scripts added. This project has
now reached the equivalent of a 360-page book. See the
<filename>Change.log</filename> file for a revision
history.</para>
<para>This document is both a tutorial and a reference on shell
scripting with Bash. It assumes no previous knowledge of
scripting or programming, but progresses rapidly toward an
intermediate/advanced level of instruction.
<footnote><para>...all the while sneaking in little snippets of UNIX
wisdom and lore.</para></footnote>
The exercises and heavily-commented examples invite active reader
participation.</para>
<para>This project has evolved into a comprehensive book that
compares favorably with any of the shell scripting manuals in
print. It may serve as a textbook, a manual for self-study, and a
reference and source of knowledge on shell scripting
techniques.</para>
<para>The latest version of this document, as an archived
<quote>tarball</quote> including both the SGML
source and rendered HTML, may be downloaded from <ulink
url="http://personal.riverusers.com/~thegrendel/abs-guide-0.5.tar.gz">
the author's home site</ulink>.</para>
</abstract>
</bookinfo>
<dedication>
<para>For Anita, the source of all the magic</para>
</dedication>
<part label="Part 1" id="part1">
<title>Introduction</title>
<partintro>
<para>The shell is a command interpreter. More than just the
insulating layer between the operating system kernel and
the user, it's also a fairly powerful programming language.
A shell program, called a <emphasis>script</emphasis>, is an
easy-to-use tool for building applications by <quote>gluing</quote>
together system calls, tools, utilities, and compiled binaries.
Virtually the entire repertoire of UNIX commands, utilities, and
tools is available for invocation by a shell script. If that
were not enough, internal shell commands, such as testing
and loop constructs, give additional power and flexibility to
scripts. Shell scripts lend themselves exceptionally well to to
administrative system tasks and other routine repetitive jobs not
requiring the bells and whistles of a full-blown tightly structured
programming language.</para>
</partintro>
<chapter id="why-shell">
<title>Why Shell Programming?</title>
<para>A working knowledge of shell scripting is essential to everyone
wishing to become reasonably adept at system administration,
even if they do not anticipate ever having to actually write a
script. Consider that as a Linux machine boots up, it executes the
shell scripts in <filename class="directory">/etc/rc.d</filename>
to restore the system configuration and set up services. A detailed
understanding of these startup scripts is important for analyzing
the behavior of a system, and possibly modifying it.</para>
<para>Writing shell scripts is not hard to learn, since the scripts
can be built in bite-sized sections and there is only a fairly
small set of shell-specific operators and options
<footnote><para>These are referred to as <link
linkend="builtinref">builtins</link>, features internal to the
shell.</para></footnote>
to learn. The syntax is simple and straightforward, similar to
that of invoking and chaining together utilities at the command
line, and there are only a few <quote>rules</quote> to learn.
Most short scripts work right the first time, and debugging even
the longer ones is straightforward.</para>
<para>A shell script is a <quote>quick and dirty</quote> method of
prototyping a complex application. Getting even a limited subset
of the functionality to work in a shell script, even if slowly,
is often a useful first stage in project development. This way,
the structure of the application can be tested and played with,
and the major pitfalls found before proceeding to the final coding
in C, C++, Java, or Perl.</para>
<para>Shell scripting hearkens back to the classical UNIX philosophy
of breaking complex projects into simpler subtasks, of chaining
together components and utilities. Many consider this a better,
or at least more esthetically pleasing approach to problem solving
than using one of the new generation of high powered all-in-one
languages, such as Perl, which attempt to be all things to all
people, but at the cost of forcing you to alter your thinking
processes to fit the tool.</para>
<para>When not to use shell scripts
<itemizedlist>
<listitem>
<para>resource-intensive tasks, especially where speed is
a factor (sorting, hashing, etc.)</para>
</listitem> <listitem>
<para>procedures involving heavy-duty math operations,
especially floating point arithmetic, arbitrary precision
calculations, or complex numbers (use C++ or FORTRAN
instead)</para>
</listitem> <listitem>
<para>cross-platform portability required (use C instead)</para>
</listitem> <listitem>
<para>complex applications, where structured programming is
a necessity (need typechecking of variables, function
prototypes, etc.)</para>
</listitem> <listitem>
<para>mission-critical applications upon which you are betting the
ranch, or the future of the company</para>
</listitem> <listitem>
<para>situations where security is important, where you need to
protect against hacking</para>
</listitem> <listitem>
<para>project consists of subcomponents with interlocking
dependencies</para>
</listitem> <listitem>
<para>extensive file operations required (Bash is limited to
serial file access, and that only in a particularly clumsy
and inefficient line-by-line fashion)</para>
</listitem> <listitem>
<para>need multi-dimensional arrays</para>
</listitem> <listitem>
<para>need data structures, such as linked lists or trees</para>
</listitem> <listitem>
<para>need to generate or manipulate graphics or GUIs</para>
</listitem> <listitem>
<para>need direct access to system hardware</para>
</listitem> <listitem>
<para>need port or socket I/O</para>
</listitem> <listitem>
<para>need to use libraries or interface with legacy code</para>
</listitem> <listitem>
<para>proprietary, closed-source applications (shell scripts are
necessarily Open Source)</para>
</listitem>
</itemizedlist></para>
<para>If any of the above applies, consider a more powerful scripting
language, perhaps Perl, Tcl, Python, or possibly a high-level
compiled language such as C, C++, or Java. Even then, prototyping
the application as a shell script might still be a useful
development step.</para>
<para>We will be using Bash, an acronym for <quote>Bourne-Again
Shell</quote> and a pun on Stephen Bourne's now classic Bourne
Shell. Bash has become a <foreignphrase>de facto</foreignphrase>
standard for shell scripting on all flavors of UNIX. Most of
the principles dealt with in this document apply equally well to
scripting with other shells, such as the Korn Shell, from which
Bash derives some of its features,
<footnote><para>Many of the features of <emphasis>ksh88</emphasis>,
and even a few from the updated <emphasis>ksh93</emphasis>
have been merged into Bash.</para></footnote>
and the C Shell and its variants. (Note that C Shell programming
is not recommended due to certain inherent problems, as pointed
out in a <ulink
url="http://www.etext.org/Quartz/computer/unix/csh.harmful.gz">news
group posting</ulink> by Tom Christiansen in October of 1993).
</para>
<para>The following is a tutorial in shell scripting. It relies
heavily on examples to illustrate features of the shell.
As far as possible, the example scripts have been tested, and
some of them may actually be useful in real life. The reader
should use the actual examples in the the source archive
(<filename>something-or-other.sh</filename>),
<footnote><para>By convention, user-written shell scripts that are
Bourne shell compliant generally take a name with a
<filename>.sh</filename> extension. System scripts, such as
those found in <filename class="directory">/etc/rc.d</filename>,
do not follow this guideline.</para></footnote>
give them execute permission (<userinput>chmod u+rx
scriptname</userinput>), then run them to see what happens.
Should the source archive not be available, then cut-and-paste from
the HTML, pdf, or text rendered versions. Be aware that some of
the scripts below introduce features before they are explained,
and this may require the reader to temporarily skip ahead for
enlightenment.</para>
<para>Unless otherwise noted, the author of this document wrote
the example scripts that follow.</para>
</chapter> <!-- Why Shell Programming? -->
<chapter id="sha-bang">
<title>Starting Off With a Sha-Bang</title>
<para>In the simplest case, a script is nothing more than a list of system
commands stored in a file. At the very least, this saves the
effort of retyping that particular sequence of commands each time
it is invoked.</para>
<example id="ex1">
<title><command>cleanup</command>: A script to clean up the log
files in /var/log </title> <programlisting>&ex1;</programlisting>
</example>
<para>There is nothing unusual here, just a set of commands that
could just as easily be invoked one by one from the command line on
the console or in an xterm. The advantages of placing the commands
in a script go beyond not having to retype them time and again. The
script can easily be modified, customized, or generalized for a
particular application.</para>
<example id="ex2">
<title><command>cleanup</command>: An enhanced
and generalized version of above script.</title>
<programlisting>&ex2;</programlisting>
</example>
<para>Since you may not wish to wipe out the entire system log, this variant of
the first script keeps the last section of the message log intact. You
will constantly discover ways of refining previously written scripts
for increased effectiveness.</para>
<para>The
<firstterm><indexterm>
<primary>sha-bang</primary>
</indexterm>
sha-bang</firstterm>
(<token>
<indexterm>
<primary>#!</primary>
</indexterm>
#!</token>) at the head of a script
tells your system that this file
is a set of commands to be fed to the command interpreter indicated.
<anchor id="magnumref">The <token>#!</token> is actually a two-byte
<footnote><para>Some flavors
of UNIX take a four-byte magic number,
requiring a blank after the <token>!</token>,
<userinput>#! bin/sh</userinput>.</para></footnote>
<indexterm>
<primary>magic number</primary>
</indexterm>
<quote>magic number</quote>, a special marker that
designates a file type, or in this case an executable
shell script (see <userinput>man magic</userinput> for more
details on this fascinating topic). Immediately following
the <emphasis>sha-bang</emphasis> is a path name. This is the
path to the program that interprets the commands in the script,
whether it be a shell, a programming language, or a utility. This
command interpreter then executes the commands in the script,
starting at the top (line 1 of the script), ignoring comments.
<footnote><para>The <token>#!</token> line in a shell script
will be the first thing the command interpreter
(<command>sh</command> or <command>bash</command>)
sees. Since this line begins with a <token>#</token>,
it will be correctly interpreted as a comment when the
command interpreter finally executes the script. The
line has already served its purpose - calling the command
interpreter.</para></footnote>
</para>
<para><programlisting>#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/bin/tcl
#!/bin/sed -f
#!/usr/awk -f</programlisting></para>
<para>Each of the above script header lines calls a different command
interpreter, be it <filename>/bin/sh</filename>, the default shell
(<command>bash</command> in a Linux system) or otherwise.
<footnote>
<para>This allows some cute tricks.</para>
<para><programlisting>#!/bin/rm
# Self-deleting script.
# Nothing much seems to happen when you run this... except that the file disappears.
WHATEVER=65
echo "This line will never print (betcha!)."
exit $WHATEVER # Doesn't matter. The script will not exit here.</programlisting></para>
<para>Also, try starting a <filename>README</filename> file with a
<userinput>#!/bin/more</userinput>, and making it executable.
The result is a self-listing documentation file.</para>
</footnote>
Using <userinput>#!/bin/sh</userinput>, the default Bourne Shell
in most commercial variants of UNIX, makes the script <link
linkend="portabilityissues">portable</link> to non-Linux machines,
though you may have to sacrifice a few Bash-specific features
(the script will conform to the
<acronym>POSIX</acronym>
<footnote><para><emphasis>P</emphasis>ortable
<emphasis>O</emphasis>perating <emphasis>S</emphasis>ystem
<emphasis>I</emphasis>nterface, an attempt to standardize
UNIX-like OSes.</para></footnote>
<command>sh</command> standard).</para>
<para>Note that the path given at the <quote>sha-bang</quote> must
be correct, otherwise an error message, usually <quote>Command not
found</quote> will be the only result of running the script.</para>
<para><token>#!</token> can be omitted if the script consists only
of a set of generic system commands, using no internal
shell directives. Example 2, above, requires the initial
<token>#!</token>, since the variable assignment line,
<userinput>lines=50</userinput>, uses a shell-specific construct.
Note that <userinput>#!/bin/sh</userinput> invokes the default
shell interpreter, which defaults to <filename>/bin/bash</filename>
on a Linux machine.</para>
<important><para>This tutorial encourages a modular approach
to constructing a script. Make note of and collect
<quote>boilerplate</quote> code snippets that might be useful
in future scripts. Eventually you can build a quite extensive
library of nifty routines. As an example, the following script
prolog tests whether the script has been invoked with the correct
number of parameters.
<programlisting>if [ $# -ne Number_of_expected args ]
then
echo "Usage: `basename $0` whatever"
exit $WRONG_ARGS
fi</programlisting>
</para></important>
<sect1 id="invoking">
<title>Invoking the script</title>
<para>Having written the script, you can invoke it by <userinput>sh
scriptname</userinput>,
<footnote><para>Caution: invoking a Bash script by <userinput>sh
scriptname</userinput> turns off Bash-specific extensions, and the
script may therefore fail to execute.</para></footnote>
or alternately <userinput>bash scriptname</userinput>. (Not
recommended is using <userinput>sh &lt;scriptname</userinput>,
since this effectively disables reading from
<filename>stdin</filename> within the script.) Much more
convenient is to make the script itself directly executable with
a <link linkend="chmodref">chmod</link>.
<variablelist>
<varlistentry>
<term>Either:</term> <listitem>
<para><userinput>chmod 555 scriptname</userinput> (gives
everyone read/execute permission)
<footnote><para>A script needs <emphasis>read</emphasis>, as
well as execute permission for it to run, since the shell
needs to be able to read it.</para></footnote>
</para>
</listitem>
</varlistentry> <varlistentry>
<term>or</term> <listitem>
<para><userinput>chmod +rx scriptname</userinput> (gives
everyone read/execute permission)</para> <para><userinput>chmod
u+rx scriptname</userinput> (gives only the
script owner read/execute permission)</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>Having made the script executable, you may now test it by
<userinput>./scriptname</userinput>.
<footnote><para>Why not simply invoke the script with
<userinput>scriptname</userinput>? If the directory
you are in (<link linkend="pwdref">$PWD</link>) is where
<emphasis>scriptname</emphasis> is located, why doesn't this
work? This fails because, for security reasons, the current
directory, <quote>.</quote> is not included in a user's <link
linkend="pathref">$PATH</link>. It is therefore necessary to
explicitly invoke the script in the current directory with
a <userinput>./scriptname</userinput>.</para></footnote>
If it begins with a <quote>sha-bang</quote> line, invoking the
script calls the correct command interpreter to run it.</para>
<para>As a final step, after testing and debugging,
you would likely want to move it to <filename
class="directory">/usr/local/bin</filename> (as root, of
course), to make the script available to yourself and all
other users as a system-wide executable. The script could
then be invoked by simply typing <command>scriptname</command>
<keycap>[ENTER]</keycap> from the command line.</para>
</sect1> <!-- Invoking the script -->
<sect1 id="wrapper">
<title>Shell wrapper, self-executing script</title>
<para>A
<command>
<indexterm>
<primary>sed</primary>
</indexterm>
<indexterm>
<primary>script</primary>
<secondary>sed</secondary>
</indexterm>
sed</command> or
<command>
<indexterm>
<primary>awk</primary>
</indexterm>
<indexterm>
<primary>script</primary>
<secondary>awk</secondary>
</indexterm>
awk</command> script would normally be invoked
from the command line by a <userinput>sed -e
<replaceable>'commands'</replaceable></userinput>
or <userinput>awk
<replaceable>'commands'</replaceable></userinput>.
Embedding such a script in a bash script
permits calling it more simply, and makes it
<quote>reusable</quote>. This also enables combining
the functionality of <link linkend="sedref">sed</link>
and <link linkend="awkref">awk</link>, for example <link
linkend="piperef">piping</link> the output of a set of
<command>sed</command> commands to <command>awk</command>.
As a saved executable file, you can then repeatedly invoke it
in its original form or modified, without the inconvenience
of retyping it on the command line.</para>
<example id="ex3">
<title><command>shell wrapper</command></title>
<programlisting>&ex3;</programlisting>
</example>
<example id="ex4">
<title> A slightly more complex <command>shell wrapper</command></title>
<programlisting>&ex4;</programlisting>
</example>
<example id="coltotaler">
<title> A <command>shell wrapper</command> around an awk script</title>
<programlisting>&coltotaler;</programlisting>
</example>
<para><anchor id="perlref">For those scripts needing a single
do-it-all tool, a Swiss army knife, there is Perl. Perl combines
the capabilities of <link linkend="sedref">sed</link> and <link
linkend="awkref">awk</link>, and throws in a large subset of
<command>C</command>, to boot. It is modular and contains support
for everything ranging from object-oriented programming up to and
including the kitchen sink. Short Perl scripts can be effectively
embedded in shell scripts, and there may even be some substance
to the claim that Perl can totally replace shell scripting
(though the author of this document remains skeptical).</para>
<example id="ex56">
<title>Perl embedded in a <command>bash</command> script</title>
<programlisting>&ex56;</programlisting>
</example>
<formalpara><title>Exercise</title>
<para>Write a shell script that performs a simple task.</para>
</formalpara>
</sect1> <!-- Shell wrapper, self-executing script -->
</chapter> <!-- Starting Off With a Sha-Bang -->
</part> <!-- Part 1 (Introduction) -->
<part label="Part 2" id="part2">
<title>Basics</title>
<chapter id="exit-status">
<title>Exit and Exit Status</title>
<epigraph>
<attribution>Chet Ramey</attribution>
<para>...there are dark corners in the Bourne shell, and people use all
of them.</para>
</epigraph>
<para><anchor id="exitcommandref">The
<command>
<indexterm>
<primary>exit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exit</secondary>
</indexterm>
exit
</command>
command may be used to terminate a script, just as in a C program.
It can also return a value, which is available to the script's parent
process.</para>
<para><anchor id="exitstatusref">Every command returns an
<firstterm>
<indexterm>
<primary>exit status</primary>
</indexterm>
exit status
</firstterm>
(sometimes referred to as a
<firstterm>
<indexterm>
<primary>return status</primary>
</indexterm>
return status
</firstterm>). A successful command returns a
<returnvalue>0</returnvalue>, while an unsuccessful one returns
a <returnvalue>non-zero</returnvalue> value that usually may
be interpreted as an error code. Well-behaved UNIX commands,
programs, and utilities return a <returnvalue>0</returnvalue>
exit code upon successful completion, though there are some
exceptions.</para>
<para>Likewise, functions within a script and the script itself
return an exit status. The last command executed in the function
or script determines the exit status. Within a script, an
<userinput>exit <replaceable>nn</replaceable></userinput>
command may be used to deliver an
<returnvalue><replaceable>nn</replaceable></returnvalue> exit status
to the shell (<returnvalue><replaceable>nn</replaceable></returnvalue>
must be a decimal number in the <returnvalue>0</returnvalue> -
<returnvalue>255</returnvalue> range).</para>
<note><para>When a script ends with an <command>exit</command> that has
no parameter, the exit status of the script is the exit status of
the last command executed in the script (<emphasis>not</emphasis>
counting the <command>exit</command>).</para></note>
<para>
<varname>
<indexterm>
<primary>$?</primary>
</indexterm> <indexterm>
<primary>variable</primary> <secondary>$?</secondary>
</indexterm> $?</varname> reads the exit status of the last
command executed. After a function returns,
<varname>$?</varname> gives the exit status of the last
command executed in the function. This is Bash's way of
giving functions a <quote>return value</quote>. After a
script terminates, a <varname>$?</varname> from the command
line gives the exit status of the script, that is, the last
command executed in the script, which is, by convention,
<userinput>0</userinput> on success or an integer in the
range 1 - 255 on error.</para>
<example id="ex5">
<title>exit / exit status</title>
<programlisting>&ex5;</programlisting>
</example>
<para><link linkend="xstatvarref">$?</link> is especially useful
for testing the result of a command in a script (see <xref
linkend="filecomp"> and <xref linkend="lookup">).</para>
<note>
<para>The <link linkend="notref">!</link>, the logical
<quote>not</quote> qualifier, reverses the outcome of a test or
command, and this affects its <link linkend="exitstatusref">exit
status</link>.
<example id="negcond">
<title>Negating a condition using <token>!</token></title>
<programlisting>true # the "true" builtin.
echo "exit status of \"true\" = $?" # 0
! true
echo "exit status of \"! true\" = $?" # 1
# Note that the "!" needs a space.
# !true leads to a "command not found" error
# Thanks, S.C.</programlisting>
</example>
</para>
</note>
<caution><para>Certain exit status codes have <link
linkend="exitcodesref">reserved meanings</link> and should not
be user-specified in a script. </para></caution>
</chapter> <!-- Exit and Exit status -->
<chapter id="special-chars">
<title>Special Characters</title>
<variablelist id="scharlist">
<title><anchor id="scharlist1">Special Characters Found In
Scripts and Elsewhere</title>
<varlistentry><term><token>#</token></term>
<indexterm>
<primary>#</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>#</secondary>
</indexterm>
<indexterm>
<primary>comment</primary>
</indexterm>
<listitem>
<formalpara><title>Comments</title>
<para>Lines beginning with a <token>#</token>
(<link linkend="magnumref">with the exception of
<token>#!</token></link>) are comments.</para>
</formalpara>
<para><programlisting># This line is a comment.</programlisting></para>
<para>Comments may also occur at the end of a command.</para>
<para><programlisting>echo "A comment will follow." # Comment here.</programlisting></para>
<para>Comments may also follow <link
linkend="whitespaceref">whitespace</link> at the beginning
of a line.</para>
<para><programlisting> # A tab precedes this comment.</programlisting></para>
<caution><para>A command may not follow a comment on the
same line. There is no method of terminating the comment,
in order for <quote>live code</quote> to begin on the same
line. Use a new line for the next command.</para></caution>
<note><para>Of course, an escaped <token>#</token> in an
<command>echo</command> statement does
<emphasis>not</emphasis> begin a comment. Likewise, a
<token>#</token> appears in <link linkend="psub2">certain
parameter substitution constructs</link> and in <link
linkend="numconstants">numerical constant expressions</link>.
<programlisting>echo "The # here does not begin a comment."
echo 'The # here does not begin a comment.'
echo The \# here does not begin a comment.
echo The # here begins a comment.
echo ${PATH#*:} # Parameter substitution, not a comment.
echo $(( 2#101011 )) # Base conversion, not a comment.
# Thanks, S.C.</programlisting>
The standard <link linkend="quotingref">quoting and
escape</link> characters (&quot; ' \) escape the #.
</para></note>
<para>Certain <link linkend="psorex1">pattern matching
operations</link> also use the <token>#</token>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>;</token></term>
<indexterm>
<primary>;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>;</secondary>
</indexterm>
<indexterm>
<primary>separator</primary>
</indexterm>
<listitem>
<formalpara><title>Command separator</title>
<para>[Semicolon] Permits putting two or more commands on
the same line.</para>
</formalpara>
<para><programlisting>echo hello; echo there</programlisting></para>
<para>Note that the <quote><token>;</token></quote> sometimes
needs to be <link linkend="escp">escaped</link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>.</token></term>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<indexterm>
<primary>dot command</primary>
</indexterm>
<indexterm>
<primary>source</primary>
</indexterm>
<listitem>
<para><anchor id="dotref"></para>
<formalpara><title><quote>dot</quote> command</title>
<para>[period] Equivalent to <link
linkend="sourceref">source</link> (see
<xref linkend="ex38">). This is a bash <link
linkend="builtinref">builtin</link>.</para>
</formalpara>
<para><link linkend="regexdot">In a different
context</link>, as part of a <link
linkend="regexref">regular expression</link>, a
<quote>dot</quote> matches a single character.</para>
<para>In yet another context, a dot is the filename prefix
of a <quote>hidden</quote> file, a file that an
<command>ls</command> will not normally show.
<screen><prompt>bash$ </prompt><userinput>touch .hidden-file</userinput>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 10
-rw-r--r-- 1 bozo 4034 Jul 18 22:04 data1.addressbook
-rw-r--r-- 1 bozo 4602 May 25 13:58 data1.addressbook.bak
-rw-r--r-- 1 bozo 877 Dec 17 2000 employment.addressbook</computeroutput>
<prompt>bash$ </prompt><userinput>ls -al</userinput>
<computeroutput>total 14
drwxrwxr-x 2 bozo bozo 1024 Aug 29 20:54 ./
drwx------ 52 bozo bozo 3072 Aug 29 20:51 ../
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.addressbook
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.addressbook.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.addressbook
-rw-rw-r-- 1 bozo bozo 0 Aug 29 20:54 .hidden-file</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>"</token></term>
<listitem><formalpara><title><link linkend="dblquo">partial
quoting</link></title>
<para>[double quote] <emphasis>"STRING"</emphasis>
preserves (from interpretation) most of the special
characters within <emphasis>STRING</emphasis>. See also
<xref linkend="quoting">.</para>
</formalpara> </listitem>
</varlistentry>
<varlistentry>
<term><token>'</token></term>
<listitem><formalpara><title><link linkend="snglquo">full
quoting</link></title>
<para>[single quote] <emphasis>'STRING'</emphasis>
preserves all special characters within
<emphasis>STRING</emphasis>. This is a stronger form
of quoting than using <token>"</token>. See also <xref
linkend="quoting">.</para>
</formalpara> </listitem>
</varlistentry>
<varlistentry>
<term><token>,</token></term>
<listitem><formalpara><title><link linkend="commaop">comma
operator</link></title>
<para>The <command>comma operator</command> links together a
series of arithmetic operations. All are evaluated, but only
the last one is returned.
<programlisting>let "t2 = ((a = 9, 15 / 3))" # Set "a" and calculate "t2".</programlisting>
</para>
</formalpara> </listitem>
</varlistentry>
<varlistentry>
<term><token>\</token></term>
<listitem><formalpara><title><link linkend="escp">escape</link></title>
<para>[backslash] <userinput>\X</userinput>
<quote>escapes</quote> the character
<emphasis>X</emphasis>. This has the effect of
<quote>quoting</quote> <emphasis>X</emphasis>, equivalent
to <emphasis>'X'</emphasis>. The <token>\</token> may
be used to quote <token>"</token> and <token>'</token>,
so they are expressed literally.</para>
</formalpara>
<para>See <xref linkend="quoting"> for an in-depth explanation
of escaped characters.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/</token></term>
<listitem><formalpara><title>Filename path separator</title>
<para>[forward slash] Separates
the components of a filename (as in
<filename>/home/bozo/projects/Makefile</filename>).</para>
</formalpara>
<para>This is also the division <link
linkend="arops1">arithmetic operator</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>`</token></term>
<listitem><formalpara><title><link
linkend="commandsubref">command substitution</link></title>
<para>[backticks] <emphasis>`command`</emphasis> makes
available the output of <emphasis>command</emphasis>
for setting a variable. This is also known as
<link linkend="backquotesref">backticks</link> or
backquotes.</para></formalpara> </listitem>
</varlistentry>
<varlistentry><term><token>:</token></term>
<indexterm>
<primary>:</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>:</secondary>
</indexterm>
<indexterm>
<primary>null command</primary>
</indexterm>
<indexterm>
<primary>true</primary>
</indexterm>
<indexterm>
<primary>endless loop</primary>
</indexterm>
<listitem>
<para><anchor id="nullref"></para>
<formalpara><title>null command</title>
<para>[colon] This is the shell equivalent of a
<quote>NOP</quote> (<replaceable>no op</replaceable>, a
do-nothing operation). It may be considered a synonym for
the shell builtin <link linkend="trueref">true</link>. The
<quote><token>:</token></quote> command is a Bash
builtin, and its <link linkend="exitstatusref">exit
status</link> is <quote>true</quote>
(<returnvalue>0</returnvalue>).</para>
</formalpara>
<para><programlisting>:
echo $? # 0</programlisting></para>
<para>Endless loop:</para>
<para><programlisting>
while :
do
operation-1
operation-2
...
operation-n
done
# Same as:
# while true
# do
# ...
# done</programlisting>
</para>
<para>Placeholder in if/then test:</para>
<para><programlisting>
if condition
then : # Do nothing and branch ahead
else
take-some-action
fi</programlisting>
</para>
<para>Provide a placeholder where a binary operation is
expected, see <xref linkend="arithops"> and <link
linkend="defparam">default parameters</link>.</para>
<para><programlisting>: ${username=`whoami`}
# ${username=`whoami`} without the leading : gives an error
# unless "username" is a command or builtin...</programlisting>
</para>
<para>Provide a placeholder where a command is expected in a
<link linkend="heredocref">here document</link>. See <xref
linkend="anonheredoc">.</para>
<para>Evaluate string of variables using
<link linkend="paramsubref">parameter substitution</link>
(as in <xref linkend="ex6">).
<programlisting>: ${HOSTNAME?} ${USER?} ${MAIL?}
#Prints error message if one or more of essential environmental variables not set.</programlisting>
</para>
<para><command><link linkend="exprepl1">Variable expansion / substring
replacement</link></command>.</para>
<para>In combination with the <token>&gt;</token> <link
linkend="ioredirref">redirection operator</link>,
truncates a file to zero length, without changing its
permissions. If the file did not previously exist,
creates it.
<programlisting>: > data.xxx # File "data.xxx" now empty.
# Same effect as cat /dev/null >data.xxx
# However, this does not fork a new process, since ":" is a builtin.</programlisting>
See also <xref linkend="ex12">.</para>
<para>In combination with the <token>&gt;&gt;</token>
redirection operator, updates a file access/modification
time (<userinput>: &gt;&gt; new_file</userinput>).
If the file did not previously exist, creates it. This is
equivalent to <link linkend="touchref">touch</link>.</para>
<note><para>This applies to regular files, not pipes,
symlinks, and certain special files.</para></note>
<para>May be used to begin a comment line, although this is not
recommended. Using <token>#</token> for a comment turns
off error checking for the remainder of that line, so
almost anything may be appear in a comment. However,
this is not the case with
<token>:</token>.
<programlisting>: This is a comment that generates an error, ( if [ $x -eq 3] ).</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>!</token></term>
<indexterm>
<primary>!</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>!</secondary>
</indexterm>
<indexterm>
<primary>not</primary>
<secondary>logical</secondary>
</indexterm>
<indexterm>
<primary>not</primary>
</indexterm>
<listitem>
<para><anchor id="notref"></para>
<formalpara><title>reverse (or negate) the sense of
a test or exit status</title>
<para>The <token>!</token> operator inverts the <link
linkend="exitstatusref">exit status</link>
of the command to which it is applied (see
<xref linkend="negcond">). It also inverts
the meaning of a test operator. This can, for
example, change the sense of <quote>equal</quote>
( <link linkend="equalsignref">=</link>
) to <quote>not-equal</quote> ( != ). The
<token>!</token> operator is a Bash <link
linkend="keywordref">keyword</link>.</para>
</formalpara>
<para>In a different context, the <token>!</token>
also appears in <link linkend="ivr2">indirect variable
references</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*</token></term>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>*</secondary>
</indexterm>
<indexterm>
<primary>wild card</primary>
<secondary>globbing</secondary>
</indexterm>
<indexterm>
<primary>regular expression</primary>
</indexterm>
<listitem><formalpara><title>wild card</title>
<para>[asterisk] The <token>*</token> character serves
as a <quote>wild card</quote> for filename expansion in
<link linkend="globbingref">globbing</link>, as well as
representing any number (or zero) characters in a <link
linkend="regexref">regular expression</link>.</para>
</formalpara>
<para>A double asterisk, <token>**</token>, is the <link
linkend="exponentiationref">exponentiation
operator</link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>$</token></term>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<indexterm>
<primary>variable substitution</primary>
</indexterm>
<listitem>
<formalpara><title><link linkend="varsubn">Variable
substitution</link></title>
<para>
<programlisting>var1=5
var2=23skidoo
echo $var1 # 5
echo $var2 # 23skidoo</programlisting>
</para>
</formalpara>
<para>In a <link linkend="regexref">regular
expression</link>, a <token>$</token> <link
linkend="dollarsignref">matches the end of a
line</link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>${}</token></term>
<indexterm>
<primary>${}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>${}</secondary>
</indexterm>
<indexterm>
<primary>parameter substitution</primary>
</indexterm>
<listitem>
<formalpara><title><link linkend="paramsubref">Parameter
substitution</link></title>
<para></para></formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>$*</token></term>
<term><token>$@</token></term>
<indexterm>
<primary>$*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$*</secondary>
</indexterm>
<indexterm>
<primary>$@</primary>
<secondary>positional parameters</secondary>
</indexterm>
<indexterm>
<primary>$@</primary>
</indexterm>
<listitem><formalpara><title><link linkend="appref">positional
parameters</link></title>
<para></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>()</token></term>
<listitem><formalpara><title>command group</title>
<para><programlisting>(a=hello; echo $a)</programlisting></para>
</formalpara>
<important>
<para>A listing of commands within
<replaceable>parentheses</replaceable> starts a <link
linkend="subshellsref">subshell</link>.</para>
<para>Variables inside parentheses, within the subshell, are not
visible to the rest of the script. The parent process,
the script, <link linkend="parvis">cannot read variables
created in the child process</link>, the subshell.
<programlisting>a=123
( a=321; )
echo "a = $a" # a = 123
# "a" within parentheses acts like a local variable.</programlisting></para>
</important>
<formalpara><title>array initialization</title>
<para><programlisting>Array=(element1 element2 element3)</programlisting></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<indexterm>
<primary>{xxx,yyy,zzz..}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>brace expansion</primary>
</indexterm>
<term><token>{xxx,yyy,zzz,...}</token></term>
<listitem><formalpara><title>Brace expansion</title>
<para><programlisting>grep Linux file*.{txt,htm*}
# Finds all instances of the work "Linux"
# in the files "fileA.txt", "file2.txt", "fileR.html", "file-87.htm", etc.</programlisting></para>
</formalpara>
<para>A command may act upon a comma-separated list of file specs within
<replaceable>braces</replaceable>.
<footnote><para>The shell does the <emphasis>brace
expansion</emphasis>. The command itself acts upon the
<emphasis>result</emphasis> of the expansion.</para></footnote>
Filename expansion (<link linkend="globbingref">globbing</link>)
applies to the file specs between the braces.</para>
<caution>
<para>No spaces allowed within the braces
<emphasis>unless</emphasis> the spaces are quoted or escaped.</para>
<para><userinput>echo {file1,file2}\ :{\ A," B",' C'}</userinput></para>
<para><computeroutput>file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C</computeroutput></para>
</caution>
</listitem>
</varlistentry>
<varlistentry>
<term><token>{}</token></term>
<indexterm>
<primary>{}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>block of code</primary>
</indexterm>
<listitem>
<para><anchor id="codeblockref"></para>
<formalpara><title>Block of code</title>
<para>[curly brackets] Also referred to as an
<quote>inline group</quote>, this construct, in effect,
creates an anonymous function. However, unlike a <link
linkend="functionref">function</link>, the variables
in a code block remain visible to the remainder of the
script.</para></formalpara>
<para>
<screen><prompt>bash$ </prompt><userinput>{ local a; a=123; }</userinput>
<computeroutput>bash: local: can only be used in a function</computeroutput>
</screen>
</para>
<para><programlisting>a=123
{ a=321; }
echo "a = $a" # a = 321 (value inside code block)
# Thanks, S.C.</programlisting></para>
<para>The code block enclosed in braces may have <link
linkend="ioredirref">I/O redirected</link> to and from
it.</para>
<example id="ex8">
<title>Code blocks and I/O redirection</title>
<programlisting>&ex8;</programlisting>
</example>
<example id="rpmcheck">
<title>Saving the results of a code block to a file</title>
<programlisting>&rpmcheck;</programlisting>
</example>
<note><para>Unlike a command group within (parentheses),
as above, a code block enclosed by {braces} will
<emphasis>not</emphasis> normally launch a <link
linkend="subshellsref">subshell</link>.
<footnote><para>Exception: a code block in braces as
part of a pipe <emphasis>may</emphasis> be run as a
<link linkend="subshellsref">subshell</link>.
<programlisting>ls | { read firstline; read secondline; }
# Error. The code block in braces runs as a subshell,
# so the output of "ls" cannot be passed to variables within the block.
echo "First line is $firstline; second line is $secondline" # Will not work.
# Thanks, S.C.</programlisting></para></footnote>
</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><token>{} \;</token></term>
<listitem>
<formalpara><title>pathname</title>
<para>Mostly used in <link linkend="findref">find</link>
constructs. This is <emphasis>not</emphasis> a shell
<link linkend="builtinref">builtin</link>.</para>
</formalpara>
<note><para>The <quote><token>;</token></quote> ends
the <option>-exec</option> option of a
<command>find</command> command sequence. It needs
to be escaped to protect it from interpretation by the
shell.</para></note>
</listitem>
</varlistentry>
<varlistentry><term><token>[ ]</token></term>
<indexterm>
<primary>[]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[ ]</secondary>
</indexterm>
<indexterm>
<primary>test</primary>
</indexterm>
<listitem>
<formalpara><title>test</title>
<para></para></formalpara>
<para><anchor id="leftbracket"><link
linkend="ifthen">Test</link> expression between <command>[
]</command>. Note that <command>[</command> is part of
the shell builtin <command>test</command> (and a synonym
for it), <emphasis>not</emphasis> a link to the external
command <filename>/usr/bin/test</filename>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>[[ ]]</token></term>
<indexterm>
<primary>[[]]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[[ ]]</secondary>
</indexterm>
<indexterm>
<primary>test</primary>
</indexterm>
<listitem>
<formalpara><title>test</title>
<para></para></formalpara>
<para>Test expression between <token>[[ ]]</token> (shell
<link linkend="keywordref">keyword</link>).</para>
<para>See the discussion on the <link
linkend="dblbrackets">[[ ... ]] construct</link> for more
details.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>></token> <token>>&</token> <token>>></token> <token><</token></term>
<indexterm>
<primary>></primary>
</indexterm>
<indexterm>
<primary>>&</primary>
</indexterm>
<indexterm>
<primary>>></primary>
</indexterm>
<indexterm>
<primary><</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>></secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>>&</secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>>></secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary><</secondary>
</indexterm>
<indexterm>
<primary>redirection</primary>
</indexterm>
<listitem><formalpara><title><link linkend="ioredirref">redirection</link></title>
<para></para>
</formalpara>
<para><userinput>scriptname >filename</userinput> redirects the output of
<filename>scriptname</filename> to file
<filename>filename</filename>. Overwrite
<filename>filename</filename> if it already exists.</para>
<para><userinput>command >&2</userinput> redirects
output of <filename>command</filename> to
<filename>stderr</filename>.</para>
<para><userinput>scriptname >>filename</userinput> appends
the output of <filename>scriptname</filename>
to file <filename>filename</filename>. If
<filename>filename</filename> does not already exist,
it will be created.</para>
<formalpara><title><link linkend="processsubref">process substitution</link></title>
<para></para>
</formalpara>
<para><userinput>(command)></userinput></para>
<para><userinput><(command)</userinput></para>
<para><link linkend="ltref">In a different context</link>,
the <quote><token>&lt;</token></quote> and
<quote><token>&gt;</token></quote> characters act
as <link linkend="scomparison1">string comparison
operators</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token><<</token></term>
<listitem><formalpara><title>redirection used in a <link
linkend="heredocref">here document</link></title>
<para></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|</token></term>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>|</secondary>
</indexterm>
<indexterm>
<primary>pipe</primary>
</indexterm>
<listitem>
<para><anchor id="piperef"></para>
<formalpara><title>pipe</title>
<para>Passes the output of previous command to next one, or to shell.
This is a method of chaining commands together.</para>
</formalpara>
<para>
<programlisting>echo ls -l | sh
# Passes the output of "echo ls -l" to the shell,
# with the same result as a simple "ls -l".
cat *.lst | sort | uniq
# Sorts the output of all ".lst" files and deletes duplicate lines.</programlisting>
</para>
<para><anchor id="ucref">The output of a command or commands
may be piped to a script.
<programlisting>#!/bin/bash
# uppercase.sh : Changes input to uppercase.
tr 'a-z' 'A-Z'
# Letter ranges must be quoted
# to prevent filename generation from single-letter filenames.
exit 0</programlisting>
Now, let us pipe the output of <command>ls -l</command> to this
script.
<screen><prompt>bash$ </prompt><userinput>ls -l | ./uppercase.sh</userinput>
<computeroutput>-RW-RW-R-- 1 BOZO BOZO 109 APR 7 19:49 1.TXT
-RW-RW-R-- 1 BOZO BOZO 109 APR 14 16:48 2.TXT
-RW-R--R-- 1 BOZO BOZO 725 APR 20 20:56 DATA-FILE</computeroutput>
</screen>
</para>
<note><para>If one of the commands in the pipe
aborts, this prematurely terminates execution of the
pipe. Called a <emphasis>broken pipe</emphasis>, this
condition sends a <emphasis>SIGPIPE</emphasis> <link
linkend="signald">signal</link>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>|</token></term>
<indexterm>
<primary>>|</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>>|</secondary>
</indexterm>
<indexterm>
<primary>redirection</primary>
<secondary>force</secondary>
</indexterm>
<indexterm>
<primary>noclobber</primary>
</indexterm>
<listitem><formalpara><title>force redirection (even if
the <link linkend="noclobberref">noclobber option</link>
is set)</title>
<para>This will forcibly overwrite an existing file.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&amp;</token></term>
<listitem><formalpara><title>Run job in background</title>
<para>A command followed by an <token>&amp;</token>
will run in the background.</para>
</formalpara>
<para>
<screen><prompt>bash$ </prompt><userinput>sleep 10 &</userinput>
<computeroutput>[1] 850</computeroutput>
<computeroutput>[1]+ Done sleep 10</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<indexterm>
<primary>-</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>-</secondary>
</indexterm>
<indexterm>
<primary>redirection</primary>
<secondary>from/to stdin/stdout</secondary>
</indexterm>
<listitem><formalpara><title>redirection from/to <filename>stdin</filename> or <filename>stdout</filename></title>
<para><anchor id="coxex">[dash]</para>
</formalpara>
<para><programlisting>(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Move entire file tree from one directory to another
# [courtesy Alan Cox &lt;a.cox@swansea.ac.uk&gt;, with a minor change]
# 1) cd /source/directory Source directory, where the files to be moved are.
# 2) && "And-list": if the 'cd' operation successful, then execute the next command.
# 3) tar cf - . The 'c' option 'tar' archiving command creates a new archive,
# the 'f' (file) option, followed by '-' designates the target file as stdout,
# and do it in current directory tree ('.').
# 4) | Piped to...
# 5) ( ... ) a subshell
# 6) cd /dest/directory Change to the destination directory.
# 7) && "And-list", as above
# 8) tar xpvf - Unarchive ('x'), preserve ownership and file permissions ('p'),
# and send verbose messages to stdout ('v'),
# reading data from stdin ('f' followed by '-').
#
# Note that 'x' is a command, and 'p', 'v', 'f' are options.
# Whew!
# More elegant than, but equivalent to:
# cd source-directory
# tar cf - . | (cd ../target-directory; tar xzf -)
#
# cp -a /source/directory /dest also has same effect.
</programlisting></para>
<para><programlisting>bunzip2 linux-2.4.3.tar.bz2 | tar xvf -
# --uncompress tar file-- | --then pass it to "tar"--
# If "tar" has not been patched to handle "bunzip2",
# this needs to be done in two discrete steps, using a pipe.
# The purpose of the exercise is to unarchive "bzipped" kernel source.
</programlisting></para>
<para>Note that in this context the <quote>-</quote> is not
itself a Bash operator, but rather an option recognized by
certain UNIX utilities that write to
<filename>stdout</filename>, such as <command>tar</command>,
<command>cat</command>, etc.</para>
<para>Where a filename is expected,
<replaceable>-</replaceable> redirects output to
<filename>stdout</filename> (sometimes seen with
<userinput>tar cf</userinput>), or accepts input from
<filename>stdin</filename>, rather than from a file. This
is a method of using a file-oriented utility as a filter
in a pipe.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>file</userinput>
<computeroutput>Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...</computeroutput>
</screen>
By itself on the command line, <link
linkend="fileref">file</link> fails with an error message.
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>file -</userinput>
<userinput>#!/bin/bash</userinput>
<computeroutput>standard input: Bourne-Again shell script text executable</computeroutput>
</screen>
This time, it accepts input from <filename>stdin</filename>
and filters it.
</para>
<para>Try using <link linkend="diffref">diff</link> to
compare a file with a <emphasis>section</emphasis>
of another.</para>
<para><userinput>grep bash file1 | diff file2 -</userinput></para>
<para>Finally, a real-world example using
<replaceable>-</replaceable> with <link
linkend="tarref">tar</link>.</para>
<example id="ex58">
<title>Backup of all files changed in last day</title>
<programlisting>&ex58;</programlisting>
</example>
<caution>
<para>Filenames beginning with
<replaceable>-</replaceable> may cause problems when
coupled with the <replaceable>-</replaceable> redirection
operator. A script should check for this and pass such
filenames as <replaceable>./-FILENAME</replaceable> or
<replaceable>$PWD/-FILENAME</replaceable>.</para>
<para>If the value of a variable begins with a
<replaceable>-</replaceable>, this may likewise create
problems.
<programlisting>var="-n"
echo $var
# Has the effect of "echo -n", and outputs nothing.</programlisting>
</para>
</caution>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<listitem><formalpara><title>previous working directory</title>
<para>[dash] <command>cd -</command> changes to previous working
directory. This uses the <link
linkend="oldpwd">$OLDPWD</link> environmental
variable.</para>
</formalpara>
<caution><para>This is not to be confused with the
<quote>-</quote> redirection operator just discussed. The
interpretation of the <quote>-</quote> depends on the
context in which it appears.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<listitem><formalpara><title>Minus</title>
<para>Minus sign in an <link linkend="arops1">arithmetic
operation</link>.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>=</token></term>
<listitem><formalpara><title>Equals</title>
<para><link linkend="eqref">Assignment operator</link>
<programlisting>a=28
echo $a # 28</programlisting></para>
</formalpara>
<para>In a <link linkend="equalsignref">different context</link>,
the <quote><token>=</token></quote> is a <link
linkend="scomparison1">string comparison</link>
operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>+</token></term>
<listitem><formalpara><title>Plus</title>
<para>Addition <link linkend="arops1">arithmetic
operator</link>.</para>
</formalpara>
<para>In a <link linkend="plusref">different context</link>,
the <token>+</token> is a <link linkend="regexp">Regular
Expression</link> operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>%</token></term>
<listitem><formalpara><title><link linkend="moduloref">modulo</link></title>
<para>Modulo (remainder of a division) <link linkend="arops1">arithmetic
operation</link>.</para>
</formalpara>
<para>In a <link linkend="pctpatref">different context</link>,
the <token>%</token> is a <link linkend="psub2">pattern
matching</link> operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>~</token></term>
<listitem><formalpara><title>home directory</title>
<para>[tilde] This corresponds to the <link
linkend="homedirref">$HOME</link> internal variable.
<emphasis>~bozo</emphasis> is bozo's home directory,
and <command>ls ~bozo</command> lists the contents of it.
<token>~/</token> is the current user's home directory,
and <command>ls ~/</command> lists the contents of it.
<screen><prompt>bash$ </prompt><userinput>echo ~bozo</userinput>
<computeroutput>/home/bozo</computeroutput>
<prompt>bash$ </prompt><userinput>echo ~</userinput>
<computeroutput>/home/bozo</computeroutput>
<prompt>bash$ </prompt><userinput>echo ~/</userinput>
<computeroutput>/home/bozo/</computeroutput>
<prompt>bash$ </prompt><userinput>echo ~:</userinput>
<computeroutput>/home/bozo:</computeroutput>
<prompt>bash$ </prompt><userinput>echo ~nonexistent-user</userinput>
<computeroutput>~nonexistent-user</computeroutput>
</screen>
</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>~+</token></term>
<listitem><formalpara><title>current working directory</title>
<para>This corresponds to the <link
linkend="pwdref">$PWD</link> internal variable.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>~-</token></term>
<listitem><formalpara><title>previous working directory</title>
<para>This corresponds to the <link
linkend="oldpwd">$OLDPWD</link> internal variable.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term>Control Characters</term>
<listitem>
<para><anchor id="controlcharref"></para>
<formalpara><title> change the behavior of the
terminal or text display.</title>
<para>A control character is a <keycap>CONTROL</keycap>
+ <keycap>key</keycap> combination.</para>
</formalpara>
<itemizedlist id="ctlchar">
<listitem>
<para><userinput>Ctl-C</userinput></para>
<para>Terminate a foreground job.</para>
</listitem>
<listitem>
<para><userinput>Ctl-D</userinput></para>
<para>Log out from a shell (similar to
<link linkend="exitcommandref">exit</link>).</para>
</listitem>
<listitem>
<para><userinput>Ctl-G</userinput></para>
<para><quote>BEL</quote> (beep).</para>
</listitem>
<listitem>
<para><userinput>Ctl-H</userinput></para>
<para>Backspace.</para>
</listitem>
<listitem>
<para><userinput>Ctl-J</userinput></para>
<para>Carriage return.</para>
</listitem>
<listitem>
<para><userinput>Ctl-L</userinput></para>
<para>Formfeed (clear the terminal screen). This has
the same effect as the <link
linkend="clearref">clear</link> command.</para>
</listitem>
<listitem>
<para><userinput>Ctl-M</userinput></para>
<para>Newline.</para>
</listitem>
<listitem>
<para><userinput>Ctl-U</userinput></para>
<para>Erase a line of input.</para>
</listitem>
<listitem>
<para><userinput>Ctl-Z</userinput></para>
<para>Pause a foreground job.</para>
</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term>Whitespace</term>
<listitem>
<para><anchor id="whitespaceref"></para>
<formalpara><title>functions as a separator, separating commands or variables.</title>
<para>Whitespace consists of either spaces, tabs, blank
lines, or any combination thereof. In some contexts,
such as <link linkend="wsbad">variable assignment</link>,
whitespace is not permitted, and results in a syntax
error.</para>
</formalpara>
<para>Blank lines have no effect on the action of a script,
and are therefore useful for visually separating functional
sections.</para>
<para><link linkend="ifsref">$IFS</link>, the special variable
separating fields of input to certain commands, defaults
to whitespace.</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Special characters used in shell scripts -->
<chapter id="variables">
<title>Introduction to Variables and Parameters</title>
<para>Variables are at the heart of every programming and scripting
language. They appear in arithmetic operations and manipulation
of quantities, string parsing, and are indispensable for working
in the abstract with symbols - tokens that represent something
else. A variable is nothing more than a location or set of
locations in computer memory that holds an item of data.</para>
<sect1 id="varsubn">
<title>Variable Substitution</title>
<para>The <emphasis>name</emphasis> of a variable is a placeholder for
its <emphasis>value</emphasis>, the data it holds. Referencing its
value is called <emphasis>variable substitution</emphasis>.</para>
<variablelist id="dollarsign">
<varlistentry>
<term><token>$</token></term> <indexterm>
<primary>$</primary>
</indexterm> <indexterm>
<primary>variable</primary> <secondary>$</secondary>
</indexterm> <indexterm>
<primary>variable</primary>
<secondary>substitution</secondary>
</indexterm>
<listitem>
<para>Let us carefully distinguish between the
<emphasis>name</emphasis> of a variable
and its <emphasis>value</emphasis>. If
<userinput>variable1</userinput> is the name of a
variable, then <userinput>$variable1</userinput>
is a reference to its <emphasis>value</emphasis>,
the data item it contains. The only time a
variable appears <quote>naked</quote>, without the
<token>$</token> prefix, is when declared or assigned,
when <emphasis>unset</emphasis>, or when <link
linkend="exportref">exported</link>. Assignment
may be with an <token>=</token> (as in
<emphasis>var1=27</emphasis>), in a <link
linkend="readref">read</link> statement, and at the head
of a loop (<emphasis>for var2 in 1 2 3</emphasis>).</para>
<para><anchor id="dblquo">Enclosing a referenced value in
double quotes (<token>" "</token>) does not
interfere with variable substitution. This is called
partial quoting, sometimes referred to as <quote>weak
quoting</quote>. <anchor id="snglquo">Using single quotes
(<token>' '</token>) causes the variable name to be used
literally, and no substitution will take place. This is
full quoting, sometimes referred to as <quote>strong
quoting</quote>. See <xref linkend="quoting"> for a
detailed discussion.</para>
<para>Note that <userinput>$variable</userinput> is actually a
simplified alternate form of
<userinput>${variable}</userinput>. In contexts
where the <userinput>$variable</userinput> syntax
causes an error, the longer form may work (see <xref
linkend="Parameter-Substitution">, below).</para>
<example id="ex9">
<title>Variable assignment and substitution</title>
<programlisting>&ex9;</programlisting>
</example>
<warning><para>An uninitialized variable has a
<quote>null</quote> value - no assigned value at all
(not zero!). Using a variable before assigning a value
to it will inevitably cause problems.</para></warning>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Variable Substitution -->
<sect1 id="varassignment">
<title>Variable Assignment</title>
<variablelist>
<varlistentry>
<term><anchor id="eqref"><token>=</token></term>
<indexterm>
<primary>=</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>assignment</secondary>
</indexterm>
<listitem><para>the assignment operator (<emphasis>no space before &
after</emphasis>)</para>
<caution>
<para>Do not confuse this with <link
linkend="equalsignref">=</link> and <link
linkend="equalref">-eq</link>, which test, rather than
assign!</para>
<para>Note that <token>=</token> can be either an assignment
or a test operator, depending on context.</para>
</caution>
<example id="ex15">
<title>Plain Variable Assignment</title>
<programlisting>&ex15;</programlisting>
</example>
<example id="ex16">
<title>Variable Assignment, plain and fancy</title>
<programlisting>&ex16;</programlisting>
</example>
<para>Variable assignment using the <token>$(...)</token> mechanism
(a newer method than <link
linkend="backquotesref">backquotes</link>)</para>
<para><programlisting># From /etc/rc.d/rc.local
R=$(cat /etc/redhat-release)
arch=$(uname -m)</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Variable Assignment -->
<sect1 id="othertypesv">
<title>Special Variable Types</title>
<variablelist>
<varlistentry>
<term><replaceable>local variables</replaceable></term>
<indexterm>
<primary>variable</primary>
<secondary>local</secondary>
</indexterm>
<listitem><para>variables visible only within a <link
linkend="codeblockref">code block</link> or function
(see also <link linkend="localref">local variables</link>
in <link linkend="functionref">functions</link>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>environmental variables</replaceable></term>
<indexterm>
<primary>variable</primary>
<secondary>environmental</secondary>
</indexterm>
<listitem>
<para>variables that affect the behavior of the shell and
user interface</para>
<note>
<para>In a more general context, each process has an
<quote>environment</quote>, that is, a group of
variables that hold information that the process
may reference. In this sense, the shell behaves like
any other process.</para>
<para>Every time a shell starts, it creates shell variables that
correspond to its own environmental variables. Updating or
adding new shell variables causes the shell to update its
environment, and all the shell's child processes (the commands
it executes) inherit this environment.</para>
</note>
<caution>
<para>The space allotted to the environment is limited.
Creating too many environmental variables or ones that use up
excessive space may cause problems.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"</userinput>
<prompt>bash$ </prompt><userinput>du</userinput>
<computeroutput>bash: /usr/bin/du: Argument list too long</computeroutput>
</screen>
</para>
<para>(Thank you, S. C. for the clarification, and
for providing the above example.)</para>
</caution>
<para>If a script sets environmental variables, they need to be
<quote>exported</quote>, that is, reported to the
environment local to the script. This is the function of
the <link linkend="exportref">export</link> command.</para>
<note><para>A script can <command>export</command> variables only
to child processes, that is, only to commands or processes
which that particular script initiates. A script invoked
from the command line <replaceable>cannot</replaceable>
export variables back to the command line environment. <link
linkend="forkref">Child processes</link> cannot export
variables back to the parent processes that spawned
them.</para></note>
<para>---</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>positional parameters</replaceable></term>
<indexterm>
<primary>parameter</primary>
<secondary>positional</secondary>
</indexterm>
<listitem>
<para>arguments passed to the script from the command
line - $0, $1, $2, $3... $0 is the name of the script
itself, $1 is the first argument, $2 the second, $3 the
third, and so forth.
<footnote><para>The process calling the script sets the
<replaceable>$0</replaceable> parameter. By convention, this
parameter is the name of the script. See the manpage for
<command>execv</command>.</para></footnote>
After $9, the arguments must be enclosed in brackets,
for example, ${10}, ${11}, ${12}.</para>
<example id="ex17">
<title>Positional Parameters</title>
<programlisting>&ex17;</programlisting>
</example>
<para>Some scripts can perform different operations,
depending on which name they are invoked with. For this
to work, the script needs to check <varname>$0</varname>,
the name it was invoked by. There must also exist symbolic
links to all the alternate names of the script.</para>
<tip><para>If a script expects a command line parameter
but is invoked without one, this may cause a null variable
assignment, generally an undesirable result. One way to prevent
this is to append an extra character to both sides of the
assignment statement using the expected positional parameter.
</para></tip>
<programlisting>variable1x=$1x
# This will prevent an error, even if positional parameter is absent.
critical_argument01=$variable1x
# The extra character can be stripped off later, if desired, like so.
variable1=${variable1x/x/} # It works even if variable1x contains an 'x'.
# This uses one of the parameter substitution templates previously discussed.
# Leaving out the replacement pattern results in a deletion.
# A more straightforward way of dealing with this is to simply test
# to simply test whether expected positional parameters have been passed.
if [ -z $1 ]
then
exit $POS_PARAMS_MISSING
fi
</programlisting>
<para>---</para>
<example id="ex18">
<title><command>wh</command>, <link
linkend="whoisref">whois</link> domain name lookup</title>
<programlisting>&ex18;</programlisting>
</example>
<para>---</para>
<para>
<indexterm>
<primary>shift</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>shift</secondary>
</indexterm>
The <command>shift</command> command reassigns the positional
parameters, in effect shifting them to the left one notch.</para>
<para><varname>$1</varname> <--- <varname>$2</varname>, <varname>$2</varname> <--- <varname>$3</varname>, <varname>$3</varname> <--- <varname>$4</varname>, etc.</para>
<para>The old <varname>$1</varname> disappears, but
<varname>$0</varname> does not change. If you use a
large number of positional parameters to a script,
<command>shift</command> lets you access those past
<literal>10</literal>, although {bracket} notation also
permits this (see <xref linkend="ex17">).</para>
<example id="ex19">
<title>Using <command>shift</command></title>
<programlisting>&ex19;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Special Variable Types -->
</chapter> <!-- Variables -->
<chapter id="quoting">
<title>Quoting</title>
<para><anchor id="quotingref"></para>
<indexterm>
<primary>"</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>"</secondary>
</indexterm>
<indexterm>
<primary>'</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>'</secondary>
</indexterm>
<indexterm>
<primary>quote</primary>
</indexterm>
<indexterm>
<primary>\</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>\</secondary>
</indexterm>
<indexterm>
<primary>escape</primary>
</indexterm>
<para>Quoting means just that, bracketing a string in quotes. This
has the effect of protecting special characters in the string from
reinterpretation or expansion by the shell or shell script. (A character
is <quote>special</quote> if it has an interpretation other than its
literal meaning, such as the <token>wild card</token> character,
<token>*</token>.)</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ls -l [Vv]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT
-rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh
-rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh
</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l '[Vv]*'</userinput>
<computeroutput>ls: [Vv]*: No such file or directory</computeroutput></screen>
</para>
<note>
<para>Certain programs and utilities can still reinterpret or expand
special characters in a quoted string. This is an important use
of quoting, protecting a command-line parameter from the shell,
but still letting the calling program expand it.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep '[Ff]irst' *.txt</userinput>
<computeroutput>file1.txt:This is the first line of file1.txt.
file2.txt:This is the First line of file2.txt.</computeroutput></screen>
</para>
<para>Of course, <userinput>grep [Ff]irst *.txt</userinput>
would not work.</para>
</note>
<para>When referencing a variable, it is generally advisable in
enclose it in double quotes (<token>" "</token>). This
preserves all special characters within the variable name,
except <token>$</token>, <token>`</token> (backquote),
and <token>\</token> (escape). Keeping <token>$</token>
as a special character permits referencing a quoted variable
(<replaceable>"$variable"</replaceable>), that is, replacing the
variable with its value (see <xref linkend="ex9">, above).</para>
<para>Use double quotes to prevent word splitting.
<footnote><para><quote>Word splitting</quote>, in this context,
means dividing a character string into a number of separate and
discrete arguments.</para></footnote>
An argument enclosed in double quotes presents
itself as a single word, even if it contains <link
linkend="whitespaceref">whitespace</link> separators.
<programlisting>variable1="a variable containing five words"
COMMAND This is $variable1 # Executes COMMAND with 7 arguments:
# "This" "is" "a" "variable" "containing" "five" "words"
COMMAND "This is $variable1" # Executes COMMAND with 1 argument:
# "This is a variable containing five words"
variable2="" # Empty.
COMMAND $variable2 $variable2 $variable2 # Executes COMMAND with no arguments.
COMMAND "$variable2" "$variable2" "$variable2" # Executes COMMAND with 3 empty arguments.
COMMAND "$variable2 $variable2 $variable2" # Executes COMMAND with 1 argument (2 spaces).
# Thanks, S.C.
</programlisting></para>
<tip><para>Enclosing the arguments to an <command>echo</command>
statement in double quotes is necessary only when word splitting
is an issue.</para></tip>
<example id="weirdvars">
<title>Echoing Weird Variables</title>
<programlisting>&weirdvars;</programlisting>
</example>
<para>Single quotes (<token>' '</token>) operate similarly to double
quotes, but do not permit referencing variables, since
the special meaning of <token>$</token> is turned off.
Within single quotes, <emphasis>every</emphasis> special
character except <token>'</token> gets interpreted literally.
Consider single quotes (<quote>full quoting</quote>) to be a
stricter method of quoting than double quotes (<quote>partial
quoting</quote>).</para>
<note><para>Since even the escape character (<token>\</token>)
gets a literal interpretation within single quotes, trying to
enclose a single quote within single quotes will not yield the
expected result.
<programlisting>echo "Why can't I write 's between single quotes"
echo
# The roundabout method.
echo 'Why can'\''t I write '"'"'s between single quotes'
# |-------| |----------| |-----------------------|
# Three single-quoted strings, with escaped and quoted single quotes between.
# This example courtesy of Stephane Chazelas.</programlisting>
</para></note>
<para><anchor id="escp"><firstterm>Escaping</firstterm> is a method
of quoting single characters. The <token>escape</token>
(<token>\</token>) preceding a character tells the shell to
interpret that character literally.</para>
<caution><para>With certain commands and utilities, such as <link
linkend="echoref">echo</link> and <link
linkend="sedref">sed</link>, escaping a character may have the
opposite effect - it can toggle on a special meaning for that
character.</para></caution>
<variablelist id="specialmeanings">
<title><anchor id="spm">Special meanings of certain
escaped characters</title>
<varlistentry>
<term>used with <command>echo</command> and
<command>sed</command></term>
<listitem><para></para></listitem>
</varlistentry>
<varlistentry><term><token>\n</token></term>
<indexterm>
<primary>\n</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\n</secondary>
</indexterm>
<indexterm>
<primary>newline</primary>
</indexterm>
<listitem><para>means newline</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\r</token></term>
<indexterm>
<primary>\r</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\r</secondary>
</indexterm>
<indexterm>
<primary>carriage return</primary>
</indexterm>
<listitem><para>means return</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\t</token></term>
<indexterm>
<primary>\t</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\t</secondary>
</indexterm>
<indexterm>
<primary>tabulation</primary>
</indexterm>
<listitem><para>means tab</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\v</token></term>
<indexterm>
<primary>\v</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\v</secondary>
</indexterm>
<indexterm>
<primary>vertical tabulation</primary>
</indexterm>
<listitem><para> means vertical tab</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\b</token></term>
<indexterm>
<primary>\b</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\b</secondary>
</indexterm>
<indexterm>
<primary>backspace</primary>
</indexterm>
<listitem><para>means backspace</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\a</token></term>
<indexterm>
<primary>\a</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\a</secondary>
</indexterm>
<indexterm>
<primary>alert</primary>
</indexterm>
<indexterm>
<primary>beep</primary>
</indexterm>
<indexterm>
<primary>flash</primary>
</indexterm>
<listitem><para>means <quote>alert</quote> (beep or flash)</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\0xx</token></term>
<indexterm>
<primary>\0xx</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\0xx</secondary>
</indexterm>
<indexterm>
<primary>octal ASCII</primary>
</indexterm>
<listitem><para>translates to the octal ASCII
equivalent of <replaceable>0xx</replaceable></para>
<example id="escaped">
<title>Escaped Characters</title>
<programlisting>&escaped;</programlisting>
</example>
<para>See <xref linkend="ex77"> for another example of the
<userinput>$' '</userinput> string expansion
construct.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\"</token></term>
<indexterm>
<primary>\"</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\"</secondary>
</indexterm>
<indexterm>
<primary>quote</primary>
</indexterm>
<listitem><para> gives the quote its literal meaning</para>
<para><programlisting>echo "Hello" # Hello
echo "\"Hello\", he said." # "Hello", he said.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry><term><token>\$</token></term>
<indexterm>
<primary>\$</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\$</secondary>
</indexterm>
<indexterm>
<primary>dollar</primary>
</indexterm>
<listitem><para>gives the dollar sign its literal meaning
(variable name following <token>\$</token> will not be
referenced)</para>
<para><programlisting>echo "\$variable01" # results in $variable01</programlisting></para>
</listitem>
</varlistentry>
<varlistentry><term><token>\\</token></term>
<indexterm>
<primary>\\</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\\</secondary>
</indexterm>
<indexterm>
<primary>double backslash</primary>
</indexterm>
<listitem><para>gives the backslash its literal meaning</para>
<para><programlisting>echo "\\" # results in \</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
<note><para>The behavior of <token>\</token> depends on whether
it is itself escaped, quoted, or appearing within a <link
linkend="heredocref">here document</link>.
<programlisting>echo \z # z
echo \\z # \z
echo '\z' # \z
echo '\\z' # \\z
echo "\z" # \z
echo "\\z" # \z
echo `echo \z` # z
echo `echo \\z` # z
echo `echo \\\z` # \z
echo `echo \\\\z` # \z
echo `echo \\\\\\z` # \z
echo `echo \\\\\\\z` # \\z
echo `echo "\z"` # \z
echo `echo "\\z"` # \z
cat &lt;&lt;EOF
\z
EOF # \z
cat &lt;&lt;EOF
\\z
EOF # \z
# These examples supplied by Stephane Chazelas.</programlisting>
</para></note>
<para>Escaping a space can prevent word splitting in a command's argument list.
<programlisting>file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
# List of files as argument(s) to a command.
# Add two files to the list, and list all.
ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
echo "-------------------------------------------------------------------------"
# What happens if we escape a couple of spaces?
ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
# Error: the first three files concatenated into a single argument to 'ls -l'
# because the two escaped spaces prevent argument (word) splitting.</programlisting>
</para>
<para>The <token>escape</token> also provides a means of writing a
multi-line command. Normally, each separate line constitutes
a different command, but an <token>escape</token> at the end
of a line <emphasis>escapes the newline character</emphasis>,
and the command sequence continues on to the next line.</para>
<para><programlisting>(cd /source/directory && tar cf - . ) | \
(cd /dest/directory && tar xpvf -)
# Repeating Alan Cox's directory tree copy command,
# but split into two lines for increased legibility.
# As an alternative:
tar cf - -C /source/directory |
tar xpvf - -C /dest/directory
# See note below.
# (Thanks, Stephane Chazelas.)</programlisting>
<note><para>If a script line ends with a <token>|</token>, a pipe
character, then a <token>\</token>, an escape, is not strictly
necessary. It is, however, good programming practice to always
escape the end of a line of code that continues to the
following line.</para></note></para>
<para><programlisting>echo "foo
bar"
#foo
#bar
echo
echo 'foo
bar' # No difference yet.
#foo
#bar
echo
echo foo\
bar # Newline escaped.
#foobar
echo
echo "foo\
bar" # Same here, as \ still interpreted as escape within weak quotes.
#foobar
echo
echo 'foo\
bar' # Escape character \ taken literally because of strong quoting.
#foor\
#bar
# Examples suggested by Stephane Chazelas.</programlisting></para>
</chapter> <!-- Quoting -->
<chapter id="tests">
<title>Tests</title>
<para><anchor id="ifthen"></para>
<indexterm>
<primary>if</primary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>if</secondary>
</indexterm>
<indexterm>
<primary>then</primary>
</indexterm>
<indexterm>
<primary>else if</primary>
</indexterm>
<indexterm>
<primary>elif</primary>
</indexterm>
<para>Every reasonably complete programming language can test
for a condition, then act according to the result of
the test. Bash has the <command>test</command> command,
various bracket and parenthesis operators, and the
<command>if/then</command> construct.</para>
<sect1 id="testconstructs">
<title>Test Constructs</title>
<itemizedlist id="testingref">
<listitem>
<para>An <command>if/then</command> construct tests whether the
<link linkend="exitstatusref">exit status</link> of a list
of commands is <returnvalue>0</returnvalue> (since 0 means
<quote>success</quote> by UNIX convention), and if so, executes
one or more commands.</para>
</listitem>
<listitem>
<para>There exists a dedicated command called <command>
[</command> (<link linkend="leftbracket">left bracket</link>
special character). It is a synonym for <command>test</command>,
and a <link linkend="builtinref">builtin</link> for efficiency
reasons. This command considers its arguments as comparison
expressions or file tests and returns an exit status corresponding
to the result of the comparison (0 for true, 1 for false).</para>
</listitem>
<listitem>
<para>With version 2.02, Bash introduced the <link
linkend="dblbrackets">[[ ... ]]</link> <emphasis>extended
test command</emphasis>, which performs comparisons
in a manner more familiar to programmers from other
languages. Note that <command>[[</command> is a <link
linkend="keywordref">keyword</link>, not a command.</para>
<para>Bash sees <userinput>[[ $a -lt $b ]]</userinput> as a
single element, which returns an exit status.</para>
<para>The <link linkend="dblparens">(( ... ))</link> and <link
linkend="letref">let ...</link> constructs also return an
exit status of <returnvalue>0</returnvalue> if the arithmetic
expressions they evaluate expand to a non-zero value. These
<link linkend="arithexpref">arithmetic expansion</link>
constructs may therefore be used to perform arithmetic
comparisons.
<programlisting>let "1&lt;2" returns 0 (as "1&lt;2" expands to "1")
(( 0 && 1 )) returns 1 (as "0 && 1" expands to "0")</programlisting>
</para>
</listitem>
<listitem>
<para>An <command>if</command> can test any command, not just
conditions enclosed within brackets.
<programlisting>if cmp a b &gt; /dev/null # Suppress output.
then echo "Files a and b are identical."
else echo "Files a and b differ."
fi
if grep -q Bash file
then echo "File contains at least one occurrence of Bash."
fi
if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
then echo "Command succeeded."
else echo "Command failed."
fi</programlisting>
</para>
</listitem>
<listitem>
<para>An <command>if/then</command> construct can contain nested
comparisons and tests.
<programlisting>if echo "Next *if* is part of the comparison for the first *if*."
if [[ $comparison = "integer" ]]
then (( a < b ))
else
[[ $a < $b ]]
fi
then
echo '$a is less than $b'
fi</programlisting>
</para>
<para><emphasis>This detailed <quote>if-test</quote> explanation
courtesy of Stephane Chazelas.</emphasis></para>
</listitem>
</itemizedlist>
<example id="ex10">
<title>What is truth?</title>
<programlisting>&ex10;</programlisting>
</example>
<formalpara><title>Exercise</title>
<para>Explain the behavior of <xref linkend="ex10">, above.</para>
</formalpara>
<para><programlisting>if [ condition-true ]
then
command 1
command 2
...
else
# Optional (may be left out if not needed).
# Adds default code block executing if original condition tests false.
command 3
command 4
...
fi</programlisting>
</para>
<para>Add a semicolon when 'if' and 'then' are on same line.</para>
<para><programlisting>if [ -x "$filename" ]; then</programlisting></para>
<variablelist id="elifref">
<title><anchor id="elifref1">Else if and elif</title>
<varlistentry>
<term><token>elif</token></term>
<listitem>
<para><userinput>elif</userinput> is a contraction
for <token>else if</token>. The effect is to nest an
inner <token>if/then</token> construct within an outer
one.</para>
<para><programlisting>if [ condition1 ]
then
command1
command2
command3
elif [ condition2 ]
# Same as else if
then
command4
command5
else
default-command
fi</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
<indexterm>
<primary>test</primary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>test</secondary>
</indexterm>
<indexterm>
<primary>[</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[</secondary>
</indexterm>
<indexterm>
<primary>]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>]</secondary>
</indexterm>
The <userinput>if test condition-true</userinput> construct is the
exact equivalent of <userinput>if [ condition-true ]</userinput>.
As it happens, the left bracket, <command>[</command> , is a token
which invokes the <command>test</command> command. The closing
right bracket, <command>]</command> , in an if/test should not
therefore be strictly necessary, however newer versions of Bash
require it.</para>
<note><para>The <command>test</command> command is a Bash <link
linkend="builtinref">builtin</link> which tests file
types and compares strings. Therefore, in a Bash script,
<command>test</command> does <emphasis>not</emphasis> call
the external <filename>/usr/bin/test</filename> binary,
which is part of the <emphasis>sh-utils</emphasis>
package. Likewise, <token>[</token> does not call
<filename>/usr/bin/[</filename>, which is linked to
<filename>/usr/bin/test</filename>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>type test</userinput>
<computeroutput>test is a shell builtin</computeroutput>
<prompt>bash$ </prompt><userinput>type '['</userinput>
<computeroutput>[ is a shell builtin</computeroutput>
<prompt>bash$ </prompt><userinput>type '[['</userinput>
<computeroutput>[[ is a shell keyword</computeroutput>
<prompt>bash$ </prompt><userinput>type ']]'</userinput>
<computeroutput>]] is a shell keyword</computeroutput>
<prompt>bash$ </prompt><userinput>type ']'</userinput>
<computeroutput>bash: type: ]: not found</computeroutput>
</screen>
</para></note>
<example id="ex11">
<title>Equivalence of <token>[ ]</token> and <token>test</token></title>
<programlisting>&ex11;</programlisting>
</example>
<indexterm>
<primary>test</primary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>test</secondary>
</indexterm>
<indexterm>
<primary>[[</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[[</secondary>
</indexterm>
<indexterm>
<primary>]]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>]]</secondary>
</indexterm>
<para><anchor id="dblbrackets">The <token>[[ ]]</token> construct
is the shell equivalent of <token>[ ]</token>. This is the
<emphasis>extended test command</emphasis>, adopted from
<emphasis>ksh88</emphasis>.</para>
<note><para>No filename expansion or word splitting takes place
between <token>[[</token> and <token>]]</token>, but there is
parameter expansion and command substitution.</para></note>
<para>
<programlisting>file=/etc/passwd
if [[ -e $file ]]
then
echo "Password file exists."
fi</programlisting>
</para>
<tip>
<para>Using the <command>[[ ... ]]</command> test construct,
rather than <command>[ ... ]</command> can prevent many
logic errors in scripts. For example, The <token>&&</token>,
<token>||</token>, <token>&lt;</token>, and <token>&gt;</token>
operators work within a <token>[[ ]]</token> test, despite
giving an error within a <token>[ ]</token> construct.</para>
</tip>
<note>
<para>Following an <command>if</command>, neither the
<command>test</command> command nor the test brackets ( [ ] or [[ ]] )
are strictly necessary.
<programlisting>dir=/home/bozo
if cd "$dir" 2&gt;/dev/null; then # "2&gt;/dev/null" hides error message.
echo "Now in $dir."
else
echo "Can't change to $dir."
fi</programlisting>
The "if COMMAND" construct returns the exit status of COMMAND.
</para>
<para>Similarly, a condition within test brackets may stand alone
without an <command>if</command>, when used in combination
with a <link linkend="listconsref">list construct</link>.
<programlisting>var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
home=/home/bozo
[ -d "$home" ] || echo "$home directory does not exist."</programlisting></para>
</note>
<indexterm>
<primary>test</primary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>test</secondary>
</indexterm>
<indexterm>
<primary>((</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>))</secondary>
</indexterm>
<indexterm>
<primary>((</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>))</secondary>
</indexterm>
<para>The <link linkend="dblparens">(( )) construct</link> expands
and evaluates an arithmetic expression. If
the expression evaluates as zero, it returns an
<link linkend="exitstatusref">exit status</link> of
<returnvalue>1</returnvalue>, or <quote>false</quote>. A non-zero
expression returns an exit status of <returnvalue>0</returnvalue>,
or <quote>true</quote>. This is in marked contrast to using
the <command>test</command> and <token>[ ]</token> constructs
previously discussed.</para>
<example id="arithtests">
<title>Arithmetic Tests using <token>(( ))</token></title>
<programlisting>&arithtests;</programlisting>
</example>
</sect1> <!-- Test Constructs -->
<sect1 id="fto">
<title>File test operators</title>
<variablelist>
<title><anchor id="rtif">Returns true if...</title>
<varlistentry>
<term><token>-e</token></term>
<listitem><para>file exists</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-f</token></term>
<listitem><para>file is a <replaceable>regular</replaceable>
file (not a directory or device file)</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-s</token></term>
<listitem><para>file is not zero size</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-d</token></term>
<listitem><para>file is a directory</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-b</token></term>
<listitem><para>file is a block device (floppy, cdrom, etc.)
</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-c</token></term>
<listitem><para>file is a character device (keyboard, modem, sound
card, etc.)</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-p</token></term>
<listitem><para>file is a pipe</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-h</token></term>
<listitem><para>file is a symbolic link</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-L</token></term>
<listitem><para>file is a symbolic link</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-S</token></term>
<listitem><para>file is a socket</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-t</token></term>
<listitem>
<para>file (<link linkend="fdref">descriptor</link>) is
associated with a terminal device</para>
<para>This test option may be used to check whether the
<filename>stdin</filename> (<userinput>[ -t 0 ]</userinput>)
or <filename>stdout</filename> (<userinput>[ -t 1 ]</userinput>)
in a given script is a terminal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-r</token></term>
<listitem><para>file has read permission (<emphasis>for the
user running the test</emphasis>)</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-w</token></term>
<listitem><para>file has write permission (for the user running
the test)</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-x</token></term>
<listitem><para>file has execute permission (for the user running
the test)</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-g</token></term>
<listitem>
<para>set-group-id (sgid) flag set on file or directory</para>
<para>If a directory has the <replaceable>sgid</replaceable>
flag set, then a file created within that directory belongs
to the group that owns the directory, not necessarily to
the group of the user who created the file. This may be
useful for a directory shared by a workgroup.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-u</token></term>
<listitem>
<para>set-user-id (suid) flag set on file</para>
<para>A binary owned by <emphasis>root</emphasis>
with <replaceable>set-user-id</replaceable> flag set
runs with <emphasis>root</emphasis> privileges, even
when an ordinary user invokes it.
<footnote><para>Be aware that <emphasis>suid</emphasis>
binaries may open security holes and that the
<emphasis>suid</emphasis> flag has no effect on
shell scripts.</para></footnote>
This is useful for executables (such as
<command>pppd</command> and <command>cdrecord</command>)
that need to access system hardware. Lacking the
<emphasis>suid</emphasis> flag, these binaries could not
be invoked by a non-root user.
<screen>
<computeroutput>-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd</computeroutput>
</screen>
A file with the <replaceable>suid</replaceable> flag set shows
an <emphasis>s</emphasis> in its permissions.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-k</token></term>
<listitem>
<para><replaceable>sticky bit</replaceable> set</para>
<para>Commonly known as the <quote>sticky bit</quote>, the
<emphasis>save-text-mode</emphasis> flag is a special
type of file permission. If a file has this flag set,
that file will be kept in cache memory, for quicker access.
<footnote><para>On modern UNIX systems, the sticky
bit is no longer used for files, only on
directories.</para></footnote>
If set on a directory, it restricts write permission.
Setting the sticky bit adds a <emphasis>t</emphasis>
to the permissions on the file or directory listing.
<screen>
<computeroutput>drwxrwxrwt 7 root 1024 May 19 21:26 tmp/</computeroutput>
</screen>
If a user does not own a directory that has the sticky
bit set, but has write permission in that directory,
he can only delete files in it that he owns. This keeps
users from inadvertently overwriting or deleting each
other's files in a publicly accessible directory, such
as <filename class="directory">/tmp</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-O</token></term>
<listitem><para>you are owner of file</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-G</token></term>
<listitem><para>group-id of file same as yours</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-N</token></term>
<listitem><para>file modified since it was last read</para></listitem>
</varlistentry>
<varlistentry>
<term><token>f1 -nt f2</token></term>
<listitem><para>file <replaceable>f1</replaceable> is newer than
<replaceable>f2</replaceable></para></listitem>
</varlistentry>
<varlistentry>
<term><token>f1 -ot f2</token></term>
<listitem><para>file <replaceable>f1</replaceable> is older than
<replaceable>f2</replaceable></para></listitem>
</varlistentry>
<varlistentry>
<term><token>f1 -ef f2</token></term>
<listitem><para>files <replaceable>f1</replaceable> and
<replaceable>f2</replaceable> are hard links to the same
file</para></listitem>
</varlistentry>
<varlistentry>
<term><token>!</token></term>
<listitem><para><quote>not</quote> -- reverses the sense of the
tests above (returns true if condition absent).</para></listitem>
</varlistentry>
</variablelist>
<para><xref linkend="cookies">, <xref linkend="bingrep">,
<xref linkend="fileinfo">, and <xref linkend="mailformat">
illustrate uses of the file test operators.</para>
</sect1> <!-- File test operators -->
<sect1 id="comparison-ops">
<title>Comparison operators (binary)</title>
<variablelist id="icomparison">
<title><anchor id="icomparison1">integer comparison</title>
<varlistentry>
<term><token>-eq</token></term>
<listitem>
<para><anchor id="equalref"></para>
<para>is equal to</para>
<para><userinput>if [ "$a" -eq "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-ne</token></term>
<listitem>
<para>is not equal to</para>
<para><userinput>if [ "$a" -ne "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-gt</token></term>
<listitem>
<para>is greater than</para>
<para><userinput>if ["$a" -gt "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-ge</token></term>
<listitem>
<para>is greater than or equal to</para>
<para><userinput>if [ "$a" -ge "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-lt</token></term>
<listitem>
<para>is less than</para>
<para><userinput>if [ "$a" -lt "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-le</token></term>
<listitem>
<para>is less than or equal to</para>
<para><userinput>if [ "$a" -le "$b" ]</userinput></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="scomparison">
<title><anchor id="scomparison1">string comparison</title>
<varlistentry>
<term><token>=</token></term>
<listitem>
<para><anchor id="equalsignref"></para>
<para>is equal to</para>
<para><userinput>if [ "$a" = "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>==</token></term>
<listitem>
<para>is equal to</para>
<para><userinput>if [ "$a" == "$b" ]</userinput></para>
<para>This is a synonym for <token>=</token>.</para>
<para>
<programlisting>[[ $a == z* ]] # true if $a starts with an "z" (pattern matching)
[[ $a == "z*" ]] # true if $a is equal to z*
[ $a == z* ] # file globbing and word splitting take place
[ "$a" == "z*" ] # true if $a is equal to z*
# Thanks, S.C.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>!=</token></term>
<listitem>
<para>is not equal to</para>
<para><userinput>if [ "$a" != "$b" ]</userinput></para>
<para>This operator uses pattern matching within a <link
linkend="dblbrackets">[[ ... ]]</link> construct.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ltref"><token>&lt;</token></term>
<listitem>
<para>is less than, in ASCII alphabetical order</para>
<para><userinput>if [[ "$a" &lt; "$b" ]]</userinput></para>
<para><userinput>if [ "$a" \&lt; "$b" ]</userinput></para>
<para>Note that the <quote>&lt;</quote> needs to be
escaped within a <userinput>[ ]</userinput>
construct.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gtref"><token>&gt;</token></term>
<listitem>
<para>is greater than, in ASCII alphabetical order</para>
<para><userinput>if [[ "$a" &gt; "$b" ]]</userinput></para>
<para><userinput>if [ "$a" \&gt; "$b" ]</userinput></para>
<para>Note that the <quote>&gt;</quote> needs to be
escaped within a <userinput>[ ]</userinput> construct.</para>
<para>See <xref linkend="bubble"> for an application of this
comparison operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-z</token></term>
<listitem><para>string is <quote>null</quote>, that is, has zero length</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-n</token></term>
<listitem>
<para>string is not <quote>null</quote>.</para>
<caution><para>The <userinput>-n</userinput> test absolutely
requires that the string be quoted within the
test brackets. Using an unquoted string with
<userinput>! -z</userinput>, or even just the
unquoted string alone within test brackets (see <xref
linkend="strtest">) normally works, however, this is
an unsafe practice. <emphasis>Always</emphasis> quote
a tested string.
<footnote><para>As S.C. points out, in a compound test,
even quoting the string variable might not
suffice. <userinput>[ -n "$string" -o "$a" =
"$b" ]</userinput> may cause an error with some
versions of Bash if <varname>$string</varname>
is empty. The safe way is to append an extra
character to possibly empty variables, <userinput>[
"x$string" != x -o "x$a" = "x$b" ]</userinput>
(the <quote>x's</quote> cancel out).</para></footnote>
</para></caution>
</listitem>
</varlistentry>
</variablelist>
<example id="ex13">
<title>arithmetic and string comparisons</title>
<programlisting>&ex13;</programlisting>
</example>
<example id="strtest">
<title>testing whether a string is <emphasis>null</emphasis></title>
<programlisting>&strtest;</programlisting>
</example>
<example id="ex14">
<title><command>zmost</command></title>
<programlisting>&ex14;</programlisting>
</example>
<variablelist id="ccomparison">
<title><anchor id="ccomparison1">compound comparison</title>
<varlistentry>
<term><token>-a</token></term>
<listitem>
<para>logical and</para>
<para><replaceable>exp1 -a exp2</replaceable> returns true if
<emphasis>both</emphasis> exp1 and exp2 are true.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-o</token></term>
<listitem>
<para>logical or </para>
<para><replaceable>exp1 -o exp2</replaceable> returns
true if either exp1 <emphasis>or</emphasis> exp2 are
true.</para>
</listitem>
</varlistentry>
</variablelist>
<para>These are similar to the Bash comparison operators
<command>&&</command> and <command>||</command>, used
within <link linkend="dblbrackets">double brackets</link>.
<programlisting>[[ condition1 && condition2 ]]</programlisting>
The <command>-o</command> and <command>-a</command> operators
work with the <command>test</command> command or occur within
single test brackets.
<programlisting>if [ "$exp1" -a "$exp2" ]</programlisting>
</para>
<para>Refer to <xref linkend="andor"> and <xref linkend="twodim">
to see compound comparison operators in action.</para>
</sect1> <!-- Comparison operators (binary) -->
<sect1 id="nestedifthen">
<title>Nested if/then Condition Tests</title>
<para>Condition tests using the <command>if/then</command>
construct may be nested. The net result is identical to using the
<command>&&</command> compound comparison operator above.</para>
<para><programlisting>if [ condition1 ]
then
if [ condition2 ]
then
do-something # But only if both "condition1" and "condition2" valid.
fi
fi</programlisting></para>
<para>See <xref linkend="ex79"> for an example of nested
<replaceable>if/then</replaceable> condition tests.</para>
</sect1> <!-- Nested if/then Tests -->
</chapter> <!-- Tests -->
<chapter id="operations">
<title>Operations and Related Topics</title>
<sect1 id="ops">
<title>Operators</title>
<variablelist id="asnop">
<title><anchor id="asnop1">assignment</title>
<varlistentry>
<term><replaceable>variable assignment</replaceable></term>
<listitem><para>Initializing or changing the value of a variable</para>
</listitem>
</varlistentry>
<varlistentry>
<indexterm>
<primary>=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>=</secondary>
</indexterm>
<term>=</term>
<listitem>
<para>All-purpose assignment operator, which works for both
arithmetic and string assignments.</para>
<para>
<programlisting>var=27
category=minerals # No spaces allowed after the "=".</programlisting>
</para>
<caution>
<para>Do not confuse the <quote>=</quote> assignment
operator with the <link linkend="equalsignref">=</link> test
operator.</para>
<para>
<programlisting># = as a test operator
if [ "$string1" = "$string2" ]
# if [ "Xstring1" = "Xstring2" ] is safer,
# to prevent an error message should one of the variables be empty.
# (The prepended "X" characters cancel out.)
then
command
fi</programlisting>
</para>
</caution>
</listitem>
</varlistentry>
</variablelist>
<indexterm>
<primary>expr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>expr</secondary>
</indexterm>
<indexterm>
<primary>let</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>let</secondary>
</indexterm>
<variablelist id="arops">
<title><anchor id="arops1">arithmetic operators</title>
<varlistentry>
<term><token>+</token></term>
<indexterm>
<primary>+</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>+</secondary>
</indexterm>
<indexterm>
<primary>addition</primary>
</indexterm>
<indexterm>
<primary>plus</primary>
</indexterm>
<listitem><para>plus</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<indexterm>
<primary>-</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>-</secondary>
</indexterm>
<indexterm>
<primary>subtraction</primary>
</indexterm>
<indexterm>
<primary>minus</primary>
</indexterm>
<listitem><para>minus</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*</token></term>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>*</secondary>
</indexterm>
<indexterm>
<primary>multiplication</primary>
</indexterm>
<listitem><para>multiplication</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/</token></term>
<indexterm>
<primary>/</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>/</secondary>
</indexterm>
<indexterm>
<primary>division</primary>
</indexterm>
<listitem><para>division</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exponentiationref"><token>**</token></term>
<indexterm>
<primary>**</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>**</secondary>
</indexterm>
<indexterm>
<primary>exponentiation</primary>
</indexterm>
<listitem>
<para>exponentiation
<programlisting># Bash, version 2.02, introduced the "**" exponentiation operator.
let "z=5**3"
echo "z = $z" # z = 125</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="moduloref"><token>%</token></term>
<indexterm>
<primary>%</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>%</secondary>
</indexterm>
<indexterm>
<primary>modulo</primary>
</indexterm>
<listitem>
<para>modulo, or mod (returns the remainder of an integer
division operation)</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo `expr 5 % 3`</userinput>
<computeroutput>2</computeroutput>
</screen>
</para>
<para>This operator finds use in, among other things,
generating numbers within a specific range (see <xref
linkend="ex21"> and <xref linkend="randomtest">)
and formatting program output (see <xref
linkend="qfunction">). It can even be used to generate prime
numbers, (see <xref linkend="primes">).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>+=</token></term>
<indexterm>
<primary>+=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>+=</secondary>
</indexterm>
<indexterm>
<primary>plus-equal</primary>
</indexterm>
<listitem><para><quote>plus-equal</quote> (increment variable by a constant)</para>
<para><userinput>let "var += 5"</userinput> results in
<varname>var</varname> being incremented by
<literal>5</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-=</token></term>
<indexterm>
<primary>-=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>-=</secondary>
</indexterm>
<indexterm>
<primary>minus-equal</primary>
</indexterm>
<listitem><para><quote>minus-equal</quote> (decrement variable by a constant)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*=</token></term>
<indexterm>
<primary>*=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>*=</secondary>
</indexterm>
<indexterm>
<primary>times-equal</primary>
</indexterm>
<listitem><para><quote>times-equal</quote> (multiply variable by a constant)</para>
<para><userinput>let "var *= 4"</userinput> results in <varname>var</varname>
being multiplied by <literal>4</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/=</token></term>
<indexterm>
<primary>/=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>/=</secondary>
</indexterm>
<indexterm>
<primary>slash-equal</primary>
</indexterm>
<listitem><para><quote>slash-equal</quote> (divide variable by a constant)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>%=</token></term>
<indexterm>
<primary>%=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>%=</secondary>
</indexterm>
<indexterm>
<primary>mod-equal</primary>
</indexterm>
<listitem><para><quote>mod-equal</quote> (remainder of dividing variable by a constant)</para>
<para><emphasis>Arithmetic operators often occur in an
<link linkend="exprref">expr</link> or <link
linkend="letref">let</link> expression.</emphasis></para>
<example id="arithops">
<title>Using Arithmetic Operations</title>
<programlisting>&arithops;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<note><para>Integer variables in Bash are actually signed
<emphasis>long</emphasis> (32-bit) integers, in the range of
-2147483648 to 2147483647. An operation that takes a variable
outside these limits will give an erroneous result.
<programlisting>a=2147483646
echo "a = $a" # a = 2147483646
let "a+=1" # Increment "a".
echo "a = $a" # a = 2147483647
let "a+=1" # increment "a" again, past the limit.
echo "a = $a" # a = -2147483648
# ERROR (out of range)</programlisting>
</para></note>
<caution><para>Bash does not understand floating point arithmetic. It
treats numbers containing a decimal point as strings.
<programlisting>a=1.5
let "b = $a + 1.3" # Error.
# t2.sh: let: b = 1.5 + 1.3: syntax error in expression (error token is ".5 + 1.3")
echo "b = $b" # b=1</programlisting>
Use <link linkend="bcref">bc</link> in scripts that that need floating
point calculations or math library functions.</para></caution>
<formalpara><title>bitwise operators</title>
<para>The bitwise operators seldom make an appearance in shell scripts.
Their chief use seems to be manipulating and testing values
read from ports or sockets. <quote>Bit flipping</quote> is more
relevant to compiled languages, such as C and C++, which run
fast enough to permit its use on the fly.</para></formalpara>
<variablelist id="bitwsops">
<title><anchor id="bitwsops1">bitwise operators</title>
<varlistentry>
<term><token><<</token></term>
<indexterm>
<primary><<</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary><<</secondary>
</indexterm>
<indexterm>
<primary>left shift</primary>
</indexterm>
<listitem><para>bitwise left shift (multiplies by <literal>2</literal>
for each shift position)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token><<=</token></term>
<indexterm>
<primary><<=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary><<=</secondary>
</indexterm>
<indexterm>
<primary>left-shift-equal</primary>
</indexterm>
<listitem><para><quote>left-shift-equal</quote></para>
<para><userinput>let "var <<= 2"</userinput> results in <varname>var</varname>
left-shifted <literal>2</literal> bits (multiplied by <literal>4</literal>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>></token></term>
<indexterm>
<primary>>></primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>>></secondary>
</indexterm>
<indexterm>
<primary>right shift</primary>
</indexterm>
<listitem><para>bitwise right shift (divides by <literal>2</literal>
for each shift position)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>>=</token></term>
<indexterm>
<primary>>>=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>>>=</secondary>
</indexterm>
<indexterm>
<primary>right-shift-equal</primary>
</indexterm>
<listitem><para><quote>right-shift-equal</quote> (inverse of <token><<=</token>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&</token></term>
<indexterm>
<primary>&</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&</secondary>
</indexterm>
<indexterm>
<primary>AND</primary>
<secondary>bitwise</secondary>
</indexterm>
<listitem><para>bitwise and</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&=</token></term>
<indexterm>
<primary>&=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&=</secondary>
</indexterm>
<indexterm>
<primary>and-equal</primary>
</indexterm>
<listitem><para><quote>bitwise and-equal</quote></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|</token></term>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>|</secondary>
</indexterm>
<indexterm>
<primary>OR</primary>
<secondary>bitwise</secondary>
</indexterm>
<listitem><para>bitwise OR</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|=</token></term>
<indexterm>
<primary>|=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>|=</secondary>
</indexterm>
<indexterm>
<primary>OR-equal</primary>
</indexterm>
<listitem><para><quote>bitwise OR-equal</quote></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>~</token></term>
<indexterm>
<primary>~</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>~</secondary>
</indexterm>
<indexterm>
<primary>negate</primary>
</indexterm>
<listitem><para>bitwise negate</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>!</token></term>
<indexterm>
<primary>!</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>!</secondary>
</indexterm>
<indexterm>
<primary>NOT</primary>
</indexterm>
<listitem><para>bitwise NOT</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>^</token></term>
<indexterm>
<primary>^</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>^</secondary>
</indexterm>
<indexterm>
<primary>XOR</primary>
</indexterm>
<listitem><para>bitwise XOR</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>^=</token></term>
<indexterm>
<primary>^=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>^=</secondary>
</indexterm>
<indexterm>
<primary>XOR-equal</primary>
</indexterm>
<listitem><para><quote>bitwise XOR-equal</quote></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="logops">
<title><anchor id="logops1">logical operators</title>
<varlistentry>
<term><token>&&</token></term>
<indexterm>
<primary>&&</primary>
</indexterm>
<indexterm>
<primary>operator</primary>
<secondary>&&</secondary>
</indexterm>
<indexterm>
<primary>AND</primary>
<secondary>logical</secondary>
</indexterm>
<listitem><para>and (logical)</para>
<para><programlisting>if [ $condition1 ] && [ $condition2 ]
# Same as: if [ $condition1 -a $condition2 ]
# Returns true if both condition1 and condition2 hold true...
if [[ $condition1 && $condition2 ]] # Also works.
# Note that && operator not permitted within [ ... ] construct.</programlisting></para>
<note><para><token>&&</token> may also, depending on context, be
used in an <link linkend="listconsref">and list</link>
to concatenate commands.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><token>||</token></term>
<indexterm>
<primary>||</primary>
</indexterm>
<indexterm>
<primary>operator</primary>
<secondary>||</secondary>
</indexterm>
<indexterm>
<primary>OR</primary>
<secondary>logical</secondary>
</indexterm>
<listitem><para>or (logical)</para>
<para><programlisting>if [ $condition1 ] || [ $condition2 ]
# Same as: if [ $condition1 -o $condition2 ]
# Returns true if either condition1 or condition2 holds true...
if [[ $condition1 || $condition2 ]] # Also works.
# Note that || operator not permitted within [ ... ] construct.</programlisting></para>
<note><para>Bash tests the <link linkend="exitstatusref">exit
status</link> of each statement linked with a logical
operator.</para></note>
<example id="andor">
<title>Compound Condition Tests Using && and ||</title>
<programlisting>&andor;</programlisting>
</example>
<para>The <token>&&</token> and <token>||</token> operators also
find use in an arithmetic context.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0))</userinput>
<computeroutput>1 0 1 0</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="miscop">
<title><anchor id="miscop1">miscellaneous operators</title>
<varlistentry>
<term><anchor id="commaop"><token>,</token></term>
<indexterm>
<primary>,</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>,</secondary>
</indexterm>
<indexterm>
<primary>linking</primary>
</indexterm>
<listitem>
<para>comma operator</para>
<para>The <command>comma operator</command> chains together
two or more arithmetic operations. All the operations are
evaluated (with possible <emphasis>side effects</emphasis>,
but only the last operation is returned.</para>
<para>
<programlisting>let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
echo "t1 = $t1" # t1 = 11
let "t2 = ((a = 9, 15 / 3))" # Set "a" and calculate "t2".
echo "t2 = $t2 a = $a" # t2 = 5 a = 9</programlisting>
</para>
<para>The comma operator finds use mainly in <link
linkend="forloopref">for loops</link>. See <xref
linkend="forloopc">.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Operators -->
<sect1 id="Numerical-Constants">
<title>Numerical Constants</title>
<para><anchor id="numconstants">A shell script interprets a number
as decimal (base 10), unless that number has a
special prefix or notation. A number preceded by a
<replaceable>0</replaceable> is <replaceable>octal</replaceable>
(base 8). A number preceded by <replaceable>0x</replaceable>
is <replaceable>hexadecimal</replaceable> (base 16). A number
with an embedded <replaceable>#</replaceable> is evaluated as
<replaceable>BASE#NUMBER</replaceable> (this option is of limited
usefulness because of range restrictions).</para>
<example id="numbers">
<title>Representation of numerical constants<token>:</token></title>
<programlisting>&numbers;</programlisting>
</example>
</sect1> <!-- Numerical-Constants -->
</chapter> <!-- Operations -->
</part> <!-- Part 2 (Basics) -->
<part label="Part 3" id="part3">
<title>Beyond the Basics</title>
<chapter id="variables2">
<title>Variables Revisited</title>
<para>Used properly, variables can add power and flexibility
to scripts. This requires learning their subtleties and
nuances.</para>
<sect1 id="internalvariables">
<title>Internal Variables</title>
<variablelist id="internalvariables1">
<varlistentry>
<term><replaceable><link
linkend="builtinref">Builtin</link> variables</replaceable></term>
<listitem><para>variables affecting bash script behavior</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$BASH</varname></term>
<indexterm>
<primary>$BASH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH</secondary>
</indexterm>
<indexterm>
<primary>path to bash</primary>
</indexterm>
<listitem><para>the path to the <emphasis>Bash</emphasis>
binary itself, usually <filename>/bin/bash</filename></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$BASH_ENV</varname></term>
<indexterm>
<primary>$BASH_ENV</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH_ENV</secondary>
</indexterm>
<listitem><para>an environmental variable pointing to a Bash startup
file to be read when a script is invoked</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$BASH_VERSION</varname></term>
<indexterm>
<primary>$BASH_VERSION</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH_VERSION</secondary>
</indexterm>
<listitem><para>the version of Bash installed on the system</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $BASH_VERSION</userinput>
<computeroutput>2.04.12(1)-release</computeroutput>
</screen>
</para>
<para>
<screen><prompt>tcsh% </prompt><userinput>echo $BASH_VERSION</userinput>
<computeroutput>BASH_VERSION: Undefined variable.</computeroutput>
</screen>
</para>
<para>Checking $BASH_VERSION is a good method of determining which
shell is running. $SHELL does not necessarily give the correct
answer.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dirstackref"><varname>$DIRSTACK</varname></term>
<indexterm>
<primary>$DIRSTACK</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$DIRSTACK</secondary>
</indexterm>
<indexterm>
<primary>directory stack</primary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>stack</secondary>
</indexterm>
<listitem><para>contents of the directory stack (affected by
<link linkend="pushdref">pushd</link> and <link
linkend="popdref">popd</link>)</para> <para>This
builtin variable is the counterpart to the <link
linkend="dirsd">dirs</link> command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$EDITOR</varname></term>
<indexterm>
<primary>$EDITOR</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$EDITOR</secondary>
</indexterm>
<indexterm>
<primary>editor</primary>
</indexterm>
<listitem><para>the default editor invoked by a script, usually
<command>vi</command> or <command>emacs</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="euidref"><varname>$EUID</varname></term>
<indexterm>
<primary>$EUID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$EUID</secondary>
</indexterm>
<indexterm>
<primary>effective user id</primary>
</indexterm>
<listitem><para><quote>effective</quote> user id number</para>
<para>Identification number of whatever identity the
current user has assumed, perhaps by means of <link
linkend="suref">su</link>.</para>
<caution><para>The <varname>$EUID</varname> is not necessarily
the same as the <link
linkend="uidref">$UID</link>.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$FUNCNAME</varname></term>
<indexterm>
<primary>$FUNCNAME</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>function</secondary>
</indexterm>
<indexterm>
<primary>name</primary>
</indexterm>
<listitem>
<para>name of the current function</para>
<para><programlisting>xyz23 ()
{
echo "$FUNCNAME now executing." # xyz23 now executing.
}
xyz23
echo "FUNCNAME = $FUNCNAME" # FUNCNAME =
# Null value outside a function.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="groupsref"><varname>$GROUPS</varname></term>
<indexterm>
<primary>$GROUPS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$GROUPS</secondary>
</indexterm>
<indexterm>
<primary>groups</primary>
</indexterm>
<listitem><para>groups current user belongs to</para>
<para>This is a listing (array) of the group id numbers for
current user, as recorded in
<filename>/etc/passwd</filename>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="homedirref"><varname>$HOME</varname></term>
<indexterm>
<primary>$HOME</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$HOME</secondary>
</indexterm>
<indexterm>
<primary>home directory</primary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>home</secondary>
</indexterm>
<listitem><para>home directory of the user, usually <filename
class="directory">/home/username</filename> (see <xref
linkend="ex6">)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hostnameref"><varname>$HOSTNAME</varname></term>
<indexterm>
<primary>$HOSTNAME</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$HOSTNAME</secondary>
</indexterm>
<indexterm>
<primary>system name</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>name</secondary>
</indexterm>
<listitem>
<para>The <link linkend="hnameref">hostname</link> command
assigns the system name at bootup in an init script.
However, the <function>gethostname()</function> function
sets the Bash internal variable <varname>$HOSTNAME</varname>.
See also <xref linkend="ex6">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$HOSTTYPE</varname></term>
<indexterm>
<primary>$HOSTTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$HOSTTYPE</secondary>
</indexterm>
<indexterm>
<primary>host type</primary>
</indexterm>
<listitem><para>host type</para>
<para>Like <link linkend="machtyperef">$MACHTYPE</link>,
identifies the system hardware.</para>
<screen><prompt>bash$ </prompt><userinput>echo $HOSTTYPE</userinput>
<computeroutput>i686</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ifsref"><varname>$IFS</varname></term>
<indexterm>
<primary>$IFS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$IFS</secondary>
</indexterm>
<indexterm>
<primary>input field separator</primary>
</indexterm>
<listitem><para>input field separator</para>
<para>This defaults to <link
linkend="whitespaceref">whitespace</link> (space,
tab, and newline), but may be changed, for example,
to parse a comma-separated data file. Note that
<link linkend="appref">$*</link> uses the first
character held in <varname>$IFS</varname>. See <xref
linkend="weirdvars">.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $IFS | cat -vte</userinput>
<computeroutput>$</computeroutput>
<prompt>bash$ </prompt><userinput>bash -c 'set w x y z; IFS=":-;"; echo "$*"'</userinput>
<computeroutput>w:x:y:z</computeroutput>
</screen>
</para>
<caution><para><varname>$IFS</varname> does not handle whitespace
the same as it does other characters.
<example id="ifsh">
<title>$IFS and whitespace</title>
<programlisting>&ifsh;</programlisting>
</example>
</para></caution>
<para>(Thanks, S. C., for clarification and examples.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$IGNOREEOF</varname></term>
<indexterm>
<primary>$IGNOREEOF</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$IGNOREEOF</secondary>
</indexterm>
<indexterm>
<primary>Ignore EOF</primary>
</indexterm>
<listitem><para>ignore EOF: how many end-of-files (control-D)
the shell will ignore before logging out.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$LC_COLLATE</varname></term>
<indexterm>
<primary>$LC_COLLATE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$LC_COLLATE</secondary>
</indexterm>
<indexterm>
<primary>lowercase collate</primary>
</indexterm>
<listitem>
<para>Often set in the <filename>.bashrc</filename> or
<filename>/etc/profile</filename> files, this
variable controls collation order in filename
expansion and pattern matching. If mishandled,
<varname>LC_COLLATE</varname> can cause unexpected results
in <link linkend="globbingref">filename
globbing</link>.</para>
<note><para>As of version 2.05 of Bash,
filename globbing no longer distinguishes between lowercase
and uppercase letters in a character range between
brackets. For example, <command>ls [A-M]*</command>
would match both <filename>File1.txt</filename>
and <filename>file1.txt</filename>. To revert to
the customary behavior of bracket matching, set
<varname>LC_COLLATE</varname> to <option>C</option>
by an <userinput>export LC_COLLATE=C</userinput>
in <filename>/etc/profile</filename> and/or
<filename>~/.bashrc</filename>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$LINENO</varname></term>
<indexterm>
<primary>$LINENO</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$LINENO</secondary>
</indexterm>
<indexterm>
<primary>line number</primary>
</indexterm>
<listitem><para>This variable is the line number of the shell
script in which this variable appears. It has significance only
within the script in which it appears, and is chiefly useful for
debugging purposes.</para>
<para><programlisting>last_cmd_arg=$_ # Save it.
echo "At line number $LINENO, variable \"v1\" = $v1"
echo "Last command argument processed = $last_cmd_arg"</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="machtyperef"><varname>$MACHTYPE</varname></term>
<indexterm>
<primary>$MACHTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$MACHTYPE</secondary>
</indexterm>
<indexterm>
<primary>machine type</primary>
</indexterm>
<listitem><para>machine type</para>
<para>Identifies the system hardware.</para>
<screen><prompt>bash$ </prompt><userinput>echo $MACHTYPE</userinput>
<computeroutput>i686-debian-linux-gnu</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="oldpwd"><varname>$OLDPWD</varname></term>
<indexterm>
<primary>$OLDPWD</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$OLDPWD</secondary>
</indexterm>
<indexterm>
<primary>previous working directory</primary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>working</secondary>
</indexterm>
<listitem><para>old working directory
(<quote>OLD-print-working-directory</quote>,
previous directory you were in)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$OSTYPE</varname></term>
<indexterm>
<primary>$OSTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$OSTYPE</secondary>
</indexterm>
<indexterm>
<primary>os type</primary>
</indexterm>
<listitem><para>operating system type</para>
<screen><prompt>bash$ </prompt><userinput>echo $OSTYPE</userinput>
<computeroutput>linux-gnu</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pathref"><varname>$PATH</varname></term>
<indexterm>
<primary>$PATH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PATH</secondary>
</indexterm>
<indexterm>
<primary>path to binaries</primary>
</indexterm>
<listitem><para>path to binaries, usually
<filename class="directory">/usr/bin/</filename>,
<filename class="directory">/usr/X11R6/bin/</filename>,
<filename class="directory">/usr/local/bin</filename>, etc.</para>
<para>When given a command, the shell automatically does
a hash table search on the directories listed in the
<emphasis>path</emphasis> for the executable. The
path is stored in the environmental variable,
<varname>$PATH</varname>, a list of directories,
separated by colons. Normally, the system
stores the <varname>$PATH</varname> definition
in <filename>/etc/profile</filename> and/or
<filename>~/.bashrc</filename> (see <xref
linkend="files">).</para>
<para><screen><prompt>bash$ </prompt><command>echo $PATH</command>
<computeroutput>/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin</computeroutput></screen>
</para>
<para><userinput>PATH=${PATH}:/opt/bin</userinput> appends
the <filename class="directory">/opt/bin</filename>
directory to the current path. In a script, it may be
expedient to temporarily add a directory to the path
in this way. When the script exits, this restores the
original <varname>$PATH</varname> (a child process, such
as a script, may not change the environment of the parent
process, the shell).</para>
<note><para>The current <quote>working directory</quote>,
<filename class="directory">./</filename>, is usually
omitted from the <varname>$PATH</varname> as a security
measure.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ppidref"><varname>$PPID</varname></term>
<indexterm>
<primary>$PPID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PPID</secondary>
</indexterm>
<indexterm>
<primary>process id</primary>
</indexterm>
<listitem><para></para>
<para>The <varname>$PPID</varname> of a process is
the process id (<varname>pid</varname>) of its parent process.
<footnote><para>The pid of the currently running script is
<varname>$$</varname>, of course.</para></footnote>
</para>
<para>Compare this with the <link
linkend="pidofref">pidof</link> command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ps1ref"><varname>$PS1</varname></term>
<indexterm>
<primary>$PS1</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS1</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
</indexterm>
<listitem><para>This is the main prompt, seen at the command line.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$PS2</varname></term>
<indexterm>
<primary>$PS2</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS2</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>secondary</secondary>
</indexterm>
<listitem><para>The secondary prompt, seen when additional input is
expected. It displays as <quote>&gt;</quote>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$PS3</varname></term>
<indexterm>
<primary>$PS3</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS3</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>tertiary</secondary>
</indexterm>
<listitem><para>The tertiary prompt, displayed in a
<link linkend="selectref">select</link> loop (see <xref
linkend="ex31">).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$PS4</varname></term>
<indexterm>
<primary>$PS4</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS4</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>quartenary</secondary>
</indexterm>
<listitem>
<para>The quartenary prompt, shown at the beginning of
each line of output when invoking a script with the
<token>-x</token> <link linkend="optionsref">option</link>.
It displays as <quote>+</quote>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pwdref"><varname>$PWD</varname></term>
<indexterm>
<primary>$PWD</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PWD</secondary>
</indexterm>
<indexterm>
<primary>working directory</primary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>working</secondary>
</indexterm>
<listitem>
<para>working directory (directory you are in at the time)</para>
<para>This is the analog to the <link linkend="pwd2ref">pwd</link>
builtin command.</para>
<para><programlisting>&wipedir;</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$REPLY</varname></term>
<indexterm>
<primary>$REPLY</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$REPLY</secondary>
</indexterm>
<indexterm>
<primary>default value of read</primary>
</indexterm>
<indexterm>
<primary>reply</primary>
<secondary>read</secondary>
</indexterm>
<listitem><para>The default value when a variable is not
supplied to <link linkend="readref">read</link>. Also
applicable to <link linkend="selectref">select</link> menus,
but only supplies the item number of the variable chosen,
not the value of the variable itself.</para>
<para><programlisting>&reply;</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$SECONDS</varname></term>
<indexterm>
<primary>$SECONDS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SECONDS</secondary>
</indexterm>
<indexterm>
<primary>number of seconds the script has been running</primary>
</indexterm>
<indexterm>
<primary>runtime</primary>
<secondary>seconds</secondary>
</indexterm>
<listitem><para>The number of seconds the script has been running.</para>
<para><programlisting>&seconds;</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELLOPTS</varname></term>
<indexterm>
<primary>$SHELLOPTS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SHELLOPTS</secondary>
</indexterm>
<indexterm>
<primary>shell options</primary>
</indexterm>
<listitem><para>the list of enabled shell <link
linkend="optionsref">options</link>, a readonly
variable</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHLVL</varname></term>
<indexterm>
<primary>$SHLVL</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SHLVL</secondary>
</indexterm>
<indexterm>
<primary>shell level</primary>
</indexterm>
<listitem><para>Shell level, how deeply Bash is nested. If,
at the command line, $SHLVL is 1, then in a script it will
increment to 2.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$TMOUT</varname></term>
<indexterm>
<primary>$TMOUT</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$TMOUT</secondary>
</indexterm>
<indexterm>
<primary>timeout interval</primary>
</indexterm>
<listitem><para>If the <replaceable>$TMOUT</replaceable>
environmental variable is set to a non-zero value
<emphasis>time</emphasis>, then the shell prompt will time out
after <emphasis>time</emphasis> seconds. This will cause a
logout.</para>
<note><para>Unfortunately, this works only while waiting
for input at the shell prompt console or in an xterm. While
it would be nice to speculate on the uses of this
internal variable for timed input, for example in
combination with <link linkend="readref">read</link>,
<replaceable>$TMOUT</replaceable> does not work
in that context and is virtually useless for shell
scripting. (Reportedly the <emphasis>ksh</emphasis> version
of a timed <command>read</command> does work).</para></note>
<para>Implementing timed input in a script is certainly
possible, but hardly seems worth the effort. One method is to
set up a timing loop to signal the script when it times out.
This also requires a signal handling routine to trap (see
<xref linkend="ex76">) the interrupt generated by the timing
loop (whew!).</para>
<example id="tmdin">
<title>Timed Input</title>
<programlisting>&tmdin;</programlisting>
</example>
<para>An alternative is using <link
linkend="sttyref">stty</link>.</para>
<example id="timeout">
<title>Once more, timed input</title>
<programlisting>&timeout;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uidref"><varname>$UID</varname></term>
<indexterm>
<primary>$UID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$UID</secondary>
</indexterm>
<indexterm>
<primary>user id</primary>
</indexterm>
<listitem><para>user id number</para>
<para>current user's user identification number, as
recorded in <filename>/etc/passwd</filename>
</para>
<para>This is the current user's real id, even if she has
temporarily assumed another identity through <link
linkend="suref">su</link>. <varname>$UID</varname> is a
readonly variable, not subject to change from the command
line or within a script, and is the counterpart to the
<link linkend="idref">id</link> builtin.</para>
<example id="amiroot">
<title>Am I root?</title>
<programlisting>&amiroot;</programlisting>
</example>
<para>See also <xref linkend="ex2">.</para>
<!-- Nest note within last entry -->
<note><para>The variables <varname>$USER</varname>,
<varname>$USERNAME</varname>, <varname>$LOGNAME</varname>,
<varname>$MAIL</varname>, and <varname>$ENV</varname>
are <emphasis>not</emphasis> Bash <link
linkend="builtinref">builtins</link>. These are,
however, often set as environmental variables
in one of the Bash <link linkend="filesref">startup
files</link>. <varname>$SHELL</varname>, the name of the user's
login shell, may be set from <filename>/etc/passwd</filename>
or in an <quote>init</quote> script, and it is likewise not
a Bash builtin.</para></note>
<!-- Nest note after $USER -->
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="flpref"><varname>$-</varname></term>
<indexterm>
<primary>$-</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$-</secondary>
</indexterm>
<indexterm>
<primary>flags</primary>
</indexterm>
<listitem>
<para>Flags passed to script</para>
<caution><para>This was originally a <emphasis>ksh</emphasis>
construct adopted into Bash, and unfortunately it does not
seem to work reliably in Bash scripts. One possible use
for it is to have a script <link linkend="iitest">self-test
whether it is interactive</link>.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$!</varname></term>
<indexterm>
<primary>$!</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$!</secondary>
</indexterm>
<indexterm>
<primary>PID</primary>
<secondary>last job run in background</secondary>
</indexterm>
<listitem>
<para>PID (process id) of last job run in background</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="underscoreref"><varname>$_</varname></term>
<indexterm>
<primary>$_</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$_</secondary>
</indexterm>
<indexterm>
<primary>underscore</primary>
<secondary>last argument</secondary>
</indexterm>
<listitem>
<para>Special variable set to last argument of previous command
executed.</para>
<example id="uscref">
<title>underscore variable</title>
<programlisting>#!/bin/bash
echo $_ # /bin/bash
# Just called /bin/bash to run the script.
du >/dev/null # So no output from command.
echo $_ # du
ls -al # So no output from command.
echo $_ # -al (last argument)
:
echo $_ # :</programlisting></example>
</listitem>
</varlistentry>
</variablelist>
<!-- Last entry of intrinsic BASH variables -->
<variablelist id="posparmslist">
<title>Positional Parameters</title>
<varlistentry>
<term><anchor id="posparamref"><varname>$0</varname>, <varname>$1</varname>,
<varname>$2</varname>, etc.</term>
<indexterm>
<primary>$0</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$0</secondary>
</indexterm>
<indexterm>
<primary>positional parameter</primary>
</indexterm>
<indexterm>
<primary>parameter</primary>
<secondary>positional</secondary>
</indexterm>
<listitem>
<para>positional parameters, passed from command
line to script, passed to a function, or <link
linkend="setref">set</link> to a variable (see <xref
linkend="ex17"> and <xref linkend="ex34">)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$#</varname></term>
<indexterm>
<primary>$#</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$#</secondary>
</indexterm>
<indexterm>
<primary>positional parameter</primary>
<secondary>number of</secondary>
</indexterm>
<indexterm>
<primary>parameter</primary>
<secondary>positional</secondary>
<tertiary>number of</tertiary>
</indexterm>
<listitem><para>number of command line arguments
<footnote><para>The words <quote>argument</quote>
and <quote>parameter</quote> are often used
interchangeably. In the context of this document, they
have the same precise meaning, that of a variable passed
to a script or function.</para></footnote>
or positional parameters (see <xref linkend="ex4">)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$$</varname></term>
<indexterm>
<primary>$$</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$$</secondary>
</indexterm>
<indexterm>
<primary>PID</primary>
<secondary>of script</secondary>
</indexterm>
<listitem><para>process id of script, often used in scripts
to construct temp file names (see <xref linkend="ftpget">,
<xref linkend="online">, and <xref linkend="derpm">)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xstatvarref"><varname>$?</varname></term>
<indexterm>
<primary>$?</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$?</secondary>
</indexterm>
<indexterm>
<primary>exit status</primary>
</indexterm>
<listitem><para><link linkend="exitstatusref">exit status</link>
of a command, <link linkend="functionref">function</link>,
or the script itself (see <xref linkend="max">)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="appref"><varname>$*</varname></term>
<indexterm>
<primary>$*</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$*</secondary>
</indexterm>
<indexterm>
<primary>positional parameter</primary>
<secondary>all</secondary>
</indexterm>
<indexterm>
<primary>parameter</primary>
<secondary>positional</secondary>
<tertiary>all</tertiary>
</indexterm>
<listitem><para>All of the positional parameters, seen as a single
word</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$@</varname></term>
<indexterm>
<primary>$@</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$*</secondary>
</indexterm>
<indexterm>
<primary>positional parameter</primary>
<secondary>all</secondary>
</indexterm>
<listitem><para>Same as <token>$*</token>, but each parameter is a
quoted string, that is, the parameters are passed on
intact, without interpretation or expansion. This means,
among other things, that each parameter in the argument
list is seen as a separate word.</para>
<example id="arglist">
<title><command>arglist</command>: Listing arguments with $* and $@</title>
<programlisting>&arglist;</programlisting>
</example>
<para>The <varname>$@</varname> special parameter finds
use as a tool for filtering input into shell scripts. The
<command>cat "$@"</command> construction accepts input
to a script either from <filename>stdin</filename> or
from files given as parameters to the script. See <xref
linkend="rot13"> and <xref linkend="cryptoquote">.</para>
<caution><para>The <varname>$*</varname> and <varname>$@</varname>
parameters sometimes display inconsistent and
puzzling behavior, depending on the setting of <link
linkend="ifsref">$IFS</link>.</para></caution>
<example id="incompat">
<title>Inconsistent <varname>$*</varname> and <varname>$@</varname> behavior</title>
<programlisting>&incompat;</programlisting>
</example>
<note><para>The <command>$@</command> and <command>$*</command>
parameters differ only when between double quotes.</para></note>
<example id="ifsempty">
<title><varname>$*</varname> and <varname>$@</varname> when
<varname>$IFS</varname> is empty</title>
<programlisting>&ifsempty;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Internal Variables -->
<sect1 id="Parameter-Substitution">
<title>Parameter Substitution</title>
<para><anchor id="paramsubref"></para>
<variablelist id="pssub">
<title><anchor id="pssub1">Manipulating and/or expanding variables</title>
<varlistentry>
<term>
<userinput>${parameter}</userinput></term>
<listitem>
<para>Same as <replaceable>$parameter</replaceable>, i.e.,
value of the variable
<replaceable>parameter</replaceable>.
In certain contexts, only the less ambiguous
<replaceable>${parameter}</replaceable> form
works.</para>
<para>May be used for concatenating variables with strings.</para>
<para><programlisting>
your_id=${USER}-on-${HOSTNAME}
echo "$your_id"
#
echo "Old \$PATH = $PATH"
PATH=${PATH}:/opt/bin #Add /opt/bin to $PATH for duration of script.
echo "New \$PATH = $PATH"
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${parameter-default}</userinput></term>
<listitem>
<para>If parameter not set, use default.</para>
<para><programlisting>
echo ${username-`whoami`}
# Echoes the result of `whoami`, if variable $username is still unset.</programlisting></para>
<note><para>This is almost equivalent to
<replaceable>${parameter:-default}</replaceable>. The
extra <token>:</token> makes a difference only when
<emphasis>parameter</emphasis> has been declared,
but is null. </para></note>
<para><programlisting>&paramsub;</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${parameter=default}</userinput></term>
<term><userinput>${parameter:=default}</userinput></term>
<listitem>
<para><anchor id="defparam"></para>
<para>If parameter not set, set it to default.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when <emphasis>$parameter</emphasis>
has been declared and is null,
<footnote>
<para>If $parameter is null in a
non-interactive script, it will terminate with a <link
linkend="exitcodesref"><returnvalue>127</returnvalue>
exit status</link> (the Bash error code code for
<quote>command not found</quote>).</para>
</footnote>
as above.
</para>
<para><programlisting>
echo ${username=`whoami`}
# Variable "username" is now set to `whoami`.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${parameter+alt_value}</userinput></term>
<term><userinput>${parameter:+alt_value}</userinput></term>
<listitem>
<para>If parameter set, use
<userinput>alt_value</userinput>, else use null
string.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when <emphasis>parameter</emphasis>
has been declared and is null, see below.</para>
<para><programlisting>echo "###### \${parameter+alt_value} ########"
echo
a=${param1+xyz}
echo "a = $a" # a =
param2=
a=${param2+xyz}
echo "a = $a" # a = xyz
param3=123
a=${param3+xyz}
echo "a = $a" # a = xyz
echo
echo "###### \${parameter:+alt_value} ########"
echo
a=${param4:+xyz}
echo "a = $a" # a =
param5=
a=${param5:+xyz}
echo "a = $a" # a =
# Different result from a=${param5+xyz}
param6=123
a=${param6+xyz}
echo "a = $a" # a = xyz</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${parameter?err_msg}</userinput></term>
<term><userinput>${parameter:?err_msg}</userinput></term>
<listitem><para>If parameter set, use it, else print err_msg.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when <emphasis>parameter</emphasis>
has been declared and is null, as above.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex6">
<title>Using param substitution and <token>:</token></title>
<programlisting>&ex6;</programlisting>
</example>
<formalpara><title>Parameter substitution and/or expansion</title>
<para><anchor id="psub2">The following expressions are
the complement to the <command>match</command>
<replaceable>in</replaceable> <command>expr</command>
string operations (see <xref linkend="ex45">).
These particular ones are used mostly in parsing file
path names.</para></formalpara>
<variablelist id="psorex">
<title><anchor id="psorex1">Variable length / Substring removal</title>
<varlistentry>
<term><userinput>${#var}</userinput></term>
<listitem>
<para><userinput>String length</userinput> (number
of characters in <varname>$var</varname>). For
an <link linkend="arrayref">array</link>,
<command>${#array}</command> is the length of the
first element in the array.</para>
<note><para>
Exceptions:
<itemizedlist>
<listitem><para>
<command>${#*}</command> and
<command>${#@}</command> give the <emphasis>number
of positional parameters</emphasis>.
</para></listitem>
<listitem><para>
For an array, <command>${#array[*]}</command> and
<command>${#array[@]}</command> give the number
of elements in the array.
</para></listitem>
</itemizedlist>
</para></note>
<example id="length">
<title>Length of a variable</title>
<programlisting>&length;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var#pattern}</userinput></term>
<term><userinput>${var##pattern}</userinput></term>
<listitem>
<para>Remove from <varname>$var</varname>
the shortest/longest part of <varname>$pattern</varname>
that matches the <replaceable>front end</replaceable>
of <varname>$var</varname>.
</para>
<para>A usage illustration from <xref linkend="daysbetween">:
<programlisting># Function from "days-between.sh" example.
# Strips leading zero(s) from argument passed.
strip_leading_zero () # Better to strip possible leading zero(s)
{ # from day and/or month
val=${1#0} # since otherwise Bash will interpret them
return $val # as octal values (POSIX.2, sect 2.9.2.1).
}</programlisting>
</para>
<para>Another usage illustration:
<programlisting>echo `basename $PWD` # Basename of current working directory.
echo "${PWD##*/}" # Basename of current working directory.
echo
echo `basename $0` # Name of script.
echo $0 # Name of script.
echo "${0##*/}" # Name of script.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pctpatref"><userinput>${var%pattern}</userinput></term>
<term><userinput>${var%%pattern}</userinput></term>
<listitem><para>Remove from <varname>$var</varname>
the shortest/longest part of <varname>$pattern</varname>
that matches the <replaceable>back end</replaceable>
of <varname>$var</varname>.
</para></listitem>
</varlistentry>
</variablelist>
<para><link linkend="bash2ref">Version 2</link> of Bash adds
additional options.</para>
<example id="pattmatching">
<title>Pattern matching in parameter substitution</title>
<programlisting>&pattmatching;</programlisting>
</example>
<example id="rfe">
<title>Renaming file extensions<token>:</token></title>
<programlisting>&rfe;</programlisting>
</example>
<variablelist id="exprepl">
<title><anchor id="exprepl1">Variable expansion / Substring
replacement</title>
<varlistentry>
<term></term>
<listitem>
<para>These constructs have been adopted from
<emphasis>ksh</emphasis>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var:pos}</userinput></term>
<listitem>
<para>Variable <replaceable>var</replaceable> expanded,
starting from offset <replaceable>pos</replaceable>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var:pos:len}</userinput></term>
<listitem>
<para>Expansion to a max of <replaceable>len</replaceable>
characters of variable <replaceable>var</replaceable>, from offset
<replaceable>pos</replaceable>. See <xref linkend="pw">
for an example of the creative use of this operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/patt/replacement}</userinput></term>
<listitem>
<para>First match of <replaceable>patt</replaceable>,
within <replaceable>var</replaceable> replaced with
<replaceable>replacement</replaceable>.</para>
<para>If <replaceable>replacement</replaceable> is
omitted, then the first match of
<replaceable>patt</replaceable> is replaced by
<emphasis>nothing</emphasis>, that is, deleted.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var//patt/replacement}</userinput></term>
<listitem>
<formalpara><title>Global replacement</title>
<para>All matches of <replaceable>patt</replaceable>,
within <replaceable>var</replaceable> replaced with
<replaceable>replacement</replaceable>.</para>
</formalpara>
<para>As above, if <replaceable>replacement</replaceable>
is omitted, then all occurrences of
<replaceable>patt</replaceable> are replaced by
<emphasis>nothing</emphasis>, that is, deleted.</para>
<example id="ex7">
<title>Using pattern matching to parse arbitrary strings</title>
<programlisting>&ex7;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/#patt/replacement}</userinput></term>
<listitem>
<para>If <emphasis>prefix</emphasis> of
<replaceable>var</replaceable> matches
<replaceable>replacement</replaceable>, then substitute
<replaceable>replacement</replaceable> for
<replaceable>patt</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/%patt/replacement}</userinput></term>
<listitem>
<para>If <emphasis>suffix</emphasis> of
<replaceable>var</replaceable> matches
<replaceable>replacement</replaceable>, then substitute
<replaceable>replacement</replaceable> for
<replaceable>patt</replaceable>.</para>
<example id="varmatch">
<title>Matching patterns at prefix or suffix of string</title>
<programlisting>&varmatch;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${!varprefix*}</userinput></term>
<term><userinput>${!varprefix@}</userinput></term>
<listitem>
<para>Matches all previously declared variables beginning
with <emphasis>varprefix</emphasis>.
<programlisting>xyz23=whatever
xyz24=
a=${!xyz*} # Expands to names of declared variables beginning with "xyz".
echo "a = $a" # a = xyz23 xyz24
a=${!xyz@} # Same as above.
echo "a = $a" # a = xyz23 xyz24
# Bash, version 2.04, adds this feature.</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Parameter Substitution -->
<sect1 id="declareref">
<indexterm>
<primary>declare</primary>
</indexterm>
<indexterm>
<primary>typeset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>declare</secondary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>typeset</secondary>
</indexterm>
<title>Typing variables: <command>declare</command> or
<command>typeset</command></title>
<para>The <command>declare</command> or <command>typeset</command>
<link linkend="builtinref">builtins</link> (they are exact
synonyms) permit restricting the properties of variables. This
is a very weak form of the typing available in certain
programming languages. The <command>declare</command>
command is specific to version 2 or later of Bash. The
<command>typeset</command> command also works in ksh
scripts.</para>
<variablelist id="declareopsref">
<title><anchor id="declareopsref1">declare/typeset options</title>
<varlistentry>
<term><token>-r</token> <replaceable>readonly</replaceable></term>
<listitem><para><programlisting>declare -r var1</programlisting></para>
<para>(<userinput>declare -r var1</userinput> works the same as
<userinput>readonly var1</userinput>)</para>
<para>This is the rough equivalent of the C
<command>const</command> type qualifier. An
attempt to change the value of a readonly variable fails with an
error message.</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-i</token> <replaceable>integer</replaceable></term>
<listitem>
<para><programlisting>declare -i number
# The script will treat subsequent occurrences of "number" as an integer.
number=3
echo "number = $number" # number = 3
number=three
echo "number = $number" # number = 0
# Tries to evaluate "three" as an integer.</programlisting>
Note that certain arithmetic operations are permitted
for declared integer variables without the need
for <link linkend="exprref">expr</link> or <link
linkend="letref">let</link>.</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-a</token> <replaceable>array</replaceable></term>
<listitem><para><programlisting>declare -a indices</programlisting></para>
<para>The variable <varname>indices</varname> will be treated as
an array.</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-f</token> <replaceable>functions</replaceable></term>
<listitem>
<para><programlisting>declare -f</programlisting></para>
<para>A <userinput>declare -f</userinput> line with no
arguments in a script causes a listing of all the
functions previously defined in that script.</para>
<para><programlisting>declare -f function_name</programlisting></para>
<para>A <userinput>declare -f function_name</userinput>
in a script lists just the function named.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-x</token> <link linkend="exportref">export</link></term>
<listitem><para><programlisting>declare -x var3</programlisting></para>
<para>This declares a variable as available for exporting outside the
environment of the script itself.</para></listitem>
</varlistentry>
<varlistentry>
<term>var=$value</term>
<listitem>
<para><programlisting>declare -x var3=373</programlisting></para>
<para>The <command>declare</command> command permits
assigning a value to a variable in the same statement
as setting its properties.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex20">
<title>Using <command>declare</command> to type variables</title>
<programlisting>&ex20;</programlisting>
</example>
</sect1> <!-- Typing variables: declare or typeset -->
<sect1 id="ivr">
<title>Indirect References to Variables</title>
<para><anchor id="ivrref"></para>
<para>Assume that the value of a variable is the name of a second
variable. Is it somehow possible to retrieve the value
of this second variable from the first one? For example,
if <replaceable>a=letter_of_alphabet</replaceable>
and <replaceable>letter_of_alphabet=z</replaceable>,
can a reference to <replaceable>a</replaceable> return
<replaceable>z</replaceable>? This can indeed be done, and
it is called an <emphasis>indirect reference</emphasis>. It
uses the unusual <replaceable>eval var1=\$$var2</replaceable>
notation.</para>
<example id="indref">
<title>Indirect References</title>
<programlisting>&indref;</programlisting>
</example>
<example id="coltotaler2">
<title>Passing an indirect reference to <replaceable>awk</replaceable></title>
<programlisting>&coltotaler2;</programlisting>
</example>
<caution><para>This method of indirect referencing is a bit tricky.
If the second order variable changes its value, then the the first
order variable must be properly dereferenced (as in the above
example). <anchor id="ivr2">Fortunately, the
<replaceable>${!variable}</replaceable> notation introduced
with <link linkend="bash2ref">version 2</link> of Bash
(see <xref linkend="ex78">) makes indirect referencing more
intuitive.</para></caution>
</sect1> <!-- Indirect References to Variables -->
<sect1 id="randomvar">
<indexterm>
<primary>$RANDOM</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$RANDOM</secondary>
</indexterm>
<title>$RANDOM: generate random integer</title>
<note><para>$RANDOM is an internal Bash function (not a constant) that
returns a <emphasis>pseudorandom</emphasis> integer in the range
0 - 32767. $RANDOM should <replaceable>not</replaceable> be used
to generate an encryption key.</para></note>
<example id="ex21">
<title>Generating random numbers</title>
<programlisting>&ex21;</programlisting>
</example>
<para>Just how random is RANDOM? The best way to test this is
to write a script that tracks the distribution of
<quote>random</quote> numbers generated by RANDOM. Let's roll
a RANDOM die a few times...</para>
<example id="randomtest">
<title>Rolling the die with RANDOM</title>
<programlisting>&randomtest;</programlisting>
</example>
<para>As we have seen in the last example, it is best to
<quote>reseed</quote> the <varname>RANDOM</varname>
generator each time it is invoked. Using the same seed
for <varname>RANDOM</varname> repeats the same series
of numbers. (This mirrors the behavior of the
<replaceable>random()</replaceable> function in C.)</para>
<example id="seedingrandom">
<title>Reseeding RANDOM</title>
<programlisting>&seedingrandom;</programlisting>
</example>
<note><para>The <filename>/dev/urandom</filename> device/file provides
a means of generating much more <quote>random</quote>
pseudorandom numbers than the <varname>$RANDOM</varname>
variable. <userinput>dd if=/dev/urandom of=targetfile
bs=1 count=XX</userinput> creates a file of well-scattered
pseudorandom numbers. However, assigning these numbers
to a variable in a script requires a workaround, such as
filtering through <link linkend="odref">od</link> (as in
above example) or using <link linkend="ddref">dd</link>
(see <xref linkend="blotout">).</para></note>
</sect1> <!-- RANDOM: generate random integer -->
<sect1 id="dblparens">
<title>The Double Parentheses Construct</title>
<para>Similar to the <link linkend="letref">let</link> command,
the <command>((...))</command> construct permits arithmetic
expansion and evaluation. In its simplest form, <userinput>a=$((
5 + 3 ))</userinput> would set <quote>a</quote> to <quote>5 +
3</quote>, or 8. However, this double parentheses construct is
also a mechanism for allowing C-type manipulation of variables
in Bash.</para>
<example id="cvars">
<title>C-type manipulation of variables</title>
<programlisting>&cvars;</programlisting>
</example>
<para>See also <xref linkend="forloopc">.</para>
</sect1> <!-- The Double Parentheses Construct -->
</chapter> <!-- Variables Revisited -->
<chapter id="loops">
<title>Loops and Branches</title>
<para>Operations on code blocks are the key to structured, organized
shell scripts. Looping and branching constructs provide the tools for
accomplishing this.</para>
<sect1 id="loops1">
<title>Loops</title>
<para>A <emphasis>loop</emphasis> is a block of code that iterates
(repeats) a list of commands as long as the loop control condition is
true.</para>
<variablelist id="forloopref">
<title><anchor id="forloopref1">for loops</title>
<varlistentry>
<term><command>for (in)</command></term>
<indexterm>
<primary>for</primary>
</indexterm>
<indexterm>
<primary>in</primary>
</indexterm>
<indexterm>
<primary>do</primary>
</indexterm>
<indexterm>
<primary>done</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>for</secondary>
</indexterm>
<listitem>
<para>This is the basic looping construct. It differs significantly
from its C counterpart.</para>
<para><cmdsynopsis>
<command>for</command>
<arg choice="plain"><replaceable>arg</replaceable></arg>
<arg choice="plain">in</arg>
<arg choice="opt"><replaceable>list</replaceable></arg><sbr>
<arg choice="plain">do</arg><sbr>
<arg rep=repeat choice=plain><replaceable>&nbsp;command</replaceable></arg><sbr>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<note><para>During each pass through the loop,
<replaceable>arg</replaceable> takes on the value of each
variable in the <replaceable>list</replaceable>.</para></note>
<para><programlisting>for arg in "$var1" "$var2" "$var3" ... "$varN"
# In pass 1 of the loop, $arg = $var1
# In pass 2 of the loop, $arg = $var2
# In pass 3 of the loop, $arg = $var3
# ...
# In pass N of the loop, $arg = $varN
# Arguments in [list] quoted to prevent possible word splitting.</programlisting></para>
<para>The argument <replaceable>list</replaceable> may contain wild cards.</para>
<para>If <command>do</command> is on same line as
<command>for</command>, there needs to be a semicolon
after list.</para>
<para><cmdsynopsis>
<command>for</command>
<arg choice="plain"><replaceable>arg</replaceable></arg>
<arg choice="plain">in</arg>
<arg choice="opt"><replaceable>list</replaceable></arg>
<arg choice="plain">;</arg>
<arg choice="plain">do</arg><sbr>
</cmdsynopsis></para>
<example id="ex22">
<title>Simple <command>for</command> loops</title>
<programlisting>&ex22;</programlisting>
</example>
<note><para>Each <userinput>[list]</userinput> element
may contain multiple parameters. This is useful when
processing parameters in groups. In such cases, use the
<command>set</command> command (see <xref linkend="ex34">)
to force parsing of each <userinput>[list]</userinput>
element and assignment of each component to the positional
parameters.</para></note>
<example id="ex22a">
<title><command>for</command> loop with two parameters in each
[list] element</title>
<programlisting>&ex22a;</programlisting>
</example>
<para>A variable may supply the <userinput>[list]</userinput> in a
<command>for</command> loop.</para>
<example id="fileinfo">
<title><emphasis>Fileinfo:</emphasis> operating on a file list
contained in a variable</title>
<programlisting>&fileinfo;</programlisting>
</example>
<para>The <userinput>[list]</userinput> in a
<command>for</command> loop may contain filename <link
linkend="globbingref">globbing</link>, that is, using
wildcards for filename expansion.</para>
<example id="listglob">
<title><command>Operating on files with a for</command> loop</title>
<programlisting>&listglob;</programlisting>
</example>
<para>Omitting the <userinput>in [list]</userinput> part of a
<command>for</command> loop causes the loop to operate on
<token>$@</token>, the list of arguments given on the command line
to the script.</para>
<example id="ex23">
<title>Missing <userinput>in [list]</userinput> in a
<command>for</command> loop</title>
<programlisting>&ex23;</programlisting>
</example>
<para>It is possible to use <link
linkend="commandsubref">command substitution</link>
to generate the <userinput>[list]</userinput> in a
<command>for</command> loop. See also <xref linkend="ex53">,
<xref linkend="symlinks"> and <xref linkend="base">.</para>
<example id="forloopcmd">
<title>Generating the [list] in a <command>for</command>
loop with command substitution</title>
<programlisting>&forloopcmd;</programlisting>
</example>
<para>This is a somewhat more complex example of using command
substitution to create the [list].</para>
<example id="bingrep">
<title>A <link linkend="grepref">grep</link> replacement
for binary files</title>
<programlisting>&bingrep;</programlisting>
</example>
<para>Here is yet another example of the [list] resulting
from command substitution.</para>
<example id="findstring">
<title>Checking all the binaries in a directory for
authorship</title>
<programlisting>&findstring;</programlisting>
</example>
<para>The output of a <command>for</command> loop may be piped to
a command or commands.</para>
<example id="symlinks">
<title>Listing the symbolic links in a directory</title>
<programlisting>&symlinks;</programlisting>
</example>
<para>There is an alternative syntax to a <command>for</command>
loop that will look very familiar to C programmers. This
requires double parentheses.</para>
<example id="forloopc">
<title>A C-like <command>for</command> loop</title>
<programlisting>&forloopc;</programlisting>
</example>
<para>See also <xref linkend="qfunction"> and <xref
linkend="twodim">.</para>
<para>---</para>
<para>Now, for an example from <quote>real life</quote>.</para>
<example id="ex24">
<title>Using <command>efax</command> in batch mode</title>
<programlisting>&ex24;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whileloopref"><command>while</command></term>
<indexterm>
<primary>while</primary>
</indexterm>
<indexterm>
<primary>do</primary>
</indexterm>
<indexterm>
<primary>done</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>while</secondary>
</indexterm>
<listitem>
<para>This construct tests for a condition at the top of a
loop, and keeps looping as long as that condition is
true (returns a <returnvalue>0</returnvalue> <link
linkend="exitstatusref">exit status</link>).</para>
<para><cmdsynopsis>
<command>while</command>
<arg choice="opt"><replaceable>condition</replaceable></arg><sbr>
<arg choice="plain">do</arg><sbr>
<arg choice="plain" rep="repeat"><replaceable>&nbsp;command</replaceable></arg><sbr>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<para>As is the case with <token>for/in</token> loops, placing the
<command>do</command> on the same line as the condition test
requires a semicolon.</para>
<para><cmdsynopsis>
<command>while</command>
<arg choice="opt"><replaceable>condition</replaceable></arg>
<arg choice="plain">;</arg>
<arg choice="plain">do</arg>
</cmdsynopsis></para>
<para>Note that certain specialized <command>while</command>
loops, as, for example, a <link
linkend="getoptsx">getopts construct</link>, deviate
somewhat from the standard template given here.</para>
<example id="ex25">
<title>Simple <command>while</command> loop</title>
<programlisting>&ex25;</programlisting>
</example>
<example id="ex26">
<title>Another <command>while</command> loop</title>
<programlisting>&ex26;</programlisting>
</example>
<para>A <command>while</command> loop may have multiple
conditions. Only the final condition determines when the loop
terminates. This necessitates a slightly different loop syntax,
however.</para>
<example id="ex26a">
<title><command>while</command> loop with multiple conditions</title>
<programlisting>&ex26a;</programlisting>
</example>
<para>As with a <command>for</command> loop, a
<command>while</command> loop may employ C-like syntax
by using the double parentheses construct (see also <xref
linkend="cvars">).</para>
<example id="whloopc">
<title>C-like syntax in a <command>while</command> loop</title>
<programlisting>&whloopc;</programlisting>
</example>
<note><para>A <command>while</command> loop may have its
<filename>stdin</filename> <link
linkend="redirref">redirected to a file</link> by a
<token>&lt;</token> at its end.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="untilloopref"><command>until</command></term>
<indexterm>
<primary>until</primary>
</indexterm>
<indexterm>
<primary>do</primary>
</indexterm>
<indexterm>
<primary>done</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>until</secondary>
</indexterm>
<listitem>
<para>This construct tests for a condition at the top of a loop, and keeps
looping as long as that condition is false (opposite of
<command>while</command> loop).</para>
<para><cmdsynopsis>
<command>until</command>
<arg choice="opt"><replaceable>condition-is-true</replaceable></arg><sbr>
<arg choice="plain">do</arg><sbr>
<arg choice="plain" rep="repeat"><replaceable>&nbsp;command</replaceable></arg><sbr>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<para>Note that an <command>until</command> loop tests for the
terminating condition at the top of the loop, differing from a
similar construct in some programming languages.</para>
<para>As is the case with <token>for/in</token> loops, placing the
<command>do</command> on the same line as the condition test
requires a semicolon.</para>
<para><cmdsynopsis>
<command>until</command>
<arg choice="opt"><replaceable>condition-is-true</replaceable></arg>
<arg choice="plain">;</arg>
<arg choice="plain">do</arg>
</cmdsynopsis></para>
<example id="ex27">
<title><command>until</command> loop</title>
<programlisting>&ex27;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Loops -->
<sect1 id="nestedloops">
<title>Nested Loops</title>
<para>A nested loop is a loop within a loop, an inner loop within the
body of an outer one. What happens is that the first pass of the
outer loop triggers the inner loop, which executes to completion.
Then the second pass of the outer loop triggers the inner loop
again. This repeats until the outer loop finishes. Of course, a
<command>break</command> within either the inner or outer loop may
interrupt this process.</para>
<example id="nestedloop">
<title>Nested Loop</title>
<programlisting>&nestedloop;</programlisting>
</example>
<para>See <xref linkend="bubble"> for an illustration of nested
<quote>while</quote> loops, and <xref linkend="ex68"> to see a
<quote>while</quote> loop nested inside an <quote>until</quote>
loop.</para>
</sect1> <!-- Nested Loops -->
<sect1 id="loopcontrol">
<title>Loop Control</title>
<variablelist id="brkcont">
<title><anchor id="brkcont1">Commands Affecting Loop Behavior</title>
<varlistentry>
<indexterm>
<primary>break</primary>
</indexterm>
<indexterm>
<primary>continue</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>break</secondary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>continue</secondary>
</indexterm>
<term><command>break</command></term>
<term><command>continue</command></term>
<listitem>
<para>The <command>break</command> and <command>continue</command>
loop control commands
<footnote><para>These are shell <link
linkend="builtinref">builtins</link>, whereas other
loop commands, such as <command>while</command>
and <command>case</command>, are <link
linkend="keywordref">keywords</link>.</para></footnote>
correspond exactly to their counterparts in
other programming languages. The <command>break</command> command
terminates the loop (breaks out of it), while <command>continue</command>
causes a jump to the next iteration of the loop, skipping all the
remaining commands in that particular loop cycle.</para>
<example id="ex28">
<title>Effects of <command>break</command> and
<command>continue</command> in a loop</title>
<programlisting>&ex28;</programlisting>
</example>
<para>The <command>break</command> command may optionally take a
parameter. A plain <command>break</command> terminates
only the innermost loop in which it is embedded,
but a <command>break N</command> breaks out of
<parameter>N</parameter> levels of loop.</para>
<example id="breaklevels">
<title>Breaking out of multiple loop levels</title>
<programlisting>&breaklevels;</programlisting>
</example>
<para>The <command>continue</command> command, similar to
<command>break</command>, optionally takes a parameter. A
plain <command>continue</command> cuts short the
current iteration within its loop and begins the next.
A <command>continue N</command> terminates all remaining
iterations at its loop level and continues with the
next iteration at the loop <option>N</option> levels
above.</para>
<example id="continuelevels">
<title>Continuing at a higher loop level</title>
<programlisting>&continuelevels;</programlisting>
</example>
<caution><para>The <command>continue N</command> construct is
difficult to understand and tricky to use in any meaningful
context. It is probably best avoided.</para></caution>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Loop Control Commands -->
<sect1 id="testbranch">
<title>Testing and Branching</title>
<para>The <command>case</command> and <command>select</command>
constructs are technically not loops, since they do not iterate the
execution of a code block. Like loops, however, they direct
program flow according to conditions at the top or bottom of
the block.</para>
<variablelist id="caseesac">
<title><anchor id="caseesac1">Controlling program flow in a code
block</title>
<varlistentry>
<term><command>case (in) / esac</command></term>
<indexterm>
<primary>case</primary>
</indexterm>
<indexterm>
<primary>in</primary>
</indexterm>
<indexterm>
<primary>esac</primary>
</indexterm>
<indexterm>
<primary>switch</primary>
</indexterm>
<indexterm>
<primary>;;</primary>
</indexterm>
<indexterm>
<primary>menus</primary>
</indexterm>
<listitem>
<para>The <command>case</command> construct is the shell equivalent
of <command>switch</command> in C/C++.
It permits branching to one of a number of code blocks, depending
on condition tests. It serves as a kind of shorthand for multiple
<token>if/then/else</token> statements and is an appropriate tool
for creating menus.</para>
<para><cmdsynopsis>
<command>case</command>
<arg choice="plain">"$<replaceable>variable</replaceable>"</arg>
<arg choice="plain">in</arg><sbr><sbr>
<arg choice="plain">&nbsp;"$<replaceable>condition1</replaceable>" )</arg><sbr>
<arg choice="plain" rep="repeat">&nbsp;<replaceable>command</replaceable></arg><sbr>
<arg choice="plain">&nbsp;;;</arg><sbr><sbr>
<arg choice="plain">&nbsp;"$<replaceable>condition2</replaceable>" )</arg><sbr>
<arg choice="plain" rep="repeat">&nbsp;<replaceable>command</replaceable></arg><sbr>
<arg choice="plain">&nbsp;;;</arg><sbr><sbr>
<arg choice="plain">esac</arg>
</cmdsynopsis></para>
<note><para>
<itemizedlist>
<listitem><para>Quoting the variables is not mandatory, since
word splitting does not take place.</para>
</listitem>
<listitem><para>Each test line ends with a right paren <token>)</token>.</para>
</listitem>
<listitem><para>Each condition block ends with a <emphasis>double</emphasis>
semicolon <token>;;</token>.</para>
</listitem>
<listitem><para>The entire <command>case</command> block terminates with an
<command>esac</command> (<wordasword>case</wordasword> spelled
backwards).</para>
</listitem>
</itemizedlist>
</para></note>
<example id="ex29">
<title>Using <command>case</command></title>
<programlisting>&ex29;</programlisting>
</example>
<example id="ex30">
<title>Creating menus using <command>case</command></title>
<programlisting>&ex30;</programlisting>
</example>
<para>An exceptionally clever use of <command>case</command>
involves testing for command-line parameters.
<programlisting>#! /bin/bash
case "$1" in
"") echo "Usage: ${0##*/} &lt;filename&gt;"; exit 65;; # No command-line parameters,
# or first parameter empty.
# Note that ${0##*/} is ${var##pattern} param substitution. Net result is $0.
-*) FILENAME=./$1;; # If filename passed as argument ($1) starts with a dash,
# replace it with ./$1
# so further commands don't interpret it as an option.
* ) FILENAME=$1;; # Otherwise, $1.
esac</programlisting></para>
<example id="casecmd">
<title>Using command substitution to generate the
<command>case</command> variable</title>
<programlisting>&casecmd;</programlisting>
</example>
<para>A <command>case</command> construct can filter strings for
<link linkend="globbingref">globbing</link> patterns.</para>
<example id="isalpha">
<title>Checking for alphabetic input</title>
<programlisting>&isalpha;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="selectref"><command>select</command></term>
<indexterm>
<primary>select</primary>
</indexterm>
<indexterm>
<primary>menus</primary>
</indexterm>
<listitem>
<para>The <command>select</command> construct, adopted from the Korn
Shell, is yet another tool for building menus.</para>
<para><cmdsynopsis>
<command>select</command>
<arg choice="plain"><replaceable>variable</replaceable></arg>
<arg choice="opt">in <replaceable>list</replaceable></arg><sbr>
<arg choice="plain">do</arg><sbr>
<arg choice="plain" rep="repeat">&nbsp;<replaceable>command</replaceable></arg><sbr>
<arg choice="plain">&nbsp;break</arg><sbr>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<para>This prompts the user to enter one of the choices presented in the
variable list. Note that <command>select</command> uses the
<varname>PS3</varname> prompt (<prompt>#? </prompt>) by default,
but that this may be changed.</para>
<example id="ex31">
<title>Creating menus using <command>select</command></title>
<programlisting>&ex31;</programlisting>
</example>
<para>If <userinput>in <replaceable>list</replaceable></userinput> is
omitted, then <command>select</command> uses the list of command
line arguments (<varname>$@</varname>) passed to the script or to
the function in which the <command>select</command> construct is
embedded.</para>
<para>Compare this to the behavior of a
<cmdsynopsis>
<command>for</command>
<arg choice="plain"><replaceable>variable</replaceable></arg>
<arg choice="opt">in <replaceable>list</replaceable></arg>
</cmdsynopsis>
construct with the
<userinput>in <replaceable>list</replaceable></userinput>
omitted.</para>
<example id="ex32">
<title>Creating menus using <command>select</command> in a function</title>
<programlisting>&ex32;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Testing and Branching -->
</chapter> <!-- Loops -->
<chapter id="internal">
<title>Internal Commands and Builtins</title>
<indexterm>
<primary>builtin</primary>
</indexterm>
<para><anchor id="builtinref">A <firstterm>builtin</firstterm>
is a command contained within the Bash tool set, literally
<emphasis>built in</emphasis>. A builtin may be a synonym to
a system command of the same name, but Bash reimplements it
internally.
<footnote><para>This is either for performance reasons (builtins
execute much faster than external commands, which usually
require <emphasis>forking</emphasis> off a process) or
because a particular builtin needs direct access to the
shell internals.</para></footnote>
For example, the Bash <command>echo</command> command is not the
same as <filename>/bin/echo</filename>, although their behavior
is almost identical.</para>
<para><anchor id="keywordref">A <firstterm>keyword</firstterm>
is a <emphasis>reserved</emphasis> 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, <quote><token>for</token></quote>,
<quote><token>while</token></quote> and
<quote><token>!</token></quote> are keywords. Similar to a
<emphasis>builtin</emphasis>, a keyword is hard-coded into
Bash.</para>
<variablelist id="intio">
<title><anchor id="intio1">I/O</title>
<varlistentry>
<term><anchor id="echoref"><command>echo</command></term>
<indexterm>
<primary>echo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>echo</secondary>
</indexterm>
<listitem>
<para>prints (to <filename>stdout</filename>) an expression
or variable (see <xref linkend="ex9">).
<programlisting>echo Hello
echo $a</programlisting></para>
<para>An <command>echo</command> requires the
<option>-e</option> option to print escaped characters. See
<xref linkend="escaped">.</para>
<para>Normally, each <command>echo</command> command prints
a terminal newline, but the <option>-n</option> option
suppresses this.</para>
<note>
<para>An <command>echo</command> can be used to feed a
sequence of commands down a pipe.</para>
<para><programlisting>if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]]
then
echo "$VAR contains the substring sequence \"txt\""
fi</programlisting></para>
</note>
<note><para>An <command>echo</command>, in combination with
<link linkend="commandsubref">command substitution</link>
can set a variable.</para> <para><userinput>a=`echo
"HELLO" | tr A-Z a-z`</userinput></para>
<para>See also <xref linkend="lowercase">, <xref
linkend="ex57">, <xref linkend="monthlypmt">, and <xref
linkend="base">.</para></note>
<caution><para>Be aware that <command>echo `command`</command>
deletes any linefeeds that the output
of <replaceable>command</replaceable>
generates. Since <varname>$IFS</varname> normally
contains <token>\n</token> as one of its set of <link
linkend="whitespaceref">whitespace</link> characters, Bash
segments the output of <replaceable>command</replaceable>
at linefeeds into arguments to <command>echo</command>,
which then emits these arguments separated by
spaces.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput> printf '\n\n1\n2\n3\n\n\n\n'</userinput>
<computeroutput>
1
2
3
</computeroutput>
<prompt>bash $</prompt>
<prompt>bash$ </prompt><userinput>echo "`printf '\n\n1\n2\n3\n\n\n\n'`"</userinput>
<computeroutput>
1
2
3
</computeroutput>
<prompt>bash $</prompt>
</screen>
</para>
</caution>
<note>
<para>This command is a shell builtin, and not the same as
<filename>/bin/echo</filename>, although its behavior is
similar.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>type -a echo</userinput>
<computeroutput>echo is a shell builtin
echo is /bin/echo</computeroutput>
</screen>
</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>printf</command></term>
<indexterm>
<primary>printf</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>printf</secondary>
</indexterm>
<listitem>
<para>The <command>printf</command>, formatted print, command is an
enhanced <command>echo</command>. It is a limited variant of the
C language <function>printf</function>, and the syntax is somewhat
different.</para>
<cmdsynopsis>
<command>printf</command>
<arg choice=plain rep=repeat><replaceable>format-string</replaceable></arg>
<arg choice=plain rep=repeat><replaceable>parameter</replaceable></arg>
</cmdsynopsis>
<para>This is the Bash builtin version
of the <filename>/bin/printf</filename> or
<filename>/usr/bin/printf</filename> command. See the
<command>printf</command> manpage (of the system command)
for in-depth coverage.</para>
<caution><para>Older versions of Bash may not support
<command>printf</command>.</para></caution>
<example id="ex47">
<title><command>printf</command> in action</title>
<programlisting>&ex47;</programlisting>
</example>
<para>Formatting error messages is a useful application of
<command>printf</command></para>
<para>
<programlisting>E_BADDIR=65
var=nonexistent_directory
error()
{
printf "$@" >&2
# Formats positional params passed, and sents them to stderr.
echo
exit $E_BADDIR
}
cd $var || error $"Can't cd to %s." "$var"
# Thanks, S.C.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="readref"><command>read</command></term> <indexterm>
<primary>read</primary>
</indexterm> <indexterm>
<primary>command</primary> <secondary>read</secondary>
</indexterm> <listitem><para><quote>Reads</quote> the value
of a variable from <filename>stdin</filename>, that
is, interactively fetches input from the keyboard. The
<option>-a</option> option lets <command>read</command>
get array variables (see <xref linkend="ex67">).</para>
<example id="ex36">
<title>Variable assignment, using <command>read</command></title>
<programlisting>&ex36;</programlisting>
</example>
<para>Normally, inputting a <userinput>\</userinput>
suppresses a newline during input to
a <command>read</command>. The <option>-r</option>
option causes an inputted <userinput>\</userinput> to be
interpreted literally.</para>
<example id="readr">
<title>Multi-line input to <command>read</command></title>
<programlisting>&readr;</programlisting>
</example>
<para>The <command>read</command> command has some interesting
options that permit echoing a prompt and even reading keystrokes
without hitting <keycap>ENTER</keycap>.</para>
<para><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.</programlisting></para>
<para>The <command>read</command> command may also
<quote>read</quote> its variable value from a file
<link linkend="ioredirref">redirected</link> to
<filename>stdin</filename>. If the file contains
more than one line, only the first line is assigned
to the variable. If <command>read</command>
has more than one parameter, then each of
these variables gets assigned a successive <link
linkend="whitespaceref">whitespace-delineated</link>
string. Caution!</para>
<example id="readredir">
<title>Using <command>read</command> with
<link linkend="ioredirref">file redirection</link></title>
<programlisting>&readredir;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intfilesystem">
<title><anchor id="intfilesystem1">Filesystem</title>
<varlistentry>
<term><command>cd</command></term>
<indexterm>
<primary>cd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cd</secondary>
</indexterm>
<listitem>
<para>The familiar <command>cd</command> change directory
command finds use in scripts where execution of a command
requires being in a specified directory.</para>
<para>
<programlisting>(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)</programlisting>
[from the <link linkend="coxex">previously cited</link>
example by Alan Cox]</para>
<para>The <option>-P</option> (physical) option to
<command>cd</command> causes it to ignore symbolic
links.</para>
<para><command>cd -</command> changes to <link
linkend="oldpwd">$OLDPWD</link>, the previous working
directory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pwd2ref"><command>pwd</command></term>
<indexterm>
<primary>pwd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pwd</secondary>
</indexterm>
<indexterm>
<primary>$PWD</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PWD</secondary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>working</secondary>
</indexterm>
<listitem><para>Print Working Directory. This gives the user's
(or script's) current directory (see <xref
linkend="ex37">). The effect is identical to
reading the value of the builtin variable <link
linkend="pwdref">$PWD</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dirsd"><command>pushd</command></term>
<term><command>popd</command></term>
<term><command>dirs</command></term>
<indexterm>
<primary>pushd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pushd</secondary>
</indexterm>
<indexterm>
<primary>popd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>popd</secondary>
</indexterm>
<indexterm>
<primary>dirs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dirs</secondary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>working</secondary>
</indexterm>
<indexterm>
<primary>bookmark</primary>
</indexterm>
<listitem><para>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 stack is used to keep track of directory names.
Options allow various manipulations of the directory stack.</para>
<para><anchor id="pushdref"><userinput>pushd
dir-name</userinput> pushes the path
<replaceable>dir-name</replaceable> onto the directory
stack and simultaneously changes the current working
directory to <replaceable>dir-name</replaceable></para>
<para><anchor id="popdref"><command>popd</command> removes (pops) the top directory path
name off the directory stack and simultaneously changes the
current working directory to that directory popped from the stack.
</para>
<para><command>dirs</command> lists the contents of the directory
stack (counterpart to <link
linkend="dirstackref">$DIRSTACK</link>) A successful
<command>pushd</command> or <command>popd</command> will
automatically invoke <command>dirs</command>.</para>
<para>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 <varname>$DIRSTACK</varname> array variable,
accessible from within a script, holds the contents of
the directory stack.
</para>
<example id="ex37">
<title>Changing the current working directory </title>
<programlisting>&ex37;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intvar">
<title><anchor id="intvar1">Variables</title>
<varlistentry>
<term><anchor id="letref"><command>let</command></term>
<indexterm>
<primary>let</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>let</secondary>
</indexterm>
<listitem>
<para>The <command>let</command> command carries out arithmetic
operations on variables. In many cases, it functions as a less
complex version of <link linkend="exprref">expr</link>.</para>
<example id="ex46">
<title>Letting <command>let</command> do some arithmetic.</title>
<programlisting>&ex46;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>eval</command></term>
<indexterm>
<primary>eval</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>eval</secondary>
</indexterm>
<listitem>
<para><userinput>eval arg1, arg2, ...</userinput></para>
<para>Translates into commands the arguments in a list
(useful for code generation within a script).</para>
<example id="ex43">
<title>Showing the effect of <command>eval</command></title>
<programlisting>&ex43;</programlisting>
</example>
<example id="ex44">
<title>Forcing a log-off</title>
<programlisting>&ex44;</programlisting>
</example>
<example id="rot13_2">
<title>A version of <quote>rot13</quote></title>
<programlisting>&rot13_2;</programlisting>
</example>
<caution><para>The <command>eval</command> command can be
risky, and normally should be avoided when there
exists a reasonable alternative. An <userinput>eval
$COMMANDS</userinput> executes the contents of
<replaceable>COMMANDS</replaceable>, which may
contain such unpleasant surprises as <command>rm -rf
*</command>. Running an <command>eval</command> on
unfamiliar code written by persons unknown is living
dangerously.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="setref"><command>set</command></term>
<indexterm>
<primary>set</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>set</secondary>
</indexterm>
<listitem><para>The <command>set</command> command changes the value
of internal script variables. One use for this
is to toggle <link linkend="optionsref">option
flags</link> which help determine the behavior of the
script. Another application for it is to reset the <link
linkend="posparamref">positional parameters</link> that
a script sees as the result of a command (<userinput>set
`command`</userinput>). The script can then parse the
fields of the command output.</para>
<example id="ex34">
<title>Using <command>set</command> with positional
parameters</title> <programlisting>&ex34;</programlisting>
</example>
<para>See also <xref linkend="ex22a">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>unset</command></term>
<indexterm>
<primary>unset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unset</secondary>
</indexterm>
<listitem><para>The <command>unset</command> command deletes a
shell variable. Note that this command does not affect
positional parameters.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>unset PATH</userinput>
<prompt>bash$ </prompt><userinput>echo $PATH</userinput>
<computeroutput>
</computeroutput>
<prompt>bash$ </prompt></screen>
</para>
<example id="uns">
<title><quote>unsetting</quote> a variable</title>
<programlisting>&uns;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exportref"><command>export</command></term>
<indexterm>
<primary>export</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>export</secondary>
</indexterm>
<listitem><para>The <command>export</command> command makes
available variables to all child processes of the
running script or shell. Unfortunately, there is no way
to <command>export</command> variables back to the parent
process, to the process that called or invoked the script
or shell. One important use of <command>export</command>
command is in <link linkend="filesref">startup files</link>,
to initialize and make accessible environmental variables
to subsequent user processes.</para>
<example id="coltotaler3">
<title>Using <command>export</command> to pass a variable to an
embedded <link linkend="awkref">awk</link> script</title>
<programlisting>&coltotaler3;</programlisting>
</example>
<tip><para>It is possible to initialize and export
variables in the same operation, as in <command>export
var1=xxx</command>.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>declare</command></term>
<term><command>typeset</command></term>
<indexterm>
<primary>declare</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>declare</secondary>
</indexterm>
<indexterm>
<primary>typeset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>typeset</secondary>
</indexterm>
<listitem>
<para>The <link linkend="declareref">declare</link> and
<link linkend="declareref">typeset</link> commands specify
and/or restrict properties of variables.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>readonly</command></term>
<indexterm>
<primary>readonly</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>readonly</secondary>
</indexterm>
<listitem><para>Same as <link linkend="declareref">declare -r</link>,
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 C language
<command>const</command> type qualifier.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="getoptsx"><command>getopts</command></term>
<indexterm>
<primary>getopts</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>getopts</secondary>
</indexterm>
<indexterm>
<primary>$OPTIND</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$OPTIND</secondary>
</indexterm>
<indexterm>
<primary>$OPTARG</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$OPTARG</secondary>
</indexterm>
<listitem>
<para>This powerful tool parses command line arguments passed to the
script. This is the bash analog of the <command>getopt</command>
library function familiar to C programmers. It permits passing and
concatenating multiple options
<footnote><para>A option 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.</para></footnote>
and associated arguments to a script (for
example <userinput>scriptname -abc -e
/usr/local</userinput>).</para>
<para>The <command>getopts</command> construct uses two implicit
variables. <varname>$OPTIND</varname> is the argument
pointer (<wordasword>OPTion INDex</wordasword>)
and <varname>$OPTARG</varname> (<wordasword>OPTion
ARGument</wordasword>) the (optional) argument attached
to an option. A colon following the option name in the
declaration tags that option as having an associated
argument.</para>
<para>A <command>getopts</command> construct usually comes
packaged in a <link linkend="whileloopref">while
loop</link>, which processes the options and
arguments one at a time, then decrements the implicit
<varname>$OPTIND</varname> variable to step to the
next.</para>
<note>
<para>
<orderedlist>
<listitem>
<para>The arguments must be passed from the
command line to the script preceded by
a minus (<option>-</option>) or a plus
(<option>+</option>). It is the prefixed
<option>-</option> or <option>+</option> that lets
<command>getopts</command> recognize command-line
arguments as <emphasis>options</emphasis>.
In fact, <command>getopts</command> will not process
arguments without the prefixed <option>-</option>
or <option>+</option>, and will terminate option
processing at the first argument encountered
lacking them.</para>
</listitem>
<listitem><para>The <command>getopts</command> template
differs slightly from the standard <command>while</command>
loop, in that it lacks condition brackets.</para>
</listitem>
<listitem>
<para>The <command>getopts</command>
construct replaces the obsolete
<command>getopt</command> command.</para>
</listitem>
</orderedlist>
</para>
</note>
<para><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 &lt;grin&gt;.
</programlisting></para>
<example id="ex33">
<title>Using <command>getopts</command> to read the
options/arguments passed to a script</title>
<programlisting>&ex33;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intscrbeh">
<title><anchor id="intscrbeh1">Script Behavior</title>
<varlistentry>
<term><anchor id="sourceref"><command>source</command></term>
<term><token>.</token> (<link linkend="dotref">dot</link> command)</term>
<indexterm>
<primary>source</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>source</secondary>
</indexterm>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>.</secondary>
</indexterm>
<listitem><para>This command, when invoked from the command line, executes a script. Within
a script, a <userinput>source file-name</userinput> loads the file
<filename>file-name</filename>. This is the
shell scripting equivalent of a C/C++ <userinput>#include</userinput>
directive. It is useful in situations when multiple scripts use a
common data file or function library.</para>
<example id="ex38">
<title><quote>Including</quote> a data file</title>
<programlisting>&ex38;</programlisting>
<para>File <filename>data-file</filename> for <xref linkend="ex38">, above.
Must be present in same directory.</para>
<programlisting>&ex38bis;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>exit</command></term>
<indexterm>
<primary>exit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exit</secondary>
</indexterm>
<listitem>
<para>Unconditionally terminates a script. The
<command>exit</command> command may optionally take an
integer argument, which is returned to the shell as
the <link linkend="exitstatusref">exit status</link>
of the script. It is a good practice to end all but the
simplest scripts with an <userinput>exit 0</userinput>,
indicating a successful run.</para>
<note><para>If a script terminates with an <command>exit</command>
lacking an argument, the exit status of the script is the exit
status of the last command executed in the script, not counting
the <command>exit</command>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="execref"><command>exec</command></term>
<indexterm>
<primary>exec</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exec</secondary>
</indexterm>
<listitem>
<para>This shell builtin replaces the current process with
a specified command. Normally, when the shell encounters
a command, it forks <anchor id="forkref"> off
<footnote><para>When a command or the shell itself
initiates (or <emphasis>spawns</emphasis>) a new
subprocess to carry out a task, this is called
<emphasis>forking</emphasis>. This new process
is the <quote>child</quote>, and the process
that <emphasis>forked</emphasis> it off is the
<quote>parent</quote>. While the <emphasis>child
process</emphasis> is doing its work, the
<emphasis>parent process</emphasis> is still
running.</para></footnote>
a child process to actually execute the command. Using the
<command>exec</command> builtin, the shell does not fork,
and the command exec'ed replaces the shell. When used in
a script, therefore, it forces an exit from the script when
the <command>exec</command>'ed command terminates. For this
reason, if an <command>exec</command> appears in a script,
it would probably be the final command.</para>
<para>An <command>exec</command> also serves to reassign <link
linkend="fdref">file descriptors</link>. <userinput>exec
&lt;zzz-file</userinput> replaces <filename>stdin</filename>
with the file <filename>zzz-file</filename> (see <xref
linkend="redir1">).</para>
<example id="ex54">
<title>Effects of <command>exec</command></title>
<programlisting>&ex54;</programlisting>
</example>
<note><para>The <option>-exec</option> option to
<link linkend="findref">find</link> is
<replaceable>not</replaceable> the same as the
<command>exec</command> shell builtin.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>shopt</command></term>
<indexterm>
<primary>shopt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>shopt</secondary>
</indexterm>
<listitem>
<para>This command permits changing shell options on the fly (see
<xref linkend="al"> and <xref linkend="unal">). It often
appears in the Bash <link linkend="filesref">startup
files</link>, but also has its uses in scripts. Needs
<link linkend="bash2ref">version 2</link> or later of Bash.
<programlisting>shopt -s cdspell
# Allows minor misspelling directory names with 'cd'
command.</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intcommand">
<title><anchor id="intcommand1">Commands</title>
<varlistentry>
<term><anchor id="trueref"><command>true</command></term>
<indexterm>
<primary>true</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>true</secondary>
</indexterm>
<listitem><para>A command that returns a successful
(<returnvalue>zero</returnvalue>) <link
linkend="exitstatusref">exit status</link>, but does
nothing else.
</para>
<para><programlisting># Endless loop
while true # alias for ":"
do
operation-1
operation-2
...
operation-n
# Need a way to break out of loop.
done</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>false</command></term>
<indexterm>
<primary>false</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>false</secondary>
</indexterm>
<listitem><para>A command that returns an unsuccessful <link
linkend="exitstatusref">exit status</link>,
but does nothing else.</para>
<para><programlisting># Null loop
while false
do
# The following code will not execute.
operation-1
operation-2
...
operation-n
# Nothing happens!
done
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>type [cmd]</command></term>
<indexterm>
<primary>type</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>type</secondary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>which</secondary>
</indexterm>
<listitem><para>Similar to the <link
linkend="whichref">which</link> external command,
<command>type cmd</command> gives the full pathname to
<quote>cmd</quote>. Unlike <command>which</command>,
<command>type</command> is a Bash builtin. The useful
<option>-a</option> option to <command>type</command>
accesses identifies <replaceable>keywords</replaceable>
and <replaceable>builtins</replaceable>, and also locates
system commands with identical names.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>type '['</userinput>
<computeroutput>[ is a shell builtin</computeroutput>
<prompt>bash$ </prompt><userinput>type -a '['</userinput>
<computeroutput>[ is a shell builtin
[ is /usr/bin/[</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>hash [cmds]</command></term>
<indexterm>
<primary>hash</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hash</secondary>
</indexterm>
<indexterm>
<primary>$PATH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PATH</secondary>
</indexterm>
<listitem><para>Record the path name of specified commands (in the
shell hash table), so the shell or script will not need to search
the <varname>$PATH</varname> on subsequent calls to those
commands. When <command>hash</command> is called with no
arguments, it simply lists the commands that have been hashed.
The <option>-r</option> option resets the hash table.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>help</command></term>
<indexterm>
<primary>help</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary></secondary>
</indexterm>
<listitem>
<para><command>help</command> COMMAND looks up
a short usage summary of the shell builtin COMMAND. This is
the counterpart to <link linkend="whatisref">whatis</link>,
but for builtins.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>help exit</userinput>
<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.</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<sect1>
<title>Job Control Commands</title>
<para>Certain of the following job control commands take a
<quote>job identifier</quote> as an argument. See the <link
linkend="jobidtable">table</link> at end of the chapter.</para>
<variablelist id="jccommandlist">
<varlistentry>
<term><command>ps</command></term>
<indexterm>
<primary>ps</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ps</secondary>
</indexterm>
<listitem>
<para><replaceable>P</replaceable>rocess
<replaceable>S</replaceable>tatistics: lists currently
executing processes by owner and PID (process id). This
is usually invoked with <option>ax</option> options,
and may be piped to <link linkend="grepref">grep</link>
or <link linkend="sedref">sed</link> to search for a
specific process (see <xref linkend="ex44"> and <xref
linkend="pidid">).</para>
<screen><prompt>bash$ </prompt><userinput> ps ax | grep sendmail</userinput>
<computeroutput>295 ? S 0:00 sendmail: accepting connections on port 25</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>pstree</command></term>
<indexterm>
<primary>pstree</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pstree</secondary>
</indexterm>
<listitem>
<para>Lists currently executing processes in
<quote>tree</quote> format. The <option>-p</option> option
shows the PIDs, as well as the process names.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>jobs</command></term>
<indexterm>
<primary>jobs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>jobs</secondary>
</indexterm>
<indexterm>
<primary>ps</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ps</secondary>
</indexterm>
<listitem>
<para>Lists the jobs running in the background, giving the job number.
Not as useful as <command>ps</command>.</para>
<note>
<para>It is all too easy to confuse
<emphasis>jobs</emphasis> and
<emphasis>processes</emphasis>. Certain <link
linkend="builtinref">builtins</link>, such as
<command>kill</command>, <command>disown</command>, and
<command>wait</command> accept either a job number or a
process number as an argument. The <command>fg</command>,
<command>bg</command> and <command>jobs</command>
commands accept only a job number.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>sleep 100 &</userinput>
<computeroutput>[1] 1384</computeroutput>
<prompt>bash $ </prompt><userinput>jobs</userinput>
<computeroutput>[1]+ Running sleep 100 &</computeroutput></screen>
</para>
<para><quote>1</quote> is the job number (jobs are
maintained by the current shell), and <quote>1384</quote>
is the process number (processes are maintained by
the system). To kill this job/process, either a <command>kill
%1</command> or a <command>kill 1384</command> works.</para>
<para><emphasis>Thanks, S.C.</emphasis></para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>disown</command></term>
<indexterm>
<primary>disown</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>disown</secondary>
</indexterm>
<listitem>
<para>Remove job(s) from the shell's table of active jobs.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>fg</command></term>
<term><command>bg</command></term>
<indexterm>
<primary>fg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>foreground</secondary>
</indexterm>
<indexterm>
<primary>background</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bg</secondary>
</indexterm>
<listitem>
<para>The <command>fg</command> command switches a job
running in the background into the foreground. The
<command>bg</command> command restarts a suspended job, and
runs it in the background. If no job number is specified,
then the <command>fg</command> or <command>bg</command>
command acts upon the currently running job.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="waitref"><command>wait</command></term>
<indexterm>
<primary>wait</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wait</secondary>
</indexterm>
<listitem>
<para>Stop script execution until all jobs running in
background have terminated, or until the job number or
process id specified as an option terminates. Returns the <link
linkend="exitstatusref">exit status</link> of waited-for
command.</para>
<para>You may use the <command>wait</command> command
to prevent a script from exiting before a background
job finishes executing (this would create a dreaded
orphan process).</para>
<example id="ex39">
<title>Waiting for a process to finish before proceeding</title>
<programlisting>&ex39;</programlisting>
</example>
<para>Optionally, <command>wait</command> can take a job
identifier as an argument, for example,
<command>wait%1</command> or <command>wait
$PPID</command>. See the <link linkend="jobidtable">job
id table</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>suspend</command></term>
<indexterm>
<primary>suspend</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>suspend</secondary>
</indexterm>
<listitem>
<para>This has a similar effect to
<keycombo><keycap>Control</keycap><keycap>Z</keycap></keycombo>,
but it suspends the shell (the shell's parent process should
resume it at an appropriate time).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>logout</command></term>
<indexterm>
<primary>logout</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>log out</secondary>
</indexterm>
<listitem>
<para>Exit a login shell, optionally specifying an <link
linkend="exitstatusref">exit status</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="timesref"><command>times</command></term>
<indexterm>
<primary>times</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>times</secondary>
</indexterm>
<listitem>
<para>Gives statistics on the system time used in executing commands, in the
following form:
<screen><computeroutput>0m0.020s 0m0.020s</computeroutput></screen>
This capability is of very limited value, since it is uncommon to
profile and benchmark shell scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="killref"><command>kill</command></term>
<indexterm>
<primary>kill</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>kill</secondary>
</indexterm>
<listitem>
<para>Forcibly terminate a process by sending it an
appropriate <emphasis>terminate</emphasis> signal (see <xref
linkend="killprocess">).</para>
<note><para><userinput>kill -l</userinput> lists all the
<link linkend="signald">signals</link>. A <userinput>kill
-9</userinput> is a <quote>sure kill</quote>, which will
usually terminate a process that stubbornly refuses to
die with a plain <command>kill</command>. Sometimes, a
<userinput>kill -15</userinput> works. A <quote>zombie
process</quote>, that is, a process whose <link
linkend="forkref">parent</link> has terminated, cannot be
killed (you can't kill something that is already dead),
but <command>init</command> will usually clean it up
sooner or later.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>command</command></term>
<indexterm>
<primary>command</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>command</secondary>
</indexterm>
<listitem><para>The <command>command COMMAND</command> directive
disables aliases and functions for the command
<quote>COMMAND</quote>.</para>
<note><para>This is one of three shell directives that
effect script command processing. The others are
<link linkend="bltref">builtin</link> and <link
linkend="enableref">enable</link>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bltref"><command>builtin</command></term>
<indexterm>
<primary>builtin</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>builtin</secondary>
</indexterm>
<listitem><para>Invoking <command>builtin
BUILTIN_COMMAND</command> runs the command
<quote>BUILTIN_COMMAND</quote> as a shell <link
linkend="builtinref">builtin</link>, temporarily disabling
both functions and external system commands with the
same name.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="enableref"><command>enable</command></term>
<indexterm>
<primary>enable</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>enable</secondary>
</indexterm>
<listitem>
<para>This either enables or disables a shell
builtin command. As an example, <command>enable -n
kill</command> disables the shell builtin <link
linkend="killref">kill</link>, so that when Bash
subsequently encounters <command>kill</command>, it invokes
<filename>/bin/kill</filename>.</para>
<para><anchor id="enableref1">The <option>-a</option>
option to <command>enable</command> lists all the
shell builtins, indicating whether or not they
are enabled. The <option>-f filename</option>
option lets <command>enable</command> load a <link
linkend="builtinref">builtin</link> as a shared library
(DLL) module from a properly compiled object file.
<footnote><para>This is not portable to all
systems.</para></footnote>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>autoload</command></term>
<indexterm>
<primary>autoload</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>autoloader</secondary>
</indexterm>
<listitem>
<para>This is a port to Bash of the
<emphasis>ksh</emphasis> autoloader. With
<command>autoload</command> in place, a function with
an <quote>autoload</quote> declaration will load from an
external file at its first invocation.
<footnote><para>The same effect as
<command>autoload</command> can be achieved with <link
linkend="declareref">typeset -fu</link>.</para></footnote>
This saves system resources.</para>
<para>Note that <command>autoload</command> is not a part of the
core Bash installation. It needs to be loaded in with
<command>enable -f</command> (see above).</para>
</listitem>
</varlistentry>
</variablelist>
<table id="jobidtable">
<title>Job Identifiers</title>
<tgroup cols="2">
<thead>
<row>
<entry>Notation</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>%N</option></entry>
<entry>Job number [N]</entry>
</row>
<row>
<entry><option>%S</option></entry>
<entry>Invocation (command line) of job begins with string <emphasis>S</emphasis></entry>
</row>
<row>
<entry><option>%?S</option></entry>
<entry>Invocation (command line) of job contains within it string <emphasis>S</emphasis></entry>
</row>
<row>
<entry><option>%%</option></entry>
<entry><quote>current</quote> job (last job stopped in
foreground or started in background)</entry>
</row>
<row>
<entry><option>%+</option></entry>
<entry><quote>current</quote> job (last job stopped in
foreground or started in background)</entry>
</row>
<row>
<entry><option>%-</option></entry>
<entry>Last job</entry>
</row>
<row>
<entry><option>$!</option></entry>
<entry>Last background process</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1> <!-- Job Control Commands -->
</chapter> <!-- Internal Commands and Builtins -->
<chapter id="external">
<title>External Filters, Programs and Commands</title>
<para>Standard UNIX commands make shell scripts more versatile. The
power of scripts comes from coupling system commands and shell
directives with simple programming constructs.</para>
<sect1 id="basic">
<title>Basic Commands</title>
<variablelist id="basiccommands">
<title><anchor id="basiccommands1">Command Listing</title>
<varlistentry>
<term><command>ls</command></term>
<indexterm>
<primary>ls</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ls</secondary>
</indexterm>
<listitem>
<para>The basic file <quote>list</quote> command. It is all too easy
to underestimate the power of this humble command. For
example, using the <option>-R</option>, recursive option,
<command>ls</command> provides a tree-like listing of
a directory structure. Other interesting options are
<option>-S</option>, sort listing by file size, and
<option>-t</option>, sort by file modification time.</para>
<example id="ex40">
<title>Using <command>ls</command> to create a table of contents
for burning a <abbrev>CDR</abbrev> disk</title>
<programlisting>&ex40;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cat</command></term>
<term><command>tac</command></term>
<indexterm>
<primary>cat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cat</secondary>
</indexterm>
<indexterm>
<primary>tac</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tac</secondary>
</indexterm>
<listitem>
<para><command>cat</command>, an acronym for
<wordasword>concatenate</wordasword>,
lists a file to <filename>stdout</filename>. When
combined with redirection (<token>></token> or
<token>>></token>), it is commonly used to concatenate
files.
<programlisting>cat filename cat file.1 file.2 file.3 &gt; file.123</programlisting>
The <option>-n</option>
option to <command>cat</command> inserts consecutive
numbers before all lines of the target file(s). The
<option>-b</option> option numbers only the non-blank
lines. The <option>-v</option> option echoes nonprintable
characters, using <token>^</token> notation.</para>
<para>See also <xref linkend="lnum"> and <xref linkend="rot13">.</para>
<para><command>tac</command>, is the inverse of
<wordasword>cat</wordasword>, listing a file backwards from its end.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rev</command></term>
<indexterm>
<primary>rev</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rev</secondary>
</indexterm>
<listitem>
<para>reverses each line of a file, and outputs to
<filename>stdout</filename>. This is not the same effect
as <command>tac</command>, as it preserves the order of
the lines, but flips each one around.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cat file1.txt</userinput>
<computeroutput>This is line 1.
This is line 2.</computeroutput>
<prompt>bash$ </prompt><userinput>tac file1.txt</userinput>
<computeroutput>This is line 2.
This is line 1.</computeroutput>
<prompt>bash$ </prompt><userinput>rev file1.txt</userinput>
<computeroutput>.1 enil si sihT
.2 enil si sihT</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cp</command></term>
<indexterm>
<primary>cp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cp</secondary>
</indexterm>
<listitem>
<para>This is the file copy command. <userinput>cp file1
file2</userinput> copies <filename>file1</filename>
to <filename>file2</filename>, overwriting
<filename>file2</filename> if it already exists (see <xref
linkend="ex42">).</para>
<tip><para>Particularly useful are the <option>-a</option>
archive flag (for copying an entire directory tree)
and the <option>-r</option> and <option>-R</option>
recursive flags.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mv</command></term>
<listitem>
<para>This is the file move command. It is equivalent to a combination of
<command>cp</command> and <command>rm</command>. It may be used to
move multiple files to a directory. For some examples of using
<command>mv</command> in a script, see <xref linkend="rfe"> and
<xref linkend="rn">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rm</command></term>
<indexterm>
<primary>rm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rm</secondary>
</indexterm>
<listitem>
<para>Delete (remove) a file or files. The <option>-f</option>
forces removal of even readonly files.</para>
<warning><para>When used with the recursive flag
<option>-r</option>, this command removes files all the way
down the directory tree.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rmdir</command></term>
<indexterm>
<primary>rmdir</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rmdir</secondary>
</indexterm>
<listitem>
<para>Remove directory. The directory must be empty of
all files, including <anchor id="dotfilesref"> invisible
<quote>dotfiles</quote>,
<footnote><para>These are files whose names begin with
a dot, such as <filename>~/.Xdefaults</filename>. Such
filenames do not show up in a normal
<command>ls</command> listing, and they cannot
be deleted by an accidental <command>rm -rf
*</command>. Dotfiles are generally used as
setup and configuration files in a user's home
directory.</para></footnote>
for this command to succeed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mkdir</command></term>
<indexterm>
<primary>mkdir</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkdir</secondary>
</indexterm>
<listitem>
<para>Make directory, creates a new directory.
<userinput>mkdir -p project/programs/December</userinput>
creates the named directory. The
<replaceable>-p</replaceable> option automatically creates
any necessary parent directories.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="chmodref"><command>chmod</command></term>
<indexterm>
<primary>chmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chmod</secondary>
</indexterm>
<listitem>
<para>Changes the attributes of an existing file (see <xref
linkend="ex44">).</para>
<para><programlisting>chmod +x filename
# Makes "filename" executable for all users.
chmod u+s filename
# Sets "suid" bit on "filename" permissions.
# An ordinary user may execute "filename" with same privileges as the file's owner.
# (This does not apply to shell scripts.)</programlisting></para>
<para><programlisting>chmod 644 filename
# Makes "filename" readable/writable to owner, readable to
# others
# (octal mode).</programlisting></para>
<para><programlisting>chmod 1777 directory-name
# Gives everyone read, write, and execute permission in directory,
# however also sets the "sticky bit".
# This means that only the owner of the directory,
# owner of the file, and, of course, root
# can delete any particular file in that directory.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>chattr</command></term>
<indexterm>
<primary>chattr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chattr</secondary>
</indexterm>
<listitem>
<para>Change file attributes. This has the same effect
as <command>chmod</command> above, but with a
different invocation syntax, and it works only on an
<emphasis>ext2</emphasis> filesystem.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ln</command></term>
<listitem>
<para>Creates links to pre-existings files. Most often used
with the <option>-s</option>, symbolic or
<quote>soft</quote> link flag. This permits referencing
the linked file by more than one name and is a superior
alternative to aliasing (see <xref linkend="ex18">).</para>
<para><userinput>ln -s oldfile newfile</userinput>
links the previously existing
<filename>oldfile</filename> to the newly created link,
<filename>newfile</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Basic Commands -->
<sect1 id="moreadv">
<title>Complex Commands</title>
<variablelist id="cclisting">
<title><anchor id="cclisting1">Command Listing</title>
<varlistentry>
<term><anchor id="findref"><command>find</command></term>
<indexterm>
<primary>find</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>find</secondary>
</indexterm>
<indexterm>
<primary>{}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>\;</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\;</secondary>
</indexterm>
<listitem>
<para>-exec <replaceable>COMMAND</replaceable> \;</para>
<para>Carries out <replaceable>COMMAND</replaceable> on
each file that <command>find</command> scores a hit
on. <replaceable>COMMAND</replaceable> terminates
with <token>\;</token> (the <token>;</token>
is escaped to make certain the shell passes it to
<command>find</command> literally, which concludes the
command sequence). If <replaceable>COMMAND</replaceable>
contains <token>{}</token>, then <command>find</command>
substitutes the full path name of the selected file.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>find ~/ -name '*.txt'</userinput>
<computeroutput>/home/bozo/.kde/share/apps/karm/karmdata.txt
/home/bozo/misc/irmeyc.txt
/home/bozo/test-scripts/1.txt</computeroutput>
</screen>
</para>
<para>
<programlisting>find /home/bozo/projects -mtime 1
# Lists all files in /home/bozo/projects directory tree
# that were modified within the last day.</programlisting>
</para>
<para><programlisting>find /etc -exec grep '[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*' {} \;
# Finds all IP addresses (xxx.xxx.xxx.xxx) in /etc directory files.
# There a few extraneous hits - how can they be filtered out?
# Perhaps by:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
# [:digit:] is one of the character classes
# introduced with the POSIX 1003.2 standard.
# Thanks, S.C.
</programlisting></para>
<caution><para>The <option>-exec</option> option to
<command>find</command> should not be confused with the <link
linkend="execref">exec</link> shell builtin.</para></caution>
<example id="ex57">
<title><command>Badname</command>, eliminate file names
in current directory containing bad characters and <link
linkend="whitespaceref">whitespace</link>.</title>
<programlisting>&ex57;</programlisting>
</example>
<para>See <xref linkend="ex48">, <xref linkend="ex58">,
and <xref linkend="findstring"> for scripts using
<command>find</command>. Its manpage provides more detail
on this complex and powerful command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>xargs</command></term>
<indexterm>
<primary>xargs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>xargs</secondary>
</indexterm>
<listitem>
<para>A filter for feeding arguments to a command, and also a tool for
assembling the commands themselves. It breaks a data
stream into small enough chunks for filters and commands
to process. Consider it as a powerful replacement
for backquotes. In situations where backquotes fail
with a <errorname>too many arguments</errorname>
error, substituting <command>xargs</command> often
works. Normally, <command>xargs</command> reads from
<filename>stdin</filename> or from a pipe, but it can also
be given the output of a file.</para>
<para>The default command for <command>xargs</command> is
<link linkend="echoref">echo</link>.</para>
<para><userinput>ls | xargs -p -l gzip</userinput> <link
linkend="gzipref">gzips</link> every file in current
directory, one at a time, prompting before each
operation.</para>
<tip>
<para>An interesting <command>xargs</command>
option is <option>-n <replaceable>XX</replaceable></option>,
which limits the number of arguments passed
to <replaceable>XX</replaceable>.</para>
<para><userinput>ls | xargs -n 8 echo</userinput> lists the files in the
current directory in <literal>8</literal> columns.</para>
</tip>
<tip>
<para>Another useful option is
<option>-0</option>, in combination with <command>find
-print0</command> or <command>grep -lZ</command>. This
allows handling arguments containing whitespace or
quotes.</para>
<para>
<userinput>find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f</userinput>
</para>
<para>
<userinput>grep -rliwZ GUI / | xargs -0 rm -f</userinput>
</para>
<para>Either of the above will remove any file containing <quote>GUI</quote>.
<emphasis>(Thanks, S.C.)</emphasis></para>
</tip>
<example id="ex41">
<title>Log file using <command>xargs</command> to monitor system log</title>
<programlisting>&ex41;</programlisting>
</example>
<example id="ex42">
<title><command>copydir</command>, copying files in current
directory to another, using <command>xargs</command></title>
<programlisting>&ex42;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exprref"><userinput>expr</userinput></term>
<indexterm>
<primary>expr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>expr</secondary>
</indexterm>
<listitem>
<para>All-purpose expression evaluator:
Concatenates and evaluates the arguments according
to the operation given (arguments must be separated
by spaces). Operations may be arithmetic, comparison,
string, or logical.</para>
<variablelist>
<varlistentry>
<term><userinput>expr 3 + 5</userinput></term>
<listitem>
<para>returns <literal>8</literal></para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>expr 5 % 3</userinput></term>
<listitem>
<para>returns 2</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>y=`expr $y + 1`</userinput></term>
<listitem>
<para>Increment a variable, with the same effect
as <userinput>let y=y+1</userinput> and
<userinput>y=$(($y+1))</userinput> This is an
example of <link linkend="arithexpref">arithmetic
expansion</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>z=`expr substr $string28 $position $length`</userinput></term>
<listitem>
<para>External programs, such as <link
linkend="sedref">sed</link> and
<command>Perl</command> have far superior string
parsing facilities, and it might well be advisable
to use these rather than the built-in Bash ones.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex45">
<title>Using <command>expr</command></title>
<programlisting>&ex45;</programlisting>
</example>
<important>
<para>The <link linkend="nullref">:</link> operator
can substitute for <command>match</command>. For example,
<userinput>b=`expr $a : [0-9]*`</userinput> is the
exact equivalent of <userinput>b=`expr match $a
[0-9]*`</userinput> in the above listing.</para>
<para><programlisting>&ex45a;</programlisting></para>
</important>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Complex Commands -->
<sect1 id="timedate">
<title>Time / Date Commands</title>
<variablelist id="tdlisting">
<title><anchor id="tdlisting1">Command Listing</title>
<varlistentry>
<term><command>date</command></term>
<indexterm>
<primary>date</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>date</secondary>
</indexterm>
<listitem>
<para>Simply invoked, <command>date</command> prints the date and
time to <filename>stdout</filename>. Where this command gets
interesting is in its formatting and parsing options.</para>
<example id="ex51">
<title>Using <command>date</command></title>
<programlisting>&ex51;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="timref"><command>time</command></term>
<indexterm>
<primary>time</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>time</secondary>
</indexterm>
<listitem>
<para>Outputs very verbose timing statistics for executing a command.</para>
<para><userinput>time ls -l /</userinput> gives something like this:
<screen><computeroutput>0.00user 0.01system 0:00.05elapsed 16%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (149major+27minor)pagefaults 0swaps</computeroutput></screen>
</para>
<para>See also the very similar <link
linkend="timesref">times</link> command in the previous
section.</para>
<note><para>As of <link linkend="bash2ref">version 2.0</link>
of Bash, <command>time</command> became a shell reserved word,
with slightly altered behavior in a pipeline.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="touchref"><command>touch</command></term>
<indexterm>
<primary>touch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>touch</secondary>
</indexterm>
<listitem>
<para>Utility for updating access/modification times of a
file to current system time or other specified time,
but also useful for creating a new file. The command
<userinput>touch zzz</userinput> will create a new file
of zero length, named <filename>zzz</filename>, assuming
that <filename>zzz</filename> did not previously exist.
Time-stamping empty files in this way is useful for
storing date information, for example in keeping track of
modification times on a project.
</para>
<para>The <command>touch</command> command is equivalent to
<userinput>: &gt;&gt; newfile</userinput> (for ordinary
files).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="atref"><command>at</command></term>
<indexterm>
<primary>at</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>at</secondary>
</indexterm>
<indexterm>
<primary>cron</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cron</secondary>
</indexterm>
<listitem>
<para>The <command>at</command> job control command executes a given
set of commands at a specified time. This is a user version of
<link linkend="cronref">cron</link>.</para>
<para><userinput>at 2pm January 15</userinput> prompts for a set of
commands to execute at that time. These commands may include
executable shell scripts.</para>
<para>Using either the <option>-f</option> option or input
redirection (<token><</token>), <command>at</command>
reads a command list from a file. This file can
include shell scripts, though they should, of course,
be noninteractive. Particularly clever is including the
<link linkend="runpartsref">run-parts</link> command in
the file to execute a set of scripts.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>at 2:30 am Friday < at-jobs.list</userinput>
<computeroutput>job 2 at 2000-10-27 02:30</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>batch</command></term>
<indexterm>
<primary>batch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>batch</secondary>
</indexterm>
<indexterm>
<primary>at</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>at</secondary>
</indexterm>
<listitem>
<para>The <command>batch</command> job control command is similar to
<command>at</command>, but it runs a command list when the system
load drops below <literal>.8</literal>. Like
<command>at</command>, it can read commands from a file with the
<option>-f</option> option.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cal</command></term>
<indexterm>
<primary>cal</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cal</secondary>
</indexterm>
<listitem>
<para>Prints a neatly formatted monthly calendar to
<filename>stdout</filename>. Will do current year or a large
range of past and future years.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sleep</command></term>
<indexterm>
<primary>sleep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sleep</secondary>
</indexterm>
<listitem>
<para>This is the shell equivalent of a wait loop. It pauses for a
specified number of seconds, doing nothing. This can be useful for
timing or in processes running in the background, checking for a
specific event every so often (see <xref linkend="online">).
<programlisting>sleep 3
# Pauses 3 seconds.</programlisting>
</para>
<note><para>The <command>sleep</command> command defaults to
seconds, but minute, hours, or days may also be specified.
<programlisting>sleep 3 h
# Pauses 3 hours!</programlisting>
</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>usleep</command></term>
<indexterm>
<primary>usleep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>usleep</secondary>
</indexterm>
<listitem>
<para><emphasis>Microsleep</emphasis> (the <quote>u</quote>
may be read as the Greek <quote>mu</quote>, or micro
prefix). This is the same as <command>sleep</command>,
above, but <quote>sleeps</quote> in microsecond
intervals. This can be used for fine-grain timing, or for
polling an ongoing process at very frequent intervals.
<programlisting>usleep 30
# Pauses 30 microseconds.</programlisting>
</para>
<caution><para>The <command>usleep</command> command does not
provide particularly accurate timing, and is therefore
unsuitable for critical timing loops.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><command>hwclock</command></term>
<term><command>clock</command></term>
<indexterm>
<primary>hwclock</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hwclock</secondary>
</indexterm>
<indexterm>
<primary>clock</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>clock</secondary>
</indexterm>
<listitem>
<para>The <command>hwclock</command> command accesses or
adjusts the machine's hardware clock. Some
options require root privileges. The
<filename>/etc/rc.d/rc.sysinit</filename> startup file
uses <command>hwclock</command> to set the system time
from the hardware clock at bootup.</para>
<para>The <command>clock</command> command is a synonym for
<command>hwclock</command>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Time / Date Commands -->
<sect1 id="textproc">
<title>Text Processing Commands</title>
<variablelist id="tpcommandlisting">
<title><anchor id="tpcommandlisting1">Command Listing</title>
<varlistentry>
<term><anchor id="sortref"><command>sort</command></term>
<indexterm>
<primary>sort</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sort</secondary>
</indexterm>
<listitem>
<para>File sorter, often used as a filter in a pipe. This
command can sort a text stream or file forwards or
backwards, or according to various keys or character
positions. The <emphasis>info page</emphasis> lists its
many options. See <xref linkend="findstring"> and <xref
linkend="symlinks">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tsort</command></term>
<indexterm>
<primary>tsort</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>topological sort</secondary>
</indexterm>
<listitem>
<para>Topological sort, reading in pairs of
whitespace-separated strings and sorting according to
input patterns.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="diffref"><command>diff</command></term>
<term><command>patch</command></term>
<indexterm>
<primary>diff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>diff</secondary>
</indexterm>
<indexterm>
<primary>patch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>patch</secondary>
</indexterm>
<listitem>
<para><command>diff</command>: flexible file comparison
utility. It compares the target files line-by-line
sequentially. In some applications, such as comparing
word dictionaries, it may be helpful to filter the
files through <link linkend="sortref">sort</link>
and <command>uniq</command> before piping them
to <command>diff</command>. <userinput>diff file-1
file-2</userinput> outputs the lines in the files that
differ, with carets showing which file each particular
line belongs to.</para>
<para>The <option>--side-by-side</option> option to
<command>diff</command> outputs each compared file, line by line,
in separate columns, with non-matching lines marked.</para>
<para>There are available various fancy frontends for
<command>diff</command>, such as <command>spiff</command>,
<command>wdiff</command>, <command>xdiff</command>, and
<command>mgdiff</command>. </para>
<tip><para>The <command>diff</command> command returns an exit
status of 0 if the compared files are identical, and 1 if
they differ. This permits use of <command>diff</command>
in a test construct within a shell script (see
below).</para></tip>
<para>A common use for <command>diff</command> is generating
difference files to be used with <command>patch</command>
The <option>-e</option> option outputs files suitable
for <command>ed</command> or <command>ex</command>
scripts.</para>
<para><command>patch</command>: flexible versioning
utility. Given a difference file generated by
<command>diff</command>, <command>patch</command> can
upgrade a previous version of a package to a newer version.
It is much more convenient to distribute a relatively
small <quote>diff</quote> file than the entire body of a
newly revised package. Kernel <quote>patches</quote> have
become the preferred method of distributing the frequent
releases of the Linux kernel.</para>
<para><programlisting>patch -p1 &lt;patch-file
# Takes all the changes listed in 'patch-file'
# and applies them to the files referenced therein.
# This upgrades to a newer version of the package.</programlisting></para>
<para>Patching the kernel:</para>
<para><programlisting>cd /usr/src
gzip -cd patchXX.gz | patch -p0
# Upgrading kernel source using 'patch'.
# From the Linux kernel docs "README",
# by anonymous author (Alan Cox?).</programlisting></para>
<note>
<para>The <command>diff</command> command can also
recursively compare directories (for the filenames
present).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>diff ~/notes1 ~/notes2</userinput>
<computeroutput>Only in /home/bozo/notes1: file02
Only in /home/bozo/notes1: file03
Only in /home/bozo/notes2: file04</computeroutput>
</screen>
</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>diff3</command></term>
<indexterm>
<primary>diff3</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>diff3</secondary>
</indexterm>
<listitem>
<para>An extended version of <command>diff</command> that compares
three files at a time. This command returns an exit value
of 0 upon successful execution, but unfortunately this gives
no information about the results of the comparison.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sdiff</command></term>
<indexterm>
<primary>sdiff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sdiff</secondary>
</indexterm>
<listitem>
<para>Compare and/or edit two files in order to merge
them into an output file. Because of its interactive nature,
this command would find little use in a script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cmp</command></term>
<indexterm>
<primary>cmp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cmp</secondary>
</indexterm>
<listitem>
<para>The <command>cmp</command> command is a simpler version of
<command>diff</command>, above. Whereas <command>diff</command>
reports the differences between two files,
<command>cmp</command> merely shows at what point they
differ.</para>
<note><para>Like <command>diff</command>, <command>cmp</command>
returns an exit status of 0 if the compared files are
identical, and 1 if they differ. This permits use in a test
construct within a shell script.</para></note>
<example id="filecomp">
<title>Using <command>cmp</command> to compare two files
within a script.</title>
<programlisting>&filecomp;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>comm</command></term>
<indexterm>
<primary>comm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>comm</secondary>
</indexterm>
<listitem>
<para>Versatile file comparison utility. The files must be
sorted for this to be useful.</para>
<para><command>comm
<replaceable>-options</replaceable>
<replaceable>first-file</replaceable>
<replaceable>second-file</replaceable></command></para>
<para><userinput>comm file-1 file-2</userinput> outputs three columns:
<itemizedlist>
<listitem><para>column 1 = lines unique to <filename>file-1</filename></para>
</listitem>
<listitem><para>column 2 = lines unique to <filename>file-2</filename></para>
</listitem>
<listitem><para>column 3 = lines common to both.</para>
</listitem>
</itemizedlist></para>
<para>The options allow suppressing output of one or more columns.
<itemizedlist>
<listitem><para><option>-1</option> suppresses column
<literal>1</literal></para>
</listitem>
<listitem><para><option>-2</option> suppresses column
<literal>2</literal></para>
</listitem>
<listitem><para><option>-3</option> suppresses column
<literal>3</literal></para>
</listitem>
<listitem><para><option>-12</option> suppresses both columns
<literal>1</literal> and <literal>2</literal>, etc.</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uniqref"><command>uniq</command></term>
<indexterm>
<primary>uniq</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uniq</secondary>
</indexterm>
<listitem>
<para>This filter removes duplicate lines from a sorted
file. It is often seen in a pipe coupled with
<link linkend="sortref">sort</link>.
<programlisting>cat list-1 list-2 list-3 | sort | uniq > final.list
# Concatenates the list files,
# sorts them,
# removes duplicate lines,
# and finally writes the result to an output file.</programlisting></para>
<para>The useful <option>-c</option> option prefixes each line of
the input file with the number of occurrences.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="expandref"><command>expand</command></term>
<term><command>unexpand</command></term>
<indexterm>
<primary>expand</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>expand</secondary>
</indexterm>
<indexterm>
<primary>unexpand</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unexpand</secondary>
</indexterm>
<listitem>
<para>The <command>expand</command> filter converts tabs to
spaces. It is often used in a pipe.</para>
<para>The <command>unexpand</command> filter
converts spaces to tabs. This reverses the effect of
<command>expand</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cut</command></term>
<indexterm>
<primary>cut</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cut</secondary>
</indexterm>
<indexterm>
<primary>awk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>awk</secondary>
</indexterm>
<listitem>
<para>A tool for extracting fields from files. It is similar to the
<userinput>print $N</userinput> command set in <link
linkend="awkref">awk</link>, but more limited. It may be
simpler to use <command>cut</command> in a script than
<command>awk</command>. Particularly important are the
<option>-d</option> (delimiter) and <option>-f</option>
(field specifier) options.</para>
<para>Using <command>cut</command> to obtain a listing of the
mounted filesystems:
<programlisting>cat /etc/mtab | cut -d ' ' -f1,2</programlisting></para>
<para>Using <command>cut</command> to list the OS and kernel version:
<programlisting>uname -a | cut -d" " -f1,3,11,12</programlisting></para>
<para><userinput>cut -d ' ' -f2,3 filename</userinput> is equivalent to
<userinput>awk -F'[ ]' '{ print $2, $3 }' filename</userinput></para>
<para>See also <xref linkend="base">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>colrm</command></term>
<indexterm>
<primary>colrm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>colrm</secondary>
</indexterm>
<listitem>
<para>Column removal filter. This removes columns (characters)
from a file and writes the file, lacking the range of
specified columns, back to <filename>stdout</filename>.
<userinput>colrm 2 4 &lt;filename</userinput> removes the
second through fourth characters from each line of the
text file <filename>filename</filename>.</para>
<warning><para>If the file contains tabs or nonprintable
characters, this may cause unpredictable
behavior. In such cases, consider using
<link linkend="expandref">expand</link> and
<command>unexpand</command> in a pipe preceding
<command>colrm</command>.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><command>paste</command></term>
<indexterm>
<primary>paste</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>paste</secondary>
</indexterm>
<indexterm>
<primary>cut</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cut</secondary>
</indexterm>
<listitem>
<para>Tool for merging together different files into a single,
multi-column file. In combination with
<command>cut</command>, useful for creating system log
files.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>join</command></term>
<indexterm>
<primary>join</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>join</secondary>
</indexterm>
<listitem>
<para>Consider this a special-purpose cousin of
<command>paste</command>. This powerful utility allows
merging two files in a meaningful fashion, which essentially
creates a simple version of a relational database.</para>
<para>The <command>join</command> command operates on
exactly two files, but pastes together only those lines
with a common tagged field (usually a numerical label),
and writes the result to <filename>stdout</filename>.
The files to be joined should be sorted according to the
tagged field for the matchups to work properly.</para>
<para><programlisting>File: 1.data
100 Shoes
200 Laces
300 Socks</programlisting></para>
<para><programlisting>File: 2.data
100 $40.00
200 $1.00
300 $2.00</programlisting></para>
<para>
<screen><prompt>bash$ </prompt><userinput>join 1.data 2.data</userinput>
<computeroutput>File: 1.data 2.data
100 Shoes $40.00
200 Laces $1.00
300 Socks $2.00</computeroutput>
</screen>
</para>
<note><para>The tagged field appears only once in the
output.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>head</command></term>
<indexterm>
<primary>head</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>head</secondary>
</indexterm>
<listitem>
<para>lists the beginning of a file to
<filename>stdout</filename> (the default is
<literal>10</literal> lines, but this can be changed). It
has a number of interesting options.
<example id="rnd">
<title>Generating 10-digit random numbers</title>
<programlisting>&rnd;</programlisting>
</example>
See also <xref linkend="ex52">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tail</command></term>
<indexterm>
<primary>tail</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tail</secondary>
</indexterm>
<listitem>
<para>lists the end of a file to <filename>stdout</filename>
(the default is <literal>10</literal> lines). Commonly used
to keep track of changes to a system logfile, using the
<option>-f</option> option, which outputs lines appended
to the file.</para>
<example id="ex12">
<title>Using <command>tail</command> to monitor the system log</title>
<programlisting>&ex12;</programlisting>
</example>
<para>See also <xref linkend="ex41">, <xref linkend="ex52"> and
<xref linkend="online">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="grepref"><command>grep</command></term>
<indexterm>
<primary>grep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>grep</secondary>
</indexterm>
<listitem>
<para>A multi-purpose file search tool that uses
<link linkend="regexref">regular
expressions</link>. Originally a command/filter
in the ancient <command>ed</command> line editor,
<userinput>g/re/p</userinput>, or <emphasis>global -
regular expression - print</emphasis>.</para>
<para><cmdsynopsis>
<command>grep</command> <arg
choice="plain"><replaceable>pattern</replaceable></arg>
<arg choice="opt"
rep="repeat"><replaceable>file</replaceable></arg>
</cmdsynopsis> search the files
<filename>file</filename>, etc. for occurrences of
<replaceable>pattern</replaceable>.</para>
<para><userinput>ls -l | grep '\.txt$'</userinput> has the
same effect as <userinput>ls -l *.txt</userinput> (although
symbolic links may cause problems).</para>
<para>The <option>-i</option> option causes a case-insensitive
search.</para>
<para>The <option>-l</option> option lists only the files in which
matches were found, but not the matching lines.</para>
<para>The <option>-v</option> (or <option>--invert-match</option>)
option <emphasis>filters out</emphasis> matches.
<programlisting>grep pattern1 *.txt | grep -v pattern2
# Matches all lines in "*.txt" files containing "pattern1",
# but ***not*** "pattern2".
</programlisting></para>
<para>The <option>-c</option> (<option>--count</option>)
option gives a numerical count of matches, rather than
actually listing the matches.
<programlisting>grep -c txt *.sgml # (number of occurrences of "txt" in "*.sgml" files)
# grep -cz .
# ^ dot
# means count (-c) zero-separated (-z) items matching "."
# that is, non-empty ones (containing at least 1 character).
#
printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz . # 4
printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz '$' # 5
printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -cz '^' # 5
#
printf 'a b\nc d\n\n\n\n\n\000\n\000e\000\000\nf' | grep -c '$' # 9
# By default, newline chars (\n) separate items to match.
# Note that the -z option is GNU "grep" specific.
# Thanks, S.C.</programlisting>
</para>
<para><xref linkend="online"> demonstrates how to use
<command>grep</command> to search for a word pattern in
a system log file.</para>
<para>If there is a successful match, <command>grep</command>
returns an <link linkend="exitstatusref">exit status</link>
of 0, which makes it useful in a condition test in a
script.</para>
<example id="grp">
<title>Emulating <quote>grep</quote> in a script</title>
<programlisting>&grp;</programlisting>
</example>
<note><para><anchor id="egrepref"><command>egrep</command>
is the same as <command>grep -E</command>. This
uses a somewhat different, extended set of <link
linkend="regexref">regular expressions</link>, which can
make the search somewhat more flexible.</para></note>
<note><para><command>fgrep</command> is the same as <command>grep
-F</command>. It does a literal string search (no regular
expressions), which generally speeds things up quite a
bit.</para></note>
<tip><para>To search compressed files, use
<command>zgrep</command>. It also works on non-compressed
files, though slower than plain <command>grep</command>.
This is handy for searching through a mixed set of files,
some of them compressed, some not. </para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>look</command></term>
<indexterm>
<primary>look</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>look</secondary>
</indexterm>
<listitem>
<para>The command <command>look</command> works like
<command>grep</command>, but does a lookup on
a <quote>dictionary</quote>, a sorted word list.
By default, <command>look</command> searches for a match
in <filename>/usr/dict/words</filename>, but a different
dictionary file may be specified.</para>
<example id="lookup">
<title>Checking words in a list for validity</title>
<programlisting>&lookup;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sed</command></term>
<term><command>awk</command></term>
<indexterm>
<primary>sed</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sed</secondary>
</indexterm>
<indexterm>
<primary>awk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>awk</secondary>
</indexterm>
<listitem>
<para>Scripting languages especially suited for parsing text
files and command output. May be embedded singly or in
combination in pipes and shell scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command><link linkend="sedref">sed</link></command></term>
<listitem>
<para>Non-interactive <quote>stream editor</quote>, permits using
many <command>ex</command> commands in batch mode. It
finds many uses in shell scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command><link linkend="awkref">awk</link></command></term>
<listitem>
<para>Programmable file extractor and formatter, good for
manipulating and/or extracting fields (columns) in
structured text files. Its syntax is similar to C.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>wc</command></term>
<indexterm>
<primary>wc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wc</secondary>
</indexterm>
<listitem>
<para><emphasis>wc</emphasis> gives a <quote>word count</quote> on a file or I/O stream:
<screen><prompt>bash $ </prompt><userinput>wc /usr/doc/sed-3.02/README</userinput>
<computeroutput>20 127 838 /usr/doc/sed-3.02/README</computeroutput>
[20 lines 127 words 838 characters]</screen></para>
<para><userinput>wc -w</userinput> gives only the word count.</para>
<para><userinput>wc -l</userinput> gives only the line count.</para>
<para><userinput>wc -c</userinput> gives only the character count.</para>
<para><userinput>wc -L</userinput> gives only the length of the longest line.</para>
<para>Using <command>wc</command> to count how many
<emphasis>.txt</emphasis> files are in current working directory:
<programlisting>$ ls *.txt | wc -l
# Will work as long as none of the "*.txt" files have a linefeed in their name.
# Alternative ways of doing this are:
# find . -maxdepth 1 -name \*.txt -print0 | grep -cz .
# (shopt -s nullglob; set -- *.txt; echo $#)
# Thanks, S.C.</programlisting>
</para>
<para>Using <command>wc</command> to total up the size of all the
files whose names begin with letters in the range d - h
<screen><prompt>bash$ </prompt><userinput>wc [d-h]* | grep total | awk '{print $3}'</userinput>
<computeroutput>71832</computeroutput>
</screen>
</para>
<para>See also <xref linkend="ex52"> and <xref
linkend="redir4">.</para>
<para>Certain commands include some of the
functionality of <command>wc</command> as options.
<programlisting>... | grep foo | wc -l
# This frequently used construct can be more concisely rendered.
... | grep -c foo
# Just use the "-c" (or "--count") option of grep.
# Thanks, S.C.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tr</command></term>
<indexterm>
<primary>tr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tr</secondary>
</indexterm>
<listitem>
<para>character translation filter.</para>
<caution><para><link linkend="ucref">Must use quoting and/or
brackets</link>, as appropriate. Quotes prevent the
shell from reinterpreting the special characters in
<command>tr</command> command sequences. Brackets should be
quoted to prevent expansion by the shell. </para></caution>
<para>Either <userinput>tr "A-Z" "*" &lt;filename</userinput>
or <userinput>tr A-Z \* &lt;filename</userinput> changes
all the uppercase letters in <filename>filename</filename>
to asterisks (writes to <filename>stdout</filename>).
On some systems this may not work, but <userinput>tr A-Z
'[**]'</userinput> will.</para>
<para>The <option>-d</option> option deletes a range of
characters.
<programlisting>tr -d 0-9 &lt;filename
# Deletes all digits from the file "filename".</programlisting></para>
<para>The <option>--squeeze-repeats</option> (or
<option>-s</option>) option deletes all but the
first instance of a string of consecutive characters.
This option is useful for removing excess <link
linkend="whitespaceref">whitespace</link>.
<screen><prompt>bash$ </prompt><userinput>echo "XXXXX" | tr --squeeze-repeats 'X'</userinput>
<computeroutput>X</computeroutput></screen></para>
<example id="ex49">
<title><command>toupper</command>: Transforms a file to all uppercase.</title>
<programlisting>&ex49;</programlisting>
</example>
<example id="lowercase">
<title><command>lowercase</command>: Changes all filenames in working directory to lowercase.</title>
<programlisting>&lowercase;</programlisting>
</example>
<example id="du">
<title><command>du</command>: DOS to UNIX text file conversion.</title>
<programlisting>&du;</programlisting>
</example>
<example id="rot13">
<title><command>rot13</command>: rot13, ultra-weak encryption.</title>
<programlisting>&rot13;</programlisting>
</example>
<example id="cryptoquote">
<title>Generating <quote>Crypto-Quote</quote> Puzzles</title>
<programlisting>&cryptoquote;</programlisting>
</example>
<sidebar><title><command>tr</command> variants</title>
<para>
The <command>tr</command> utility has two historic
variants. The BSD version does not use brackets
(<userinput>tr a-z A-Z</userinput>), but the SysV one does
(<userinput>tr '[a-z]' '[A-Z]'</userinput>). The GNU version
of <command>tr</command> resembles the BSD one, so quoting
letter ranges within brackets is mandatory.
</para>
</sidebar>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="foldref"><command>fold</command></term>
<indexterm>
<primary>fold</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fold</secondary>
</indexterm>
<listitem>
<para>A filter that wraps lines of input to a specified width.
This is especially useful with the <option>-s</option>
option, which breaks lines at word spaces (see <xref
linkend="ex50"> and <xref linkend="mailformat">).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>fmt</command></term>
<indexterm>
<primary>fmt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fmt</secondary>
</indexterm>
<listitem>
<para>Simple-minded file formatter, used as a filter in a
pipe to <quote>wrap</quote> long lines of text
output.</para>
<example id="ex50">
<title>Formatted file listing.</title>
<programlisting>&ex50;</programlisting>
</example>
<para>See also <xref linkend="ex41">.</para>
<tip><para>A powerful alternative to <command>fmt</command> is
Kamil Toman's <command>par</command>
utility, available from <ulink
url="http://www.cs.berkeley.edu/~amc/Par/">http://www.cs.berkeley.edu/~amc/Par/</ulink>.
</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ptx</command></term>
<indexterm>
<primary>ptx</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>index</secondary>
</indexterm>
<listitem>
<para>The <command>ptx [targetfile]</command> command
outputs a permuted index (cross-reference list) of the
targetfile. This may be further filtered and formatted in a
pipe, if necessary.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>column</command></term>
<indexterm>
<primary>column</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>column</secondary>
</indexterm>
<listitem>
<para>Column formatter. This filter transforms list-type
text output into a <quote>pretty-printed</quote> table
by inserting tabs at appropriate places.</para>
<example id="col">
<title>Using <command>column</command> to format a directory
listing</title>
<programlisting>&col;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>nl</command></term>
<indexterm>
<primary>nl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fmt</secondary>
</indexterm>
<listitem>
<para>Line numbering filter. <userinput>nl filename</userinput>
lists <filename>filename</filename> to
<filename>stdout</filename>, but inserts consecutive
numbers at the beginning of each non-blank line. If
<filename>filename</filename> omitted, operates on
<filename>stdin.</filename></para>
<example id="lnum">
<title><command>nl</command>: A self-numbering script.</title>
<programlisting>&lnum;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>pr</command></term>
<indexterm>
<primary>pr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pr</secondary>
</indexterm>
<listitem>
<para>Print formatting filter. This will paginate files
(or <filename>stdout</filename>) into sections suitable for
hard copy printing or viewing on screen. Various options
permit row and column manipulation, joining lines, setting
margins, numbering lines, adding page headers, and merging
files, among other things. The <command>pr</command>
command combines much of the functionality of
<command>nl</command>, <command>paste</command>,
<command>fold</command>, <command>column</command>, and
<command>expand</command>.</para>
<para><userinput>pr -o 5 --width=65 fileZZZ | more</userinput>
gives a nice paginated listing to screen of
<filename>fileZZZ</filename> with margins set at 5 and
65.</para>
<para>A particularly useful option is <option>-d</option>,
forcing double-spacing (same effect as <command>sed
-G</command>).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gettextref"><command>gettext</command></term>
<indexterm>
<primary>gettext</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>gettext</secondary>
</indexterm>
<listitem>
<para>A GNU utility for <link
linkend="localization">localization</link> and
translating the text output of programs into
foreign languages. While primarily intended for C
programs, <command>gettext</command> also finds
use in shell scripts. See the <replaceable>info
page</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>iconv</command></term>
<indexterm>
<primary>iconv</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>encoding</secondary>
</indexterm>
<listitem>
<para>A utility for converting file(s) to a different encoding
(character set). Its chief use is for localization.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>groff</command></term>
<term><command>gs</command></term>
<term><command>TeX</command></term>
<indexterm>
<primary>groff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>groff</secondary>
</indexterm>
<indexterm>
<primary>gs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>Postscript</secondary>
</indexterm>
<indexterm>
<primary>TeX</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>TeX</secondary>
</indexterm>
<listitem>
<para>Groff, TeX, and Postscript are text markup languages
used for preparing copy for printing or formatted video
display.</para>
<para><emphasis>Manpages</emphasis> use
<command>groff</command> (see <xref linkend="manview">).
<emphasis>Ghostscript</emphasis> (<command>gs</command>)
is a GPL Postscript interpreter. <command>TeX</command>
is Donald Knuth's elaborate typsetting system. It is
often convenient to write a shell script encapsulating
all the options and arguments passed to one of these
markup languages.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lex</command></term>
<term><command>yacc</command></term>
<indexterm>
<primary>lex</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>flex</secondary>
</indexterm>
<indexterm>
<primary>yacc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bison</secondary>
</indexterm>
<listitem>
<para>The <command>lex</command> lexical analyzer produces
programs for pattern matching. This has been replaced
by the nonproprietary <command>flex</command> on Linux
systems.</para>
<para>The <command>yacc</command> utility creates a
parser based on a set of specifications. This has been
replaced by the nonproprietary <command>bison</command>
on Linux systems.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Text Processing Commands -->
<sect1 id="filearchiv">
<title>File and Archiving Commands</title>
<variablelist id="faarchiving">
<title><anchor id="faarchiving1">Archiving</title>
<varlistentry>
<term><anchor id="tarref"><command>tar</command></term>
<indexterm>
<primary>tar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tar</secondary>
</indexterm>
<listitem>
<para>The standard UNIX archiving utility. Originally a
<wordasword>Tape ARchiving</wordasword> program,
it has developed into a general purpose package that
can handle all manner of archiving with all types of
destination devices, ranging from tape drives to regular
files to even <filename>stdout</filename> (see <xref
linkend="ex58">). GNU tar has long since been patched to
accept <link linkend="gzipref">gzip</link> compression
options, such as <command>tar czvf archive-name.tar.gz
*</command>, which recursively archives and compresses all
files (except <link linkend="dotfilesref">dotfiles</link>)
in a directory tree.</para>
<para>Some useful <command>tar</command> options:
<orderedlist>
<listitem><para><option>-c</option> create (a new archive)</para></listitem>
<listitem><para><option>--delete</option> delete (files from the archive)</para></listitem>
<listitem><para><option>-r</option> append (files to the archive)</para></listitem>
<listitem><para><option>-t</option> list (archive contents)</para></listitem>
<listitem><para><option>-u</option> update archive</para></listitem>
<listitem><para><option>-x</option> extract (files from the archive)</para></listitem>
<listitem><para><option>-z</option> <link linkend="gzipref">gzip</link> the archive</para></listitem>
</orderedlist>
</para>
<caution><para>It may be difficult to recover data from a
corrupted <emphasis>gzipped</emphasis> tar
archive. When archiving important files, make multiple
backups.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><command>shar</command></term>
<indexterm>
<primary>shar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>archive</secondary>
</indexterm>
<listitem>
<para>Shell archiving utility. The files in a shell archive
are concatenated without compression, and the resultant
archive is essentially a shell script, complete with
<token>#!/bin/sh</token> header, and containing all
the necessary unarchiving commands. Shar archives
still show up in Internet newsgroups, but otherwise
<command>shar</command> has been pretty well replaced by
<command>tar</command>/<command>gzip</command>. The
<command>unshar</command> command unpacks
<command>shar</command> archives.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ar</command></term>
<indexterm>
<primary>ar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>archive</secondary>
</indexterm>
<listitem>
<para>Creation and manipulation utility for archives, mainly
used for binary object file libraries.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>cpio</command></term>
<indexterm>
<primary>cpio</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cpio</secondary>
</indexterm>
<listitem>
<para>This specialized archiving copy command is rarely seen any more,
having been supplanted by
<command>tar</command>/<command>gzip</command>. It still
has its uses, such as moving a directory tree.</para>
<example id="ex48">
<title>Using <command>cpio</command> to move a directory tree</title>
<programlisting>&ex48;</programlisting>
</example>
<example id="derpm">
<title>Unpacking an <emphasis>rpm</emphasis> archive</title>
<programlisting>&derpm;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="facompression">
<title><anchor id="facompression1">Compression</title>
<varlistentry>
<term><anchor id="gzipref"><command>gzip</command></term>
<indexterm>
<primary>gzip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>gzip</secondary>
</indexterm>
<listitem>
<para>The standard GNU/UNIX compression utility, replacing
the inferior and proprietary
<command>compress</command>. The corresponding decompression
command is <command>gunzip</command>, which is the equivalent of
<command>gzip -d</command>.</para>
<para>The <command>zcat</command> filter decompresses a
<emphasis>gzipped</emphasis> file to
<filename>stdout</filename>, as possible input to a pipe or
redirection. This is, in effect, a <command>cat</command>
command that works on compressed files (including files
processed with the older <command>compress</command>
utility). The <command>zcat</command> command is equivalent to
<command>gzip -dc</command>.</para>
<caution><para>On some commercial UNIX systems, <command>zcat</command>
is a synonym for <command>uncompress -c</command>,
and will not work on <emphasis>gzipped</emphasis>
files.</para></caution>
<para>See also <xref linkend="ex14">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>bzip2</command></term>
<indexterm>
<primary>bzip2</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bzip2</secondary>
</indexterm>
<listitem>
<para>An alternate compression utility, usually more efficient
than <command>gzip</command>, especially on large files. The
corresponding decompression command is
<command>bunzip2</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>compress</command></term>
<term><command>uncompress</command></term>
<indexterm>
<primary>compress</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>compress</secondary>
</indexterm>
<indexterm>
<primary>uncompress</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uncompress</secondary>
</indexterm>
<listitem>
<para>This is an older, proprietary compression
utility found in commercial UNIX distributions. The
more efficient <command>gzip</command> has largely
replaced it. Linux distributions generally include a
<command>compress</command> workalike for compatibility,
although <command>gunzip</command> can unarchive files
treated with <command>compress</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sq</command></term>
<indexterm>
<primary>sq</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sq</secondary>
</indexterm>
<listitem>
<para>Yet another compression utility, a filter that works
only on sorted ASCII word lists. It uses the standard
invocation syntax for a filter, <command>sq < input-file >
output-file</command>. Fast, but not nearly as efficient
as <link linkend="gzipref">gzip</link>. The corresponding
uncompression filter is <command>unsq</command>, invoked
like <command>sq</command>.</para>
<tip><para>The output of <command>sq</command> may be
piped to <command>gzip</command> for further
compression.</para></tip>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="fainformation">
<title><anchor id="fainformation1">File Information</title>
<varlistentry>
<term><anchor id="fileref"><command>file</command></term>
<indexterm>
<primary>file</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>file</secondary>
</indexterm>
<listitem>
<para>A utility for identifying file types. The command
<userinput>file file-name</userinput> will return a
file specification for <filename>file-name</filename>,
such as <computeroutput>ascii text</computeroutput> or
<computeroutput>data</computeroutput>. It references
the <link linkend="magnumref">magic numbers</link>
found in <filename>/usr/share/magic</filename>,
<filename>/etc/magic</filename>, or
<filename>/usr/lib/magic</filename>, depending on the
Linux/UNIX distribution.</para>
<example id="stripc">
<title>stripping comments from C program files</title>
<programlisting>&stripc;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whichref"><command>which</command></term>
<indexterm>
<primary>which</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>which</secondary>
</indexterm>
<listitem>
<para><command>which command-xxx</command> gives the full path
to <quote>command-xxx</quote>. This is useful for finding
out whether a particular command or utility is installed
on the system.</para>
<para><userinput>$bash which rm</userinput>
<screen><computeroutput>/usr/bin/rm</computeroutput></screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>whereis</command></term>
<indexterm>
<primary>whereis</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whereis</secondary>
</indexterm>
<listitem>
<para>Similar to <command>which</command>, above,
<command>whereis command-xxx</command> gives the
full path to <quote>command-xxx</quote>, but also to its
<emphasis>manpage</emphasis>.</para>
<para><userinput>$bash whereis rm</userinput>
<screen><computeroutput>rm: /bin/rm /usr/share/man/man1/rm.1.bz2</computeroutput></screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whatisref"><command>whatis</command></term>
<indexterm>
<primary>whatis</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whatis</secondary>
</indexterm>
<listitem>
<para><command>whatis filexxx</command> looks up
<quote>filexxx</quote> in the
<replaceable>whatis</replaceable> database. This is useful
for identifying system commands and important configuration
files. Consider it a simplified <command>man</command>
command.</para>
<para><userinput>$bash whatis whatis</userinput>
<screen><computeroutput>whatis (1) - search the whatis database for complete words</computeroutput></screen>
</para>
<example id="what">
<title><command>Exploring <filename
class="directory">/usr/X11R6/bin</filename></command></title>
<programlisting>&what;</programlisting>
</example>
<para>See also <xref linkend="fileinfo">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>locate</command></term>
<term><command>slocate</command></term>
<indexterm>
<primary>locate</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>locate</secondary>
</indexterm>
<indexterm>
<primary>slocate</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>slocate</secondary>
</indexterm>
<listitem>
<para>The <command>locate</command> command searches for files using a
database stored for just that purpose. The
<command>slocate</command> command is the secure version of
<command>locate</command> (which may be aliased to
<command>slocate</command>).</para>
<para><userinput>$bash locate hickson</userinput>
<screen><computeroutput>/usr/lib/xephem/catalogs/hickson.edb</computeroutput></screen></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>strings</command></term>
<indexterm>
<primary>strings</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>strings</secondary>
</indexterm>
<listitem>
<para>Use the <command>strings</command> command to find
printable strings in a binary or data file. It will list
sequences of printable characters found in the target
file. This might be handy for a quick 'n dirty examination
of a core dump or for looking at an unknown graphic image
file (<userinput>strings image-file | more</userinput> might
show something like <computeroutput>JFIF</computeroutput>,
which would identify the file as a <emphasis>jpeg</emphasis>
graphic). In a script, you would probably
parse the output of <command>strings</command>
with <link linkend="grepref">grep</link> or <link
linkend="sedref">sed</link>. See <xref linkend="bingrep">
and <xref linkend="findstring">.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="fautils">
<title><anchor id="fautils1">Utilities</title>
<varlistentry>
<term><command>basename</command></term>
<indexterm>
<primary>basename</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>basename</secondary>
</indexterm>
<listitem><para>Strips the path information from a file name, printing
only the file name. The construction <userinput>basename
$0</userinput> lets the script know its name, that is, the name it
was invoked by. This can be used for <quote>usage</quote> messages if,
for example a script is called with missing arguments:
<programlisting>echo "Usage: `basename $0` arg1 arg2 ... argn"</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>dirname</command></term>
<indexterm>
<primary>dirname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dirname</secondary>
</indexterm>
<listitem><para>Strips the <command>basename</command> from
a filename, printing only the path information.</para>
<note>
<para><command>basename</command> and <command>dirname</command>
can operate on any arbitrary string. The argument
does not need to refer to an existing file, or
even be a filename for that matter (see <xref
linkend="daysbetween">).</para>
</note>
<example id="ex35">
<title><command>basename</command> and <command>dirname</command></title>
<programlisting>&ex35;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>split</command></term>
<indexterm>
<primary>split</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>split</secondary>
</indexterm>
<listitem>
<para>Utility for splitting a file into smaller chunks. Usually used
for splitting up large files in order to back them up on floppies or
preparatory to e-mailing or uploading them.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sum</command></term>
<term><command>cksum</command></term>
<term><command>md5sum</command></term>
<indexterm>
<primary>sum</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sum</secondary>
</indexterm>
<indexterm>
<primary>cksum</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cksum</secondary>
</indexterm>
<indexterm>
<primary>md5sum</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>md5sum</secondary>
</indexterm>
<listitem>
<para>These are utilities for generating checksums. A
<emphasis>checksum</emphasis> is a number mathematically
calculated from the contents of a file, for the purpose
of checking its integrity. A script might refer to a list
of checksums for security purposes, such as ensuring that
the contents of key system files have not been altered or
corrupted. The <command>md5sum</command> command is the most
appropriate of these in security applications.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="faencencr">
<title><anchor id="faencencr1">Encoding and Encryption</title>
<varlistentry>
<term><command>uuencode</command></term>
<indexterm>
<primary>uuencode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uuencode</secondary>
</indexterm>
<listitem>
<para>This utility encodes binary files into ASCII characters, making them
suitable for transmission in the body of an e-mail message or in a
newsgroup posting.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>uudecode</command></term>
<indexterm>
<primary>uudecode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uudecode</secondary>
</indexterm>
<listitem>
<para>This reverses the encoding, decoding uuencoded files back into the
original binaries.</para>
<example id="ex52">
<title>uudecoding encoded files</title>
<programlisting>&ex52;</programlisting>
</example>
<tip><para>The <link linkend="foldref">fold -s</link> command
may be useful (possibly in a pipe) to process long uudecoded
text messages downloaded from Usenet newsgroups.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>crypt</command></term>
<indexterm>
<primary>crypt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>crypt</secondary>
</indexterm>
<listitem>
<para>At one time, this was the standard UNIX file encryption
utility.
<footnote><para>This is a symmetric block cipher, used to
encrypt files on a single system or local network, as opposed
to the <quote>public key</quote> cipher class, of which
<command>pgp</command> is a well-known
example.</para></footnote>
Politically motivated government regulations
prohibiting the export of encryption software resulted
in the disappearance of <command>crypt</command>
from much of the UNIX world, and it is still
missing from most Linux distributions. Fortunately,
programmers have come up with a number of decent
alternatives to it, among them the author's very own <ulink
url="ftp://metalab.unc.edu/pub/Linux/utils/file/cruft-0.2.tar.gz">cruft</ulink>
(see <xref linkend="encryptedpw">). </para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="famisc">
<title><anchor id="famisc1">Miscellaneous</title>
<varlistentry>
<term><command>install</command></term>
<indexterm>
<primary>install</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>install</secondary>
</indexterm>
<listitem>
<para>Special purpose file copying command, similar to
<command>cp</command>, but capable of setting permissions
and attributes of the copied files. This command seems
tailormade for installing software packages, and as such it
shows up frequently in <filename>Makefiles</filename> (in
the <replaceable>make install :</replaceable> section). It
could likewise find use in installation scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>more</command></term>
<term><command>less</command></term>
<indexterm>
<primary>more</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>more</secondary>
</indexterm>
<indexterm>
<primary>less</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>less</secondary>
</indexterm>
<listitem>
<para>Pagers that display a text file or stream to
<filename>stdout</filename>, one screenful at a time.
These may be used to filter the output of a script.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End File and Archiving Commands -->
<sect1 id="communications">
<title>Communications Commands</title>
<variablelist id="communinfo">
<title><anchor id="communinfo1">Information and Statistics</title>
<varlistentry>
<term><command>host</command></term>
<indexterm>
<primary>host</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>host</secondary>
</indexterm>
<listitem>
<para>Searches for information about an Internet host by name or
IP address, using DNS.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>vrfy</command></term>
<indexterm>
<primary>vrfy</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>vrfy</secondary>
</indexterm>
<listitem>
<para>Verify an Internet e-mail address.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>nslookup</command></term>
<indexterm>
<primary>nslookup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>name server lookup</secondary>
</indexterm>
<listitem>
<para>Do an Internet <quote>name server lookup</quote> on a host
by IP address. This may be run either interactively or
noninteractively, i.e., from within a script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>dig</command></term>
<indexterm>
<primary>dig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>domain information groper</secondary>
</indexterm>
<listitem>
<para>Similar to <command>nslookup</command>, do an Internet
<quote>name server lookup</quote> on a host. May be run
either interactively or noninteractively, i.e., from within
a script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>traceroute</command></term>
<indexterm>
<primary>traceroute</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>traceroute</secondary>
</indexterm>
<listitem>
<para>Trace the route taken by packets sent to a remote host. This
command works within a LAN, WAN, or over the
Internet. The remote host may be specified by an IP
address. The output of this command may be filtered
by <link linkend="grepref">grep</link> or <link
linkend="sedref">sed</link> in a pipe.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ping</command></term>
<indexterm>
<primary>ping</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ping</secondary>
</indexterm>
<listitem>
<para>Broadcast an <quote>ICMP ECHO_REQUEST</quote> packet to
other machines, either on a local or remote network. This
is a diagnostic tool for testing network connections,
and it should be used with caution.</para>
<para>A successful <command>ping</command> returns an <link
linkend="exitstatusref">exit status</link> of
<errorcode>0</errorcode>. This can be tested for in a
script.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ping localhost</userinput>
<computeroutput>PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes of data.
Warning: time of day goes back, taking countermeasures.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255 time=709 usec
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255 time=286 usec
--- localhost.localdomain ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/mdev = 0.286/0.497/0.709/0.212 ms</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whoisref"><command>whois</command></term>
<indexterm>
<primary>whois</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>domain name server</secondary>
</indexterm>
<listitem>
<para>Perform a DNS (Domain Name System) lookup.
The <option>-h</option> option permits specifying which
<emphasis>whois</emphasis> server to query. See <xref
linkend="ex18">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>finger</command></term>
<indexterm>
<primary>finger</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>finger</secondary>
</indexterm>
<listitem>
<para>Retrieve information about a particular user on
a network. Optionally, this command can display
the user's <filename>~/.plan</filename>,
<filename>~/.project</filename>, and
<filename>~/.forward</filename> files, if present.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>finger bozo</userinput>
<computeroutput>Login: bozo Name: Bozo Bozeman
Directory: /home/bozo Shell: /bin/bash
On since Fri Aug 31 20:13 (MST) on tty1 1 hour 38 minutes idle
On since Fri Aug 31 20:13 (MST) on pts/0 12 seconds idle
On since Fri Aug 31 20:13 (MST) on pts/1
On since Fri Aug 31 20:31 (MST) on pts/2 1 hour 16 minutes idle
No mail.
No Plan.</computeroutput>
</screen>
</para>
<para>Out of security considerations, many networks disable
<command>finger</command> and its associated daemon.
<footnote>
<para><anchor id="daemonref"></para>
<para>A <emphasis>daemon</emphasis> is a background
process not attached to a terminal session. Daemons
perform designated services either at specified times
or explicitly triggered by certain events.</para>
<para>The word <quote>daemon</quote> means ghost in
Greek, and there is certainly something mysterious,
almost supernatural, about the way UNIX daemons
silently wander about behind the scenes, carrying
out their appointed tasks.</para>
</footnote>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commremote">
<title><anchor id="commremote1">Remote Host Access</title>
<varlistentry>
<term><command>sx</command></term>
<term><command>rx</command></term>
<indexterm>
<primary>sx</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sx</secondary>
</indexterm>
<indexterm>
<primary>rx</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rx</secondary>
</indexterm>
<listitem>
<para>The <command>sx</command> and <command>rx</command>
command set serves to transfer files to and from a remote
host using the <emphasis>xmodem</emphasis> protocol. These
are generally part of a communications package, such as
<command>minicom</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sz</command></term>
<term><command>rz</command></term>
<indexterm>
<primary>sz</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sz</secondary>
</indexterm>
<indexterm>
<primary>rz</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rz</secondary>
</indexterm>
<listitem>
<para>The <command>sz</command> and <command>rz</command>
command set serves to transfer files to and from a remote
host using the <emphasis>zmodem</emphasis> protocol.
<emphasis>Zmodem</emphasis> has certain advantages over
<emphasis>xmodem</emphasis>, such as greater transmission
rate and resumption of interrupted file transfers.
Like <command>sx</command> and <command>rx</command>,
these are generally part of a communications package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ftpref"><command>ftp</command></term>
<indexterm>
<primary>ftp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>file transfer</secondary>
</indexterm>
<listitem>
<para>Utility and protocol for uploading / downloading
files to / from a remote host. An ftp session can be automated
in a script (see <xref linkend="ex72">, <xref
linkend="encryptedpw">, and <xref linkend="ftpget">).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>uucp</command></term>
<indexterm>
<primary>uucp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uucp</secondary>
</indexterm>
<listitem>
<para><emphasis>UNIX to UNIX copy</emphasis>. This is a
communications package for transferring files between UNIX
servers. A shell script is an effective way to handle a
<command>uucp</command> command sequence.</para>
<para>Since the advent of the Internet and e-mail,
<command>uucp</command> seems to have faded into obscurity,
but it still exists and remains perfectly workable in
situations where an Internet connection is not available
or appropriate.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="telnetref"><command>telnet</command></term>
<indexterm>
<primary>telnet</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>telnet</secondary>
</indexterm>
<listitem>
<para>Utility and protocol for connecting to a remote host.</para>
<caution><para>The telnet protocol contains security holes and
should therefore probably be avoided.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rlogin</command></term>
<indexterm>
<primary>rlogin</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote login</secondary>
</indexterm>
<listitem>
<para><replaceable>Remote login</replaceable>, initates a
session on a remote host. This command has security issues,
so use <link linkend="sshref">ssh</link> instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rsh</command></term>
<indexterm>
<primary>rsh</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote shell</secondary>
</indexterm>
<listitem>
<para><replaceable>Remote shell</replaceable>, executes
command(s) on a remote host. This has security issues,
so use <command>ssh</command> instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rcp</command></term>
<indexterm>
<primary>rcp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote copy</secondary>
</indexterm>
<listitem>
<para><replaceable>Remote copy</replaceable>, copies files
between two different networked machines. Using
<command>rcp</command> and similar utilities with
security implications in a shell script may not be
advisable. Consider, instead, using <command>ssh</command>
or an <command>expect</command> script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sshref"><command>ssh</command></term>
<indexterm>
<primary>ssh</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>secure shell</secondary>
</indexterm>
<listitem>
<para><replaceable>Secure shell</replaceable>, logs onto
a remote host and executes commands there. This
secure replacement for <command>telnet</command>,
<command>rlogin</command>, <command>rcp</command>, and
<command>rsh</command> uses identity authentication
and encryption. See its <emphasis>manpage</emphasis>
for details.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commlocal">
<title><anchor id="commlocal1">Local Network</title>
<varlistentry>
<term><anchor id="writeref"><command>write</command></term>
<indexterm>
<primary>write</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>write</secondary>
</indexterm>
<listitem>
<para>This is a utility for terminal-to-terminal communication.
It allows sending lines from your terminal (console
or xterm) to that of another user. The <link
linkend="mesgref">mesg</link> command may, of course,
be used to disable write access to a terminal</para>
<para>Since <command>write</command> is interactive, it
would not normally find use in a script.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commmail">
<title><anchor id="commmail1">Mail</title>
<varlistentry>
<term><command>vacation</command></term>
<indexterm>
<primary>vacation</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mail</secondary>
</indexterm>
<listitem>
<para>This utility automatically replies to e-mails that
the intended recipient is on vacation and temporarily
unavailable. This runs on a network, in conjunction with
<command>sendmail</command>, and is not applicable to a
dial-up POPmail account.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Communications Commands -->
<sect1 id="terminalccmds">
<title>Terminal Control Commands</title>
<variablelist id="termcommandlisting">
<title><anchor id="termcommandlisting1">Command Listing</title>
<varlistentry>
<term><command>tput</command></term>
<indexterm>
<primary>tput</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>terminal</secondary>
</indexterm>
<listitem>
<para>Initialize terminal and/or fetch information about it from
<filename>terminfo</filename> data. Various options permit
certain terminal operations. <command>tput clear</command>
is the equivalent of <command>clear</command>,
below. <command>tput reset</command> is the equivalent of
<command>reset</command>, below.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>tput longname</userinput>
<computeroutput>xterm terminal emulator (XFree86 4.0 Window System)</computeroutput>
</screen>
</para>
<para>Note that <link linkend="sttyref">stty</link> offers
a more powerful command set for controlling a terminal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>reset</command></term>
<indexterm>
<primary>reset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>reset</secondary>
</indexterm>
<listitem>
<para>Reset terminal parameters and clear text screen. As with
<command>clear</command>, the cursor and prompt reappear in the
upper lefthand corner of the terminal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="clearref"><command>clear</command></term>
<indexterm>
<primary>clear</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>clear</secondary>
</indexterm>
<listitem>
<para>The <command>clear</command> command simply clears
the text screen at the console or in an xterm. The
prompt and cursor reappear at the upper lefthand corner
of the screen or xterm window. This command may be used
either at the command line or in a script. See <xref
linkend="ex30">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>script</command></term>
<indexterm>
<primary>script</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>script</secondary>
</indexterm>
<listitem>
<para>This utility records (saves to a file) all the user keystrokes at
the command line in a console or an xterm window. This, in effect,
create a record of a session.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Terminal Control Commands -->
<sect1 id="mathc">
<title>Math Commands</title>
<variablelist id="mathcommandlisting">
<title><anchor id="mathcommandlisting1">Command Listing</title>
<varlistentry>
<term><command>factor</command></term>
<indexterm>
<primary>factor</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>factor</secondary>
</indexterm>
<listitem><para>Factor an integer into prime factors.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>factor 27417</userinput>
<computeroutput>27417: 3 13 19 37</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bcref"><command>bc</command></term>
<term><command>dc</command></term>
<indexterm>
<primary>bc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bc</secondary>
</indexterm>
<indexterm>
<primary>dc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dc</secondary>
</indexterm>
<listitem>
<para>These are flexible, arbitrary precision calculation
utilities.</para>
<para><command>bc</command> has a syntax vaguely resembling C.</para>
<para><command>dc</command> uses RPN (<quote>Reverse Polish
Notation</quote>).</para>
<para>Of the two, <command>bc</command> seems more useful in
scripting. It is a fairly well-behaved UNIX utility, and may
therefore be used in a pipe.</para>
<para>Bash can't handle floating point calculations, and
it lacks operators for certain important mathematical
functions. Fortunately, <command>bc</command> comes to
the rescue.</para>
<para>Here is a simple template for using
<command>bc</command> to calculate a script variable.</para>
<para>
<screen>
<userinput>variable=$(echo "OPTIONS; OPERATIONS" | bc)</userinput>
</screen>
</para>
<example id="monthlypmt">
<title>Monthly Payment on a Mortgage</title>
<programlisting>&monthlypmt;</programlisting>
</example>
<example id="base">
<title>Base Conversion</title>
<programlisting>&base;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Math Commands -->
<sect1 id="extmisc">
<title>Miscellaneous Commands</title>
<variablelist id="misccommandlisting">
<title><anchor id="misccommandlisting1">Command Listing</title>
<varlistentry>
<term><command>jot</command></term>
<term><command>seq</command></term>
<indexterm>
<primary>jot</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>jot</secondary>
</indexterm>
<indexterm>
<primary>seq</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>seq</secondary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>arguments</secondary>
</indexterm>
<listitem>
<para>These utilities emit a sequence of integers, with a
user selected increment. This can be used to advantage in
a <link linkend="forloopref">for loop</link>.</para>
<example id="ex53">
<title>Using <command>seq</command> to generate loop arguments</title>
<programlisting>&ex53;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="runpartsref"><command>run-parts</command></term>
<indexterm>
<primary>run-parts</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>run-parts</secondary>
</indexterm>
<listitem>
<para>The <command>run-parts</command> command
<footnote><para>This is actually a script adapted from
the Debian Linux distribution.</para></footnote>
executes all the scripts in a target directory,
sequentially in ASCII-sorted filename order. The
<link linkend="cronref">cron</link> command invokes
<command>run-parts</command> to run the scripts in
the <filename class="directory">/etc/cron.*</filename>
directories.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>yes</command></term>
<indexterm>
<primary>yes</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>yes</secondary>
</indexterm>
<listitem>
<para>In its default behavior the <command>yes</command>
command feeds a continuous string of the character
<computeroutput>y</computeroutput> followed
by a line feed to <filename>stdout</filename>. A
<keycombo><keycap>control</keycap><keycap>c</keycap></keycombo>
terminates the run. A different output string
may be specified, as in <userinput>yes different
string</userinput>, which would continually output
<computeroutput>different string</computeroutput> to
<filename>stdout</filename>. One might well ask the purpose
of this. From the command line or in a script, the output
of <command>yes</command> can be redirected or piped into a
program expecting user input. In effect, this becomes a sort
of poor man's version of <command>expect</command>.</para>
<para><userinput>yes | fsck /dev/hda1</userinput> runs
<command>fsck</command> non-interactively (careful!).</para>
<para><userinput>yes | rm -r dirname</userinput> has same effect as
<userinput>rm -rf dirname</userinput> (careful!).</para>
<warning><para>Be very cautious when piping <command>yes</command>
to a potentially dangerous system command, such as
<link linkend="fsckref">fsck</link> or
<link linkend="fdiskref">fdisk</link>.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><command>printenv</command></term>
<indexterm>
<primary>printenv</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>environment</secondary>
</indexterm>
<listitem>
<para>Show all the environmental variables set for a particular
user.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>printenv | grep HOME</userinput>
<computeroutput>HOME=/home/bozo</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lp</command></term>
<indexterm>
<primary>lp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lpr</secondary>
</indexterm>
<listitem>
<para>The <command>lp</command> and <command>lpr</command>
commands send file(s) to the print queue, to be printed as
hard copy.
<footnote><para>The <emphasis>print queue</emphasis> is
the group of jobs <quote>waiting in line</quote> to be
printed.</para></footnote>
These commands trace the origin of their names to the
line printers of another era.</para>
<para><prompt>bash$ </prompt><userinput>lp file1.txt</userinput>
or <prompt>bash </prompt><userinput>lp
&lt;file1.txt</userinput></para>
<para>It is often useful to pipe the formatted output from
<command>pr</command> to <command>lp</command>.</para>
<para><prompt>bash$ </prompt><userinput>pr -options file1.txt | lp</userinput>
</para>
<para>Formatting packages, such as <command>groff</command> and
<emphasis>Ghostscript</emphasis> may send their output
directly to <command>lp</command>.</para>
<para><prompt>bash$ </prompt><userinput>groff -Tascii file.tr | lp</userinput>
</para>
<para><prompt>bash$ </prompt><userinput>gs -options | lp file.ps</userinput>
</para>
<para>Related commands are <command>lpq</command>, for viewing
the print queue, and <command>lprm</command>, for removing
jobs from the print queue.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tee</command></term>
<indexterm>
<primary>tee</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tee</secondary>
</indexterm>
<listitem>
<para>[UNIX borrows an idea here from the plumbing trade.]</para>
<para>This is a redirection operator, but with a difference. Like the
plumber's <emphasis>tee</emphasis>, it permits <quote>siponing
off</quote> the output of a command
or commands within a pipe, but without affecting the result. This is
useful for printing an ongoing process to a file or paper, perhaps to
keep track of it for debugging purposes.</para>
<screen>
tee
|------> to file
|
===============|===============
command--->----|-operator-->---> result of command(s)
===============================
</screen>
<para><programlisting>cat listfile* | sort | tee check.file | uniq > result.file</programlisting>
(The file <filename>check.file</filename> contains
the concatenated sorted <quote>listfiles</quote>,
before the duplicate lines are removed by <link
linkend="uniqref">uniq</link>.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mkfifo</command></term>
<indexterm>
<primary>mkfifo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkfifo</secondary>
</indexterm>
<listitem>
<para><anchor id="namedpiperef">This obscure command
creates a <emphasis>named pipe</emphasis>, a temporary
<emphasis>first-in-first-out buffer</emphasis> for
transferring data between processes.
<footnote><para>For an excellent overview of this
topic, see Andy Vaught's article, <ulink
url="http://www2.linuxjournal.com/lj-issues/issue41/2156.html">Introduction
to Named Pipes</ulink>, in the September, 1997 issue
of <ulink url="http://www.linuxjournal.com">Linux
Journal</ulink>.</para></footnote>
Typically, one process writes to the FIFO, and the other
reads from it. See <xref linkend="fifo">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>pathchk</command></term>
<indexterm>
<primary>pathchk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pathchk</secondary>
</indexterm>
<listitem>
<para>This command checks the validity of a filename. If the
filename exceeds the maximum allowable length (255
characters) or one or more of the directories in
its path is not searchable, then an error message
results. Unfortunately, <command>pathchk</command> does
not return a recognizable error code, and it is therefore
pretty much useless in a script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ddref"><command>dd</command></term>
<indexterm>
<primary>dd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dd</secondary>
</indexterm>
<listitem>
<para>This is the somewhat obscure and much feared <quote>data
duplicator</quote> command. Originally a utility
for exchanging data on magnetic tapes between UNIX
minicomputers and IBM mainframes, this command still
has its uses. The <command>dd</command> command simply
copies a file (or <filename>stdin/stdout</filename>), but
with conversions. Possible conversions are ASCII/EBCDIC,
<footnote><para>EBCDCIC (pronounced
<quote>ebb-sid-ic</quote>) is an acronym for Extended
Binary Coded Decimal Interchange Code. This is an IBM
data format no longer in much use. A bizarre
application of the <option>conv=ebcdic</option> option
of <command>dd</command> is as a quick 'n easy, but
not very secure text file encoder.
<programlisting>cat $file | dd conv=swab,ebcdic > $file_encrypted
# Encode (looks like gibberish).
# Might as well switch bytes (swab), too, for a little extra obscurity.
cat $file_encrypted | dd conv=swab,ascii > $file_plaintext
# Decode.</programlisting>
</para></footnote>
upper/lower case, swapping of byte pairs between input
and output, and skipping and/or truncating the head or
tail of the input file. A <userinput>dd --help</userinput>
lists the conversion and other options that this powerful
utility takes.</para>
<para><programlisting># Exercising 'dd'.
n=3
p=5
input_file=project.txt
output_file=log.txt
dd if=$input_file of=$output_file bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null
# Extracts characters n to p from file $input_file.
echo -n "hello world" | dd cbs=1 conv=unblock 2> /dev/null
# Echoes "hello world" vertically.
# Thanks, S.C.</programlisting></para>
<para>To demonstrate just how versatile <command>dd</command> is,
let's use it to capture keystrokes.</para>
<example id="ddkeypress">
<title>Capturing Keystrokes</title>
<programlisting>&ddkeypress;</programlisting>
</example>
<para>The <command>dd</command> command can do random access on a
data stream.
<programlisting>echo -n . | dd bs=1 seek=4 of=file conv=notrunc
# The "conv=notrunc" option means that the output file will not be truncated.
# Thanks, S.C.</programlisting>
</para>
<para>The <command>dd</command> command can copy raw data
and disk images to and from devices, such as floppies and
tape drives (<xref linkend="copycd">). A common use is
creating boot floppies.
<programlisting>dd if=kernel-image of=/dev/fd0H1440</programlisting>
One important use for <command>dd</command> is initializing
temporary swap files (<xref linkend="ex73">). It can even
do a low-level copy of an entire hard drive partition,
although this is not necessarily recommended.</para>
<para>People (with presumably nothing better to do with
their time) are constantly thinking of interesting
applications of <command>dd</command>.</para>
<example id="blotout">
<title>Securely deleting a file</title>
<programlisting>&blotout;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="odref"><command>od</command></term>
<indexterm>
<primary>od</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>od</secondary>
</indexterm>
<listitem>
<para>The <command>od</command>, or <emphasis>octal
dump</emphasis> command converts input (or files) to octal
(base-8) or other bases. This is useful for viewing or
processing binary data files or otherwise unreadable system
device files, such as <filename>/dev/urandom</filename>,
and as a filter for binary data. See <xref
linkend="seedingrandom"> and <xref linkend="rnd">.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Miscellaneous Commands -->
</chapter> <!-- External Filters, Programs and Commands -->
<chapter id="system">
<title>System and Administrative Commands</title>
<para>The startup and shutdown scripts in
<filename class="directory">/etc/rc.d</filename> illustrate the uses
(and usefulness) of many of these comands. These are usually
invoked by root and used for system maintenance or emergency
filesystem repairs. Use with caution, as some of these commands
may damage your system if misused.</para>
<variablelist id="usersgroups">
<title><anchor id="usersgroups1">Users and Groups</title>
<varlistentry>
<term><command>chown</command></term>
<term><command>chgrp</command></term>
<indexterm>
<primary>chown</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chown</secondary>
</indexterm>
<indexterm>
<primary>chgrp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chgrp</secondary>
</indexterm>
<listitem>
<para>The <command>chown</command> command changes the
ownership of a file or files. This command is a useful
method that <replaceable>root</replaceable> can use to
shift file ownership from one user to another. An ordinary
user may not change the ownership of files, not even her
own files.
<footnote><para>This is the case on a Linux machine or a UNIX
system with disk quotas.</para></footnote>
</para>
<para>
<screen><prompt>root# </prompt><userinput>chown bozo *.txt</userinput>
<computeroutput></computeroutput>
</screen>
</para>
<para>The <command>chgrp</command> command changes the
<replaceable>group</replaceable> ownership of a file or
files. You must be owner of the file(s) as well as a member
of the destination group (or <replaceable>root</replaceable>)
to use this operation.
<programlisting>chgrp --recursive dunderheads *.data
# The "dunderheads" group will now own all the "*.data" files
# in the $PWD directory tree (that's what "recursive" means).
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>useradd</command></term>
<term><command>userdel</command></term>
<indexterm>
<primary>useradd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>useradd</secondary>
</indexterm>
<indexterm>
<primary>userdel</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>userdel</secondary>
</indexterm>
<listitem>
<para>The <command>useradd</command> administrative command
adds a user account to the system and creates a home
directory for that particular user, if so specified. The
corresponding <command>userdel</command> command removes
a user account from the system
<footnote><para>The <command>userdel</command> command
will fail if the particular user being deleted is
still logged on.</para></footnote>
and deletes associated files.</para>
<note><para>The <command>adduser</command> command is a synonym
for <command>useradd</command> and is usually a symbolic link to
it.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="idref"><command>id</command></term>
<indexterm>
<primary>id</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>id</secondary>
</indexterm>
<listitem>
<para>The <command>id</command> command lists the real and
effective user IDs and the group IDs of
the current user. This is the counterpart
to the <link linkend="uidref">$UID</link>,
<link linkend="euidref">$EUID</link>, and <link
linkend="groupsref">$GROUPS</link> internal Bash
variables.</para>
<screen><prompt>bash$ </prompt><userinput>id</userinput>
<computeroutput>uid=501(bozo) gid=501(bozo) groups=501(bozo),22(cdrom),80(cdwriter),81(audio)</computeroutput>
<prompt>bash$ </prompt><userinput>echo $UID</userinput>
<computeroutput>501</computeroutput></screen>
<para>Also see <xref linkend="amiroot">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>who</command></term>
<indexterm>
<primary>who</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whoami</secondary>
</indexterm>
<listitem>
<para>Show all users logged on to the system.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>who</userinput>
<computeroutput>bozo tty1 Apr 27 17:45
bozo pts/0 Apr 27 17:46
bozo pts/1 Apr 27 17:47
bozo pts/2 Apr 27 17:49
</computeroutput>
</screen>
</para>
<para>The <option>-m</option> gives detailed information about
only the current user. Passing any two arguments to
<command>who</command> is the equivalent of <command>who
-m</command>, as in <command>who am i</command> or <command>who
The Man</command>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>who -m</userinput>
<computeroutput>localhost.localdomain!bozo pts/2 Apr 27 17:49</computeroutput>
</screen>
</para>
<para><anchor id="whoamiref"><command>whoami</command> is similar to <command>who
-m</command>, but only lists the user name.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>whoami</userinput>
<computeroutput>bozo</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>w</command></term>
<indexterm>
<primary>w</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>w</secondary>
</indexterm>
<listitem>
<para>Show all logged on users and the processes belonging to them. This is
an extended version of <command>who</command>. The output of <command>w</command>
may be piped to <command>grep</command> to find a specific user and/or process.</para>
<screen><prompt>bash$ </prompt><userinput>w | grep startx</userinput>
<computeroutput>bozo tty1 - 4:22pm 6:41 4.47s 0.45s startx</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>logname</command></term>
<indexterm>
<primary>logname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logname</secondary>
</indexterm>
<listitem>
<para>Show current user's login name (as found in
<filename>/var/run/utmp</filename>). This is a
near-equivalent to <link linkend="whoamiref">whoami</link>,
above.</para>
<screen><prompt>bash$ </prompt><userinput>logname</userinput>
<computeroutput>bozo</computeroutput>
<prompt>bash$ </prompt><userinput>whoami</userinput>
<computeroutput>bozo</computeroutput></screen>
<para>However...</para>
<screen><prompt>bash$ </prompt><userinput>su</userinput>
<computeroutput>Password: ......</computeroutput>
<prompt>bash# </prompt><userinput>whoami</userinput>
<computeroutput>root</computeroutput>
<prompt>bash# </prompt><userinput>logname</userinput>
<computeroutput>bozo</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="suref"><command>su</command></term>
<indexterm>
<primary>su</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>su</secondary>
</indexterm>
<listitem>
<para>Runs a program or script as a
<emphasis>s</emphasis>ubstitute <emphasis>u</emphasis>ser.
<command>su rjones</command> starts a shell as user
<emphasis>rjones</emphasis>. A naked <command>su</command>
defaults to <emphasis>root</emphasis>. See <xref
linkend="fifo">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>users</command></term>
<indexterm>
<primary>users</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>users</secondary>
</indexterm>
<listitem>
<para>Show all logged on users. This is the approximate
equivalent of <command>who -q</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ac</command></term>
<indexterm>
<primary>ac</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>accounting</secondary>
</indexterm>
<listitem>
<para>Show users' logged in time, as read from
<filename>/var/log/wtmp</filename>. This is one of the GNU
accounting utilities.</para>
<screen><prompt>bash$ </prompt><userinput>ac</userinput>
<computeroutput> total 68.08</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>last</command></term>
<indexterm>
<primary>last</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logged in</secondary>
</indexterm>
<listitem>
<para>List <emphasis>last</emphasis> logged in users, as read from
<filename>/var/log/wtmp</filename>. This command can also
show remote logins.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>groups</command></term>
<indexterm>
<primary>groups</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>groups</secondary>
</indexterm>
<listitem>
<para>Lists the current user and the groups she belongs to.
This corresponds to the <link
linkend="groupsref">$GROUPS</link> internal variable,
but gives the group names, rather than the numbers.</para>
<screen><prompt>bash$ </prompt><userinput>groups</userinput>
<computeroutput>bozita cdrom cdwriter audio xgrp</computeroutput>
<prompt>bash$ </prompt><userinput>echo $GROUPS</userinput>
<computeroutput>501</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>newgrp</command></term>
<indexterm>
<primary>newgrp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>group</secondary>
</indexterm>
<listitem>
<para>Change user's group ID without logging out. This permits
access to the new group's files. Since users may be
members of multiple groups simultaneously, this command
finds little use.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="terminalssys">
<title><anchor id="terminalssys1">Terminals</title>
<varlistentry>
<term><command>tty</command></term>
<indexterm>
<primary>tty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tty</secondary>
</indexterm>
<listitem>
<para>Echoes the name of the current user's terminal.
Note that each separate xterm window counts as a different
terminal.</para>
<screen><prompt>bash$ </prompt><userinput>tty</userinput>
<computeroutput>/dev/pts/1</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sttyref"><command>stty</command></term>
<indexterm>
<primary>stty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>stty</secondary>
</indexterm>
<listitem>
<para>Shows and/or changes terminal settings. This complex
command, used in a script, can control terminal behavior
and the way output displays. See the info page, and study
it carefully.</para>
<example id="erase">
<title>setting an erase character</title>
<programlisting>&erase;</programlisting>
</example>
<example id="secretpw">
<title><command>secret password</command>:
Turning off terminal echoing </title>
<programlisting>&secretpw;</programlisting>
</example>
<para>A creative use of <command>stty</command> is detecting a
user keypress (without hitting
<keycap>ENTER</keycap>).</para>
<example id="keypress">
<title>Keypress detection</title>
<programlisting>&keypress;</programlisting>
</example>
<para>Also see <xref linkend="timeout">.</para>
<sidebar><title>terminals and modes</title>
<para>Normally, a terminal works in the
<emphasis>canonical</emphasis> mode. When a user hits a
key, the resulting character does not immediately go to
the program actually running in this terminal. A buffer
local to the terminal stores keystrokes. When the user
hits the <keycap>ENTER</keycap> key, this sends all the
stored keystrokes to the program running. There is even
a basic line editor inside the terminal.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>stty -a</userinput>
<computeroutput>speed 9600 baud; rows 36; columns 96; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = &lt;undef&gt;; eol2 = &lt;undef&gt;;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O;
...
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt</computeroutput>
</screen>
</para>
<para>Using canonical mode, it is possible to redefine the
special keys for the local terminal line editor.
<screen>
<prompt>bash$ </prompt><userinput>cat > filexxx</userinput>
<userinput>wha&lt;ctl-W&gt;I&lt;ctl-H&gt;foo bar&lt;ctl-U&gt;hello world&lt;ENTER&gt;</userinput>
<userinput>&lt;ctl-D&gt;</userinput>
<prompt>bash$ </prompt><userinput>cat filexxx</userinput>
<computeroutput>hello world</computeroutput>
<prompt>bash$ </prompt><userinput>bash$ wc -c < file</userinput>
<computeroutput>13</computeroutput>
</screen>
The process controlling the terminal receives only 13
characters (12 alphabetic ones, plus a newline), although
the user hit 26 keys.
</para>
<para>In non-canonical (<quote>raw</quote>) mode, every
key hit (including special editing keys such as
<keycap>ctl-H</keycap>) sends a character immediately to
the controlling process.</para>
<para>The Bash prompt disables both <option>icanon</option>
and <option>echo</option>, since it replaces the basic
terminal line editor with its own more elaborate one. For
example, when you hit <keycap>ctl-A</keycap> at the Bash
prompt, there's no <keycap>^A</keycap> echoed by the
terminal, but Bash gets a <keycap>\1</keycap> character,
interprets it, and moves the cursor to the begining of
the line.</para>
<para><emphasis>Stephane Chazelas</emphasis></para>
</sidebar>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tset</command></term>
<indexterm>
<primary>tset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tset</secondary>
</indexterm>
<listitem>
<para>Show or initialize terminal settings.
This is a less capable version of
<command>stty</command>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>tset -r</userinput>
<computeroutput>Terminal type is xterm-xfree86.
Kill is control-U (^U).
Interrupt is control-C (^C).</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>getty</command></term>
<term><command>agetty</command></term>
<indexterm>
<primary>getty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>getty</secondary>
</indexterm>
<indexterm>
<primary>agetty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>agetty</secondary>
</indexterm>
<listitem>
<para>The initialization process for a terminal uses
<command>getty</command> or <command>agetty</command>
to set it up for login by a user. These commands are not
used within user shell scripts. Their scripting counterpart
is <command>stty</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mesgref"><command>mesg</command></term>
<indexterm>
<primary>mesg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mesg</secondary>
</indexterm>
<listitem>
<para>Enables or disables write access to the current user's
terminal. Disabling access would prevent another user
on the network to <link linkend="writeref">write</link>
to the terminal.</para>
<tip><para>It can be very annoying to have a message
about ordering pizza suddenly appear in the middle of
the text file you are editing. On a multi-user network,
you might therefore wish to disable write access to your
terminal when you need to avoid interruptions.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>wall</command></term>
<indexterm>
<primary>wall</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wall</secondary>
</indexterm>
<listitem>
<para>This is an acronym for <quote><link
linkend="writeref">write</link> all</quote>, i.e., sending
a message to all users at every terminal logged into the
network. It is primarily a system administrator's tool,
useful, for example, when warning everyone that the
system will shortly go down due to a problem (see <xref
linkend="ex70">).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>wall System going down for maintenance in 5 minutes!</userinput>
<computeroutput>Broadcast message from bozo (pts/1) Sun Jul 8 13:53:27 2001...
System going down for maintenance in 5 minutes!</computeroutput>
</screen>
</para>
<note><para>If write access to a particular terminal has been
disabled with <command>mesg</command>, then
<command>wall</command> cannot send a message to
it.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>dmesg</command></term>
<indexterm>
<primary>dmesg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dmesg</secondary>
</indexterm>
<listitem>
<para>Lists all system bootup messages to
<filename>stdout</filename>. Handy for debugging and
ascertaining which device drivers were installed
and which system interrupts in use. The output
of <command>dmesg</command> may, of course, be
parsed with <link linkend="grepref">grep</link>,
<link linkend="sedref">sed</link>, or <link
linkend="awkref">awk</link> from within a script.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="statisticssys">
<title><anchor id="statisticssys1">Information and Statistics</title>
<varlistentry>
<term><command>uname</command></term>
<indexterm>
<primary>uname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uname</secondary>
</indexterm>
<listitem>
<para>Output system specifications (OS, kernel version,
etc.) to <filename>stdout</filename>. Invoked with the
<option>-a</option> option, gives verbose system info
(see <xref linkend="ex41">).</para>
<screen><prompt>bash$ </prompt><userinput>uname -a</userinput>
<computeroutput>Linux localhost.localdomain 2.2.15-2.5.0 #1 Sat Feb 5 00:13:43 EST 2000 i686 unknown</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>arch</command></term>
<indexterm>
<primary>arch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>arch</secondary>
</indexterm>
<listitem>
<para>Show system architecture.
Equivalent to <command>uname -m</command>. See <xref
linkend="casecmd">.</para>
<screen><prompt>bash$ </prompt><userinput>arch</userinput>
<computeroutput>i686</computeroutput>
<prompt>bash$ </prompt><userinput>uname -m</userinput>
<computeroutput>i686</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lastcomm</command></term>
<indexterm>
<primary>lastcomm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>last</secondary>
</indexterm>
<listitem>
<para> Gives information about previous commands, as stored
in the <filename>/var/account/pacct</filename> file. Command
name and user name can be specified by options. This is
one of the GNU accounting utilities.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lsof</command></term>
<indexterm>
<primary>lsof</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lsof</secondary>
</indexterm>
<listitem>
<para>List open files. This command outputs a detailed
table of all currently open files and gives information
about their owner, size, the processes associated with
them, and more. Of course, <command>lsof</command> may
be piped to <link linkend="grepref">grep</link> and/or
<link linkend="awkref">awk</link> to parse and analyze
its results.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lsof</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
init 1 root mem REG 3,5 30748 30303 /sbin/init
init 1 root mem REG 3,5 73120 8069 /lib/ld-2.1.3.so
init 1 root mem REG 3,5 931668 8075 /lib/libc-2.1.3.so
cardmgr 213 root mem REG 3,5 36956 30357 /sbin/cardmgr
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>strace</command></term>
<indexterm>
<primary>strace</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>strace</secondary>
</indexterm>
<listitem>
<para>Diagnostic and debugging tool for tracing system
calls and signals. The simplest way of invoking it is
<command>strace COMMAND</command>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>strace df</userinput>
<computeroutput>execve("/bin/df", ["df"], [/* 45 vars */]) = 0
uname({sys="Linux", node="bozo.localdomain", ...}) = 0
brk(0) = 0x804f5e4
...</computeroutput>
</screen>
</para>
<para>This is the Linux equivalent of
<command>truss</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="freeref"><command>free</command></term>
<indexterm>
<primary>free</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>free</secondary>
</indexterm>
<listitem>
<para>Shows memory and cache usage in tabular form. The
output of this command lends itself to parsing, using
<link linkend="grepref">grep</link>, <link
linkend="awkref">awk</link> or <command>Perl</command>. The
<command>procinfo</command> command shows all the
information that <command>free</command> does, and much
more.</para>
<screen><prompt>bash$ </prompt><command>free</command>
<computeroutput> total used free shared buffers cached
Mem: 30504 28624 1880 15820 1608 16376
-/+ buffers/cache: 10640 19864
Swap: 68540 3128 65412</computeroutput></screen>
<para>To show unused RAM memory:</para>
<screen><prompt>bash$ </prompt><command>free | grep Mem | awk '{ print $4 }'</command>
<computeroutput>1880</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="procinforef"><command>procinfo</command></term>
<indexterm>
<primary>procinfo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>procinfo</secondary>
</indexterm>
<listitem>
<para>Extract and list information and statistics from the
<link linkend="devprocref"><filename
class="directory">/proc</filename>
pseudo-filesystem</link>. This gives a very extensive and
detailed listing.</para>
<screen><prompt>bash$ </prompt><userinput>procinfo | grep Bootup</userinput>
<computeroutput>Bootup: Wed Mar 21 15:15:50 2001 Load average: 0.04 0.21 0.34 3/47 6829</computeroutput>
</screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>du</command></term>
<indexterm>
<primary>du</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>du</secondary>
</indexterm>
<listitem>
<para>Show (disk) file usage, recursively. Defaults to current
working directory, unless otherwise specified.</para>
<screen><prompt>bash$ </prompt><command>du -ach</command>
<computeroutput>1.0k ./wi.sh
1.0k ./tst.sh
1.0k ./random.file
6.0k .
6.0k total</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>df</command></term>
<indexterm>
<primary>df</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>df</secondary>
</indexterm>
<listitem>
<para>Shows filesystem usage in tabular form.</para>
<screen><prompt>bash$ </prompt><command>df</command>
<computeroutput>Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda5 273262 92607 166547 36% /
/dev/hda8 222525 123951 87085 59% /home
/dev/hda7 1408796 1075744 261488 80% /usr</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>stat</command></term>
<indexterm>
<primary>stat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>stat</secondary>
</indexterm>
<listitem>
<para>Gives detailed and verbose <emphasis>stat</emphasis>istics
on a given file (even a directory or device file) or set
of files.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>stat test.cru</userinput>
<computeroutput> File: "test.cru"
Size: 49970 Allocated Blocks: 100 Filetype: Regular File
Mode: (0664/-rw-rw-r--) Uid: ( 501/ bozo) Gid: ( 501/ bozo)
Device: 3,8 Inode: 18185 Links: 1
Access: Sat Jun 2 16:40:24 2001
Modify: Sat Jun 2 16:40:24 2001
Change: Sat Jun 2 16:40:24 2001</computeroutput>
</screen>
</para>
<para>If the target file does not exist, <command>stat</command>
returns an error message.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>stat nonexistent-file</userinput>
<computeroutput>nonexistent-file: No such file or directory</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="vmstatref"><command>vmstat</command></term>
<indexterm>
<primary>vmstat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>virtual memory</secondary>
</indexterm>
<listitem>
<para>Display virtual memory statistics.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>vmstat</userinput>
<computeroutput> procs memory swap io system cpu
r b w swpd free buff cache si so bi bo in cs us sy id
0 0 0 0 11040 2636 38952 0 0 33 7 271 88 8 3 89</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>netstat</command></term>
<indexterm>
<primary>netstat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>netstat</secondary>
</indexterm>
<listitem>
<para>Show current network information and statistics,
such as routing tables and active connections. This utility
accesses information in <filename>/proc/net</filename>
(<xref linkend="devproc">). See <xref
linkend="constat">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uptimeref"><command>uptime</command></term>
<indexterm>
<primary>uptime</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uptime</secondary>
</indexterm>
<listitem>
<para>Shows how long the system has been running, along with
associated statistics.</para>
<screen><prompt>bash$ </prompt><userinput>uptime</userinput>
<computeroutput>10:28pm up 1:57, 3 users, load average: 0.17, 0.34, 0.27</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hnameref"><command>hostname</command></term>
<indexterm>
<primary>hostname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hostname</secondary>
</indexterm>
<listitem>
<para>Lists the system's host name. This command sets the host
name in an <filename class="directory">/etc/rc.d</filename>
setup script (<filename>/etc/rc.d/rc.sysinit</filename>
or similar). It is equivalent to <command>uname
-n</command>, and a counterpart to the <link
linkend="hostnameref">$HOSTNAME</link> internal
variable.</para>
<screen><prompt>bash$ </prompt><userinput>hostname</userinput>
<computeroutput>localhost.localdomain</computeroutput>
<prompt>bash$ </prompt><userinput>echo $HOSTNAME</userinput>
<computeroutput>localhost.localdomain</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>hostid</command></term>
<indexterm>
<primary>hostid</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>host id</secondary>
</indexterm>
<listitem>
<para>Echo a 32-bit hexadecimal numerical identifier for the
host machine.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>hostid</userinput>
<computeroutput>7f0100</computeroutput></screen>
</para>
<note>
<para>This command allegedly fetches a <quote>unique</quote>
serial number for a particular system. Certain
product registration procedures use this number
to brand a particular user license. Unfortunately,
<command>hostid</command> only returns the machine
network address in hexadecimal, with pairs of bytes
transposed.</para>
<para>The network address of a typical non-networked Linux
machine, is found in <filename>/etc/hosts</filename>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cat /etc/hosts</userinput>
<computeroutput>127.0.0.1 localhost.localdomain localhost</computeroutput>
</screen>
</para>
<para>As it happens, transposing the bytes of
<userinput>127.0.0.1</userinput>, we get
<userinput>0.127.1.0</userinput>, which translates in
hex to <userinput>007f0100</userinput>, the exact equivalent
of what <command>hostid</command> returns, above. There
exist only a few million other Linux machines with this
identical <emphasis>hostid</emphasis>.</para>
</note>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="syslog">
<title><anchor id="syslog1">System Logs</title>
<varlistentry>
<term><command>logger</command></term>
<indexterm>
<primary>logger</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logger</secondary>
</indexterm>
<listitem>
<para>Appends a user-generated message to the system log
(<filename>/var/log/messages</filename>). You do not have
to be root to invoke <command>logger</command>.
<programlisting>logger Experiencing instability in network connection at 23:10, 05/21.
# Now, do a 'tail /var/log/messages'.</programlisting></para>
<para>By embedding a <command>logger</command> command in a script,
it is possible to write debugging information to
<filename>/var/log/messages</filename>.
<programlisting>logger -t $0 -i Logging at line "$LINENO".
# The "-t" option specifies the tag for the logger entry.
# The "-i" option records the process ID.
# tail /var/log/message
# ...
# Jul 7 20:48:58 localhost ./test.sh[1712]: Logging at line 3.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>logrotate</command></term>
<indexterm>
<primary>logrotate</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logrotate</secondary>
</indexterm>
<listitem>
<para>This utility manages the system log files, rotating,
compressing, deleting, and/or mailing them, as appropriate.
Usually <command>cron</command> runs
<command>logrotate</command> on a daily basis.</para>
<para>Adding an appropriate entry to
<filename>/etc/logrotate.conf</filename> makes it possible
to manage personal log files, as well as system-wide
ones.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="jobcontrolsys">
<title><anchor id="jobcontrolsys1">Job Control</title>
<varlistentry>
<term><command>nice</command></term>
<indexterm>
<primary>nice</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>nice</secondary>
</indexterm>
<listitem>
<para>Run a background job with an altered
priority. Priorities run from 19 (lowest) to -20
(highest). Only <emphasis>root</emphasis> may set the
negative (higher) priorities. Related commands are
<command>renice</command>, <command>snice</command>,
and <command>skill</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>nohup</command></term>
<indexterm>
<primary>nohup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>nohup</secondary>
</indexterm>
<listitem>
<para>Keeps a command running even after user logs off.
The command will run as a foreground process unless followed
by <token>&</token>. If you use <command>nohup</command>
within a script, consider coupling it with a <link
linkend="waitref">wait</link> to avoid creating an orphan
or zombie process.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pidofref"><command>pidof</command></term>
<indexterm>
<primary>pidof</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>process id</secondary>
</indexterm>
<listitem>
<para>Identifies <emphasis>process id (pid)</emphasis> of a
running job. Since job control commands, such as <link
linkend="killref">kill</link> and <command>renice</command>
act on the <emphasis>pid</emphasis> of a process (not
its name), it is sometimes necessary to identify that
<emphasis>pid</emphasis>. The <command>pidof</command>
command is the approximate counterpart to the <link
linkend="ppidref">$PPID</link> internal variable.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>pidof xclock</userinput>
<computeroutput>880</computeroutput>
</screen>
</para>
<example id="killprocess">
<title><command>pidof</command> helps kill a process</title>
<programlisting>&killprocess;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>fuser</command></term>
<indexterm>
<primary>fuser</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fuser</secondary>
</indexterm>
<listitem>
<para>Identifies the processes (by pid) that are accessing
a given file, set of files, or directory. May also be
invoked with the <option>-k</option> option, which kills
those processes. This has interesting implications for
system security, especially in scripts preventing
unauthorized users from accessing system services.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cronref"><command>cron</command></term>
<indexterm>
<primary>cron</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cron</secondary>
</indexterm>
<listitem>
<para>Administrative program scheduler, performing such
duties as cleaning up and deleting system log
files and updating the <database>slocate</database>
database. This is the superuser version of <link
linkend="atref">at</link> (although each user may have their
own <filename>crontab</filename> file which can be changed
with the <command>crontab</command> command). It runs
as a <link linkend="daemonref">daemon</link> and executes
scheduled entries from <filename>/etc/crontab</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="runcontrolsys">
<title><anchor id="runcontrolsys1">Process Control and Booting</title>
<varlistentry>
<term><command>init</command></term>
<indexterm>
<primary>init</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>init</secondary>
</indexterm>
<listitem>
<para>The <command>init</command> command is the <link
linkend="forkref">parent</link> of all processes. Called
in the final step of a bootup, <command>init</command>
determines the runlevel of the system from
<filename>/etc/inittab</filename>. Invoked by its alias
<command>telinit</command>, and by root only.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>telinit</command></term>
<indexterm>
<primary>telinit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>telinit</secondary>
</indexterm>
<listitem>
<para>Symlinked to <command>init</command>, this is a means of changing the system runlevel,
usually done for system maintenance or emergency filesystem
repairs. Invoked only by root. This command can be dangerous - be
certain you understand it well before using!</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>runlevel</command></term>
<indexterm>
<primary>runlevel</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>runlevel</secondary>
</indexterm>
<listitem>
<para>Shows the current and last runlevel, that is, whether the system
is halted (runlevel <literal>0</literal>), in single-user mode
(<literal>1</literal>), in multi-user mode (<literal>2</literal>
or <literal>3</literal>), in X Windows (<literal>5</literal>), or
rebooting (<literal>6</literal>). This command accesses the
<filename>/var/run/utmp</filename> file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>halt</command></term>
<term><command>shutdown</command></term>
<term><command>reboot</command></term>
<indexterm>
<primary>halt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>halt</secondary>
</indexterm>
<indexterm>
<primary>shutdown</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>shutdown</secondary>
</indexterm>
<indexterm>
<primary>reboot</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>reboot</secondary>
</indexterm>
<listitem>
<para>Command set to shut the system down, usually just prior to a power down.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="networksys">
<title><anchor id="networksys1">Network</title>
<varlistentry>
<term><command>ifconfig</command></term>
<indexterm>
<primary>ifconfig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ifconfig</secondary>
</indexterm>
<listitem>
<para>Network interface configuration and tuning utility.
It is most often used at bootup to set up the interfaces,
or to shut them down when rebooting.
<programlisting># Code snippets from /etc/rc.d/init.d/network
# ...
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
[ -x /sbin/ifconfig ] || exit 0
# ...
for i in $interfaces ; do
if ifconfig $i 2>/dev/null | grep -q "UP" >/dev/null 2>&1 ; then
action "Shutting down interface $i: " ./ifdown $i boot
fi
# The GNU-specific "-q" option to to "grep" means "quiet", i.e., producing no output.
# Redirecting output to /dev/null is therefore not strictly necessary.
# ...
echo "Currently active devices:"
echo `/sbin/ifconfig | grep ^[a-z] | awk '{print $1}'`
# ^^^^^ should be quoted to prevent globbing.
# The following also work.
# echo $(/sbin/ifconfig | awk '/^[a-z]/ { print $1 })'
# echo $(/sbin/ifconfig | sed -e 's/ .*//')
# Thanks, S.C., for additional comments.</programlisting>
See also <xref linkend="online">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>route</command></term>
<indexterm>
<primary>route</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>route</secondary>
</indexterm>
<listitem>
<para>Show info about or make changes to the kernel routing table.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>route</userinput>
<computeroutput>Destination Gateway Genmask Flags Metric Ref Use Iface
127.0.0.0 * 255.0.0.0 U 0 0 0 lo</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mknod</command></term>
<indexterm>
<primary>mknod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mknod</secondary>
</indexterm>
<listitem>
<para>Creates block or character device files (may be necessary when installing
new hardware on the system).</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="filesystemsys">
<title><anchor id="filesystemsys1">Filesystem</title>
<varlistentry>
<term><command>mount</command></term>
<indexterm>
<primary>mount</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mount</secondary>
</indexterm>
<listitem>
<para>Mount a filesystem, usually on an external device,
such as a floppy or CDROM. The file
<filename>/etc/fstab</filename> provides a handy listing
of available filesystems, partitions, and devices,
including options, that may be automatically or manually
mounted. The file <filename>/etc/mtab</filename> shows
the currently mounted filesystems and partitions
(including the virtual ones, such as <filename
class="directory">/proc</filename>).</para>
<para><command>mount -a</command> mounts all filesystems and
partitions listed in <filename>/etc/fstab</filename>,
except those with a <option>noauto</option>
option. At bootup, a startup script in
<filename class="directory">/etc/rc.d</filename>
(<filename>rc.sysinit</filename> or something similar)
invokes this to get everything mounted.</para>
<para><programlisting>mount -t iso9660 /dev/cdrom /mnt/cdrom
# Mounts CDROM
mount /mnt/cdrom
# Shortcut, if /mnt/cdrom listed in /etc/fstab</programlisting>
</para>
<para>This versatile command can even mount an ordinary file as if
it were a filesystem on a block device. It accomplishes that by
associating the file with a <link linkend="loopbackref">loopback
device</link>. One application of this is to mount and examine an
ISO9660 image before burning it onto a CDR.
<footnote><para>For more detail on burning CDRs, see Alex
Withers' article, <ulink
url="http://www2.linuxjournal.com/lj-issues/issue66/3335.html">Creating
CDs</ulink>, in the October, 1999 issue of <ulink
url="http://www.linuxjournal.com">Linux
Journal</ulink>.</para></footnote>
</para>
<example id="isomountref">
<title>Checking a CD image</title>
<programlisting># As root...
mkdir /mnt/cdtest # Prepare a mount point, if not already there.
mount -r -t iso9660 -o loop cd-image.iso /mnt/cdtest # Mount the image.
# "-o loop" option equivalent to "losetup /dev/loop0"
cd /mnt/cdtest # Now, check the image.
ls -alR # List the files in the directory tree there.
# And so forth.</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>umount</command></term>
<indexterm>
<primary>umount</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>umount</secondary>
</indexterm>
<listitem>
<para>Unmount a currently mounted filesystem. Before physically removing a
previously mounted floppy or CDROM disk, the device must be
<command>umount</command>ed, else filesystem corruption may result.
<programlisting>umount /mnt/cdrom
# You may now press the eject button and safely remove the disk.</programlisting></para>
<note><para>The <command>automount</command> utility, if
properly installed, can mount and unmount floppies or
CDROM disks as they are accessed or removed. On laptops
with swappable floppy and CDROM drives, this can cause
problems, though.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>sync</command></term>
<indexterm>
<primary>sync</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sync</secondary>
</indexterm>
<listitem>
<para>Forces an immediate write of all updated data from
buffers to hard drive (synchronize drive
with buffers). While not strictly necessary, a
<command>sync</command> assures the sys admin or
user that the data just changed will survive a sudden
power failure. In the olden days, a <userinput>sync;
sync</userinput> (twice, just to make absolutely sure) was a
useful precautionary measure before a system reboot.</para>
<para>At times, you may wish to force an immediate buffer
flush, as when securely deleting a file. See
<xref linkend="blotout">.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="losetupref"><command>losetup</command></term>
<indexterm>
<primary>losetup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>losetup</secondary>
</indexterm>
<listitem>
<para>Sets up and configures <link linkend="loopbackref">
loopback devices</link>.</para>
<example id="createfs">
<title>Creating a filesystem in a file</title>
<programlisting>SIZE=1000000 # 1 meg
head -c $SIZE < /dev/zero > file # Set up file of designated size.
losetup /dev/loop0 file # Set it up as loopback device.
mke2fs /dev/loop0 # Create filesystem.
mount -o loop /dev/loop0 /mnt # Mount it.
# Thanks, S.C.</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mkswap</command></term>
<indexterm>
<primary>mkswap</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkswap</secondary>
</indexterm>
<listitem>
<para>Creates a swap partition or file. The swap area must
subsequently be enabled with
<command>swapon</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>swapon</command></term>
<term><command>swapoff</command></term>
<indexterm>
<primary>swapon</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>swapon</secondary>
</indexterm>
<indexterm>
<primary>swapoff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>swapoff</secondary>
</indexterm>
<listitem>
<para>Enable / disable swap partitition or file.
These commands usually take effect at bootup and
shutdown.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>mke2fs</command></term>
<indexterm>
<primary>mke2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mke2fs</secondary>
</indexterm>
<listitem>
<para>Create a Linux ext2 filesystem. This command must be invoked
as root.</para>
<example id="adddrv">
<title>Adding a new hard drive</title>
<programlisting>&adddrv;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><command>tune2fs</command></term>
<indexterm>
<primary>tune2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tune2fs</secondary>
</indexterm>
<listitem>
<para>Tune ext2 filesystem. May be used to change filesystem
parameters, such as maximum mount count. This must be
invoked as root.</para>
<warning><para>This is an extremely dangerous command. Use it at
your own risk, as you may inadvertently destroy your filesystem.
</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><command>dumpe2fs</command></term>
<indexterm>
<primary>dumpe2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dumpe2fs</secondary>
</indexterm>
<listitem>
<para>Dump (list to <filename>stdout</filename>) very verbose
filesystem info. This must be invoked as root.</para>
<screen><prompt>root# </prompt><command>dumpe2fs /dev/hda7 | grep 'ount count'</command>
<computeroutput>dumpe2fs 1.19, 13-Jul-2000 for EXT2 FS 0.5b, 95/08/09
Mount count: 6
Maximum mount count: 20</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fdiskref"><command>fdisk</command></term>
<indexterm>
<primary>fdisk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fdisk</secondary>
</indexterm>
<listitem>
<para>Create or change a partition table on a storage device,
usually a hard drive. This command must be invoked as
root.</para>
<warning><para>Use this command with extreme caution. If something
goes wrong, you may destroy an existing
filesystem.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fsckref"><command>fsck</command></term>
<term><command>e2fsck</command></term>
<term><command>debugfs</command></term>
<indexterm>
<primary>fsck</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fsck</secondary>
</indexterm>
<indexterm>
<primary>e2fsck</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>e2fsck</secondary>
</indexterm>
<indexterm>
<primary>debugfs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>debugfs</secondary>
</indexterm>
<listitem>
<para>Filesystem check, repair, and debug command set.</para>
<para><command>fsck</command>: a front end for checking a UNIX
filesystem (may invoke other utilities). The actual
filesystem type generally defaults to ext2.</para>
<para><command>e2fsck</command>: ext2 filesystem checker.</para>
<para><command>debugfs</command>: ext2 filesystem debugger.</para>
<caution><para>All of these should be invoked as root, and they
can damage or destroy a filesystem if misused.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><command>chroot</command></term>
<indexterm>
<primary>chroot</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chroot</secondary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>root</secondary>
<tertiary>change</tertiary>
</indexterm>
<listitem>
<para>CHange ROOT directory. Normally commands are fetched
from <link linkend="pathref">$PATH</link>, relative to
<filename class="directory">/</filename>, the default root
directory. This changes the root directory to a different
one (and also changes the working directory to there).
This is useful for security purposes, for instance when
the system administrator wishes to restrict certain users,
such as those <link linkend="telnetref">telnetting</link>
in, to a secured portion of the filesystem (this
is sometimes referred to as confining a guest user
to a <quote>chroot jail</quote>). Note that after a
<command>chroot</command>, the execution path for system
binaries is no longer valid.</para>
<para>A <userinput>chroot /opt</userinput> would cause
references to <filename
class="directory">/usr/bin</filename>
to be translated to <filename
class="directory">/opt/usr/bin</filename>. Likewise,
<userinput>chroot /aaa/bbb /bin/ls</userinput> would
redirect future instances of <command>ls</command>
to <filename>/aaa/bbb</filename> as the base directory,
rather than <filename class="directory">/</filename> as is
normally the case. An <command>alias XX 'chroot /aaa/bbb
ls'</command> in a user's <filename>~/.bashrc</filename>
effectively restricts which portion of the filesystem
she may run command <quote>XX</quote> on.</para>
<para>The <command>chroot</command> command is also handy
when running from an emergency boot floppy
(<command>chroot</command> to <filename>/dev/fd0</filename>),
or as an option to <command>lilo</command> when recovering
from a system crash. Other uses include installation
from a different filesystem (an <command>rpm</command>
option) or running a readonly filesystem from a CD ROM.
Invoke only as root, and use with care.</para>
<caution><para>It might be necessary to copy certain system
files to a <emphasis>chrooted</emphasis> directory,
since the normal <varname>$PATH</varname> can no longer
be relied upon.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><command>lockfile</command></term>
<indexterm>
<primary>lockfile</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lockfile</secondary>
</indexterm>
<listitem>
<para>This utility is part of the <command>procmail</command>
package (<ulink url="http://www.procmail.org">www.procmail.org</ulink>).
It creates a <emphasis>lock file</emphasis>, a semaphore file that
controls access to a file, device, or resource. The lock file
serves as a flag that this particular file, device, or resource is
in use by a particular process (<quote>busy</quote>), and
this permits only restricted access (or no access) to other
processes.</para>
<para>Lock files are used in such applications as protecting
system mail folders from simultaneously being changed by multiple
users, indicating that a modem port is being accessed, and showing
that an instance of <application>Netscape</application> is using its
cache. Scripts may check for the existence of a lock file created
by a certain process to check if that process is running. Note
that if a script attempts create a lock file that already exists,
the script will likely hang.</para>
<para>Normally, applications create and check for lock files
in the <filename class="directory">/var/lock</filename>
directory. A script can test for the presence of a lock file by
something like the following.
<programlisting>appname=xyzip
# Application "xyzip" created lock file "/var/lock/xyzip.lock".
if [ -e "/var/lock/$appname.lock ]
then
...</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="periphsys">
<title><anchor id="periphsys1">Backup</title>
<varlistentry>
<term><command>dump</command></term>
<term><command>restore</command></term>
<indexterm>
<primary>dump</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dump</secondary>
</indexterm>
<indexterm>
<primary>restore</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>restore</secondary>
</indexterm>
<listitem>
<para>The <command>dump</command> command is an elaborate
filesystem backup utility, generally used on larger
installations and networks.
<footnote><para>Operators of single-user Linux systems
generally prefer something simpler for backups, such
as <command>tar</command>.</para></footnote>
It reads raw disk partitions and writes a backup file
in a binary format. Files to be backed up may be saved
to a variety of storage media, including disks and tape
drives. The <command>restore</command> command restores
backups made with <command>dump</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>fdformat</command></term>
<indexterm>
<primary>fdformat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>floppy</secondary>
</indexterm>
<listitem>
<para>Perform a low-level format on a floppy disk.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="sysresources">
<title><anchor id="sysresources1">System Resources</title>
<varlistentry>
<term><command>ulimit</command></term>
<indexterm>
<primary>ulimit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ulimit</secondary>
</indexterm>
<listitem>
<para>Sets an <emphasis>upper limit</emphasis> on system
resources. Usually invoked with the <option>-f</option>
option, which sets a limit on file size (<command>ulimit
-f 1000</command> limits files to 1 meg maximum). The
<option>-t</option> option limits the coredump size
(<command>ulimit -c 0</command> eliminates coredumps).
Normally, the value of <command>ulimit</command>
would be set in <filename>/etc/profile</filename>
and/or <filename>~/.bash_profile</filename> (see <xref
linkend="files">).</para>
</listitem>
</varlistentry>
<varlistentry>
<indexterm>
<primary>umask</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>umask</secondary>
</indexterm>
<term><command>umask</command></term>
<listitem>
<para>User file creation MASK. Limit the default file attributes
for a particular user. All files created by that user take
on the attributes specified by <command>umask</command>. The
(octal) value passed to <command>umask</command> defines the
the file permissions <emphasis>disabled</emphasis>. 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 <quote>not-and</quote>
operator. Its effect is somewhat similar to
subtraction.</para></footnote>
Of course, the user may later change the
attributes of particular files with <link
linkend="chmodref">chmod</link>.The usual practice
is to set the value of <command>umask</command>
in <filename>/etc/profile</filename> and/or
<filename>~/.bash_profile</filename> (see <xref
linkend="files">).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rdev</command></term>
<indexterm>
<primary>rdev</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rdev</secondary>
</indexterm>
<listitem>
<para>Get info about or make changes to root device, swap space, or video
mode. The functionality of <command>rdev</command> has generally been taken over by
<command>lilo</command>, but <command>rdev</command> remains
useful for setting up a ram disk. This is another dangerous command, if misused.
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="modulessys">
<title><anchor id="modulessys1">Modules</title>
<varlistentry>
<term><command>lsmod</command></term>
<indexterm>
<primary>lsmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lsmod</secondary>
</indexterm>
<listitem>
<para>List installed kernel modules.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lsmod</userinput>
<computeroutput>Module Size Used by
autofs 9456 2 (autoclean)
opl3 11376 0
serial_cs 5456 0 (unused)
sb 34752 0
uart401 6384 0 [sb]
sound 58368 0 [opl3 sb uart401]
soundlow 464 0 [sound]
soundcore 2800 6 [sb sound]
ds 6448 2 [serial_cs]
i82365 22928 2
pcmcia_core 45984 0 [serial_cs ds i82365]</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>insmod</command></term>
<indexterm>
<primary>insmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>insmod</secondary>
</indexterm>
<listitem>
<para>Force insertion of a kernel module. Must be invoked as root.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>modprobe</command></term>
<indexterm>
<primary>modprobe</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>modprobe</secondary>
</indexterm>
<listitem>
<para>Module loader that is normally invoked automatically in a startup script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>depmod</command></term>
<indexterm>
<primary>depmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>depmod</secondary>
</indexterm>
<listitem>
<para>Creates module dependency file, usually invoked from startup script.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="miscsys">
<title><anchor id="miscsys1">Miscellaneous</title>
<varlistentry>
<term><command>env</command></term>
<indexterm>
<primary>env</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>env</secondary>
</indexterm>
<listitem>
<para>Runs a program or script with certain environmental
variables set or changed (without changing the overall
system environment). The <option>[varname=xxx]</option>
permits changing the environmental variable
<varname>varname</varname> for the duration of the
script. With no options specified, this command lists all
the environmental variable settings.</para>
<note><para>In Bash and other Bourne shell derivatives, it is
possible to set variables in a single command's environment.
<programlisting>var1=value1 var2=value2 commandXXX
# $var1 and $var2 set in the environment of 'commandXXX' only.</programlisting>
</para></note>
<tip><para>The first line of a script (the
<quote>sha-bang</quote> line) may use <command>env</command>
when the path to the shell or interpreter is unknown.
<programlisting>#! /usr/bin/env perl
print "This Perl script will run,\n";
print "even when I don't know where to find Perl.\n";
# Good for portable cross-platform scripts,
# where the Perl binaries may not be in the expected place.
# Thanks, S.C.</programlisting>
</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><command>ldd</command></term>
<indexterm>
<primary>ldd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ldd</secondary>
</indexterm>
<listitem>
<para>Show shared lib dependencies for an executable file.</para>
<screen><prompt>bash$ </prompt><userinput>ldd /bin/ls</userinput>
<computeroutput>libc.so.6 => /lib/libc.so.6 (0x4000c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><command>nm</command></term>
<indexterm>
<primary>nm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>symbol</secondary>
</indexterm>
<listitem>
<para>List symbols in an unstripped compiled binary.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>rdist</command></term>
<indexterm>
<primary>rdist</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rdist</secondary>
</indexterm>
<listitem>
<para>Remote distribution client: synchronizes, clones,
or backs up a file system on a remote server.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Using our knowledge of administrative commands, let us examine a system
script. One of the shortest and simplest to understand scripts is
<command>killall</command>, used to suspend running processes at system shutdown.</para>
<example id="ex55">
<title><command>killall</command>, from <filename class="directory">/etc/rc.d/init.d</filename></title>
<programlisting>&ex55;</programlisting>
</example>
<para>That wasn't so bad. Aside from a little fancy footwork with variable
matching, there is no new material there.</para>
<formalpara><title>Exercise</title>
<para>In <filename class="directory">/etc/rc.d/init.d</filename>,
analyze the <command>halt</command> script. It is a bit longer than
<command>killall</command>, but similar in concept. Make a copy of this script somewhere in
your home directory and experiment with it (do <emphasis>not</emphasis> run it as root). Do
a simulated run with the <option>-vn</option> flags
(<userinput>sh -vn scriptname</userinput>). Add extensive comments. Change
the <quote>action</quote> commands to <quote>echos</quote>.</para></formalpara>
<para>Now, look at some of the more complex scripts in
<filename class="directory">/etc/rc.d/init.d</filename>. See if
you can understand parts of them. Follow the above procedure
to analyze them. For some additional insight, you might also
examine the file <filename>sysvinitfiles</filename> in <filename
class="directory">/usr/doc/initscripts-X.XX</filename>, which
is part of the <quote>initscripts</quote> documentation.</para>
</chapter> <!-- System and Administrative Commands -->
<chapter id="commandsub">
<title>Command Substitution</title>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>`</secondary>
</indexterm>
<para><emphasis><anchor id="commandsubref">Command
substitution</emphasis> reassigns the output of a command
or even multiple commands; it literally plugs the command
output into another context.</para>
<para><anchor id="backquotesref">The classic form of <emphasis>command
substitution</emphasis> uses backquotes ('...'). Commands
within backquotes (backticks) generate command line text.
<programlisting>script_name=`basename $0`
echo "The name of this script is $script_name."</programlisting></para>
<para>The output of commands can be used as arguments to
another command, to set a variable, and even for generating
the argument list in a <quote>for</quote> loop.
<programlisting>rm `cat filename`
# <quote>filename</quote> contains a list of files to delete.
#
# S. C. points out that "arg list too long" error might result.
# Better is xargs rm -- < filename
# ( -- covers those cases where <quote>filename</quote> begins with a <quote>-</quote> )
textfile_listing=`ls *.txt`
# Variable contains names of all *.txt files in current working directory.
echo $textfile_listing
textfile_listing2=$(ls *.txt) # The alternative form of command substitution.
echo $textfile_listing
# Same result.
# A possible problem with putting a list of files into a single string
# is that a newline may creep in.
#
# A safer way to assign a list of files to a parameter is with an array.
# shopt -s nullglob # If no match, filename expands to nothing.
# textfile_listing=( *.txt )
#
# Thanks, S.C.</programlisting></para>
<caution><para>Command substitution may result in word splitting.
<programlisting>COMMAND `echo a b` # 2 args: a and b
COMMAND "`echo a b`" # 1 arg: "a b"
COMMAND `echo` # no arg
COMMAND "`echo`" # one empty arg
# Thanks, S.C.</programlisting></para></caution>
<caution>
<para>Word splitting resulting from command
substitution may remove trailing newlines characters
from the output of the reassigned command(s). This can
cause unpleasant surprises.
<programlisting>dir_listing=`ls -l`
echo $dirlisting
# Expecting a nicely ordered directory listing, such as:
# -rw-rw-r-- 1 bozo 30 May 13 17:15 1.txt
# -rw-rw-r-- 1 bozo 51 May 15 20:57 t2.sh
# -rwxr-xr-x 1 bozo 217 Mar 5 21:13 wi.sh
# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh
# The newlines disappeared.</programlisting>
</para>
<para>Even when there is no word splitting, command
substitution can remove trailing newlines.
<programlisting># cd "`pwd`" # This should always work.
# However...
mkdir 'dir with trailing newline
'
cd 'dir with trailing newline
'
cd "`pwd`" # Error message:
# bash: cd: /tmp/file with trailing newline: No such file or directory
cd "$PWD" # Works fine.
old_tty_setting=$(stty -g) # Save old terminal setting.
echo "Hit a key "
stty -icanon -echo # Disable "canonical" mode for terminal.
# Also, disable *local* echo.
key=$(dd bs=1 count=1 2&gt; /dev/null) # Using 'dd' to get a keypress.
stty "$old_tty_setting" # Restore old setting.
echo "You hit ${#key} key." # ${#variable} = number of characters in $variable
#
# Hit any key except RETURN, and the output is "You hit 1 key."
# Hit RETURN, and it's "You hit 0 key."
# The newline gets eaten in the command substitution.
Thanks, S.C.</programlisting>
</para>
</caution>
<note><para>The <command>$(COMMAND)</command> form has
superseded backticks for command substitution.
<programlisting>output=$(sed -n /"$1"/p $file)
# From "grp.sh" example.</programlisting></para></note>
<para>Examples of command substitution in shell scripts:
<orderedlist>
<listitem><para><xref linkend="bingrep"></para></listitem>
<listitem><para><xref linkend="casecmd"></para></listitem>
<listitem><para><xref linkend="seedingrandom"></para></listitem>
<listitem><para><xref linkend="ex57"></para></listitem>
<listitem><para><xref linkend="lowercase"></para></listitem>
<listitem><para><xref linkend="grp"></para></listitem>
<listitem><para><xref linkend="ex53"></para></listitem>
<listitem><para><xref linkend="ex24"></para></listitem>
<listitem><para><xref linkend="symlinks"></para></listitem>
<listitem><para><xref linkend="stripc"></para></listitem>
<listitem><para><xref linkend="redir4"></para></listitem>
<listitem><para><xref linkend="tree"></para></listitem>
<listitem><para><xref linkend="pidid"></para></listitem>
<listitem><para><xref linkend="monthlypmt"></para></listitem>
<listitem><para><xref linkend="base"></para></listitem>
</orderedlist>
</para>
</chapter> <!-- Command Substitution -->
<chapter id="arithexp">
<title>Arithmetic Expansion</title>
<para><anchor id="arithexpref">Arithmetic expansion provides a
powerful tool for performing arithmetic operations
in scripts. Translating a string into a numerical
expression is relatively straightforward using
<link linkend="backquotesref">backticks</link>, <link
linkend="dblparens">double parentheses</link>, or <link
linkend="letref">let</link>.</para>
<variablelist id="arithexpvar">
<title><anchor id="arithexpvar1">Variations</title>
<varlistentry>
<term>Arithmetic expansion with backticks (often used in
conjunction with <link linkend="exprref">expr</link>)</term> <indexterm>
<primary>arithmetic</primary> <secondary>expansion</secondary>
</indexterm> <indexterm>
<primary>arithmetic</primary> <secondary>expansion</secondary>
</indexterm> <listitem>
<para><programlisting>z=`expr $z + 3` # 'expr' does the expansion.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term>Arithmetic expansion with double parentheses</term>
<indexterm><primary>double</primary>
<secondary>parentheses</secondary></indexterm>
<term>and using <command>let</command></term>
<indexterm><primary>let</primary>
<secondary>let</secondary></indexterm>
<listitem>
<para>The use of backticks in arithmetic
expansion has been superseded by double parentheses
<userinput>$((...))</userinput> or the very
convenient <command>let</command> construction.
<programlisting>z=$(($z+3))
# $((EXPRESSION)) is arithmetic expansion. # Not to be confused with
# command substitution.
let z=z+3
let "z += 3" #If quotes, then spaces and special operators allowed.
# 'let' is actually arithmetic evaluation, rather than expansion.</programlisting>
All the above are equivalent. You may use whichever one
<quote>rings your chimes</quote>.
</para>
<para>Examples of arithmetic expansion in scripts:
<orderedlist>
<listitem><para><xref linkend="ex45"></para></listitem>
<listitem><para><xref linkend="ex25"></para></listitem>
<listitem><para><xref linkend="ex66"></para></listitem>
<listitem><para><xref linkend="bubble"></para></listitem>
<listitem><para><xref linkend="tree"></para></listitem>
</orderedlist>
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Arithmetic Expansion -->
<chapter id="io-redirection">
<title>I/O Redirection</title>
<para><anchor id="ioredirref"></para>
<para>There are always three default <quote>files</quote>
open, <filename>stdin</filename> (the keyboard),
<filename>stdout</filename> (the screen), and
<filename>stderr</filename> (error messages output to the
screen). These, and any other open files, can be redirected.
Redirection simply means capturing output from a file, command,
program, script, or even code block within a script (see <xref
linkend="ex8"> and <xref linkend="rpmcheck">) and sending it as
input to another file, command, program, or script.</para>
<para><anchor id="fdref">Each open file gets assigned a file descriptor.
<footnote><para>A <emphasis>file descriptor</emphasis>
is simply a number that the operating system assigns
to an open file to keep track of it. Consider it
a simplified version of a file pointer. It is
analogous to a <emphasis>file handle</emphasis> in
C.</para></footnote>
The file descriptors for <filename>stdin</filename>,
<filename>stdout</filename>, and <filename>stderr</filename> are
0, 1, and 2, respectively. For opening additional files, there
remain descriptors 3 to 9. It is sometimes useful to assign one of
these additional file descriptors to <filename>stdin</filename>,
<filename>stdout</filename>, or <filename>stderr</filename>
as a temporary duplicate link.
<footnote><para>Using <replaceable>file
descriptor 5</replaceable> might cause problems.
When Bash creates a child process, as with <link
linkend="execref">exec</link>, the child inherits
fd 5 (see Chet Ramey's archived e-mail, <ulink
url="http://www.geocrawler.com/archives/3/342/1996/1/0/1939805/">
SUBJECT: RE: File descriptor 5 is held open</ulink>).
Best leave this particular fd alone.</para></footnote>
This simplifies restoration to normal after complex redirection
and reshuffling (see <xref linkend="redir1">).</para>
<para><anchor id="ioredirectionref"></para>
<programlisting> >
# Redirect stdout to a file.
# Creates the file if not present, otherwise overwrites it.
ls -lR > dir-tree.list
# Creates a file containing a listing of the directory tree.
: > filename
# The > truncates file "filename" to zero length.
# The : serves as a dummy placeholder, producing no output.
>>
# Redirect stdout to a file.
# Creates the file if not present, otherwise appends to it.
2>&amp;1
# Redirects stderr to stdout.
# Error messages get sent to same place as standard output.
i>&amp;j
# Redirects file descriptor <emphasis>i</emphasis> to <emphasis>j</emphasis>.
# All output of file pointed to by <emphasis>i</emphasis> gets sent to file pointed to by <emphasis>j</emphasis>.
>&amp;j
# Redirects, by default, file descriptor <emphasis>1</emphasis> (stdout) to <emphasis>j</emphasis>.
# All stdout gets sent to file pointed to by <emphasis>j</emphasis>.
0<
<
# Accept input from a file.
# Companion command to <quote>></quote>, and often used in combination with it.
#
# grep search-word &lt;filename
[j]&lt;&gt;filename
# Open file "filename" for reading and writing, and assign file descriptor "j" to it.
# If "filename" does not exist, create it.
# If file descriptor "j" is not specified, default to fd 0, stdin.
#
# An application of this is writing at a specified place in a file.
echo 1234567890 > File # Write string to "File".
exec 3&lt;&gt; File # Open "File" and assign fd 3 to it.
read -n 4 <&3 # Read only 4 characters.
echo -n . >&3 # Write a decimal point there.
exec 3>&- # Close fd 3.
cat File # ==> 1234.67890
# Random access, by golly.
|
# Pipe.
# General purpose process and command chaining tool.
# Similar to <quote>></quote>, but more general in effect.
# Useful for chaining commands, scripts, files, and programs together.
cat *.txt | sort | uniq > result-file
# Sorts the output of all the .txt files and deletes duplicate lines,
# finally saves results to <quote>result-file</quote>.
</programlisting>
<para>Multiple instances of input and output redirection
and/or pipes can be combined in a single command
line.
<programlisting>command &lt; input-file &gt; output-file
command1 | command2 | command3 > output-file</programlisting>
See <xref linkend="derpm"> and <xref linkend="fifo">.</para>
<para>Multiple output streams may be redirected to one file.
<programlisting>ls -yz >> command.log 2>&1
# Capture result of illegal options "yz" to "ls" in file "command.log".
# Because stderr redirected to the file, any error messages will also be there.
</programlisting></para>
<variablelist id="closingfiledescriptors">
<title><anchor id="cfd">Closing File Descriptors</title>
<varlistentry>
<term><token>n<&-</token></term>
<listitem>
<para>Close input file descriptor
<replaceable>n</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>0<&-</token></term>
<term><token><&-</token></term>
<listitem>
<para>Close <filename>stdin</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>n>&-</token></term>
<listitem>
<para>Close output file descriptor <replaceable>n</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>1>&-</token></term>
<term><token>>&-</token></term>
<listitem>
<para>Close <filename>stdout</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Child processes inherit open file descriptors. This is why pipes
work. To prevent an fd from being inherited, close it.
<programlisting># Redirecting only stderr to a pipe.
exec 3>&1 # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&- # Close fd 3 for 'ls' and 'grep'.
exec 3>&- # Now close it for the remainder of the script.
# Thanks, S.C.</programlisting>
</para>
<para>For a more detailed introduction to I/O redirection see
<xref linkend="ioredirintro">.</para>
<sect1><title>Using <command>exec</command></title>
<para>The <command>exec &lt;filename</command> command redirects
<filename>stdin</filename> to a file. From that point on, all
<filename>stdin</filename> comes from that file, rather than
its normal source (usually keyboard input). This provides a
method of reading a file line by line and possibly parsing
each line of input using <link linkend="sedref">sed</link>
and/or <link linkend="awkref">awk</link>.</para>
<example id="redir1">
<title>Redirecting <filename>stdin</filename> using
<command>exec</command></title>
<programlisting>&redir1;</programlisting>
</example>
</sect1><!-- Using exec For Redirection -->
<sect1 id="redircb"><title>Redirecting Code Blocks</title>
<para><anchor id="redirref">Blocks of code, such as <link
linkend="whileloopref">while</link>, <link
linkend="untilloopref">until</link>, and <link
linkend="forloopref">for</link> loops, even <link
linkend="ifthen">if/then</link> test blocks can also incorporate
redirection of <filename>stdin</filename>. Even a function may
use this form of redirection (see <xref linkend="realname">).
The <token>&lt;</token> operator at the the end of the code
block accomplishes this.</para>
<example id="redir2">
<title>Redirected <emphasis>while</emphasis> loop</title>
<programlisting>&redir2;</programlisting>
</example>
<example id="redir2a">
<title>Alternate form of redirected <emphasis>while</emphasis> loop</title>
<programlisting>&redir2a;</programlisting>
</example>
<example id="redir3">
<title>Redirected <emphasis>until</emphasis> loop</title>
<programlisting>&redir3;</programlisting>
</example>
<example id="redir4">
<title>Redirected <emphasis>for</emphasis> loop</title>
<programlisting>&redir4;</programlisting>
</example>
<example id="redir5">
<title>Redirected <emphasis>if/then</emphasis> test</title>
<programlisting>&redir5;</programlisting>
</example>
<note><para><link linkend="heredocref">Here documents</link>
are a special case of redirected code blocks.</para></note>
</sect1><!-- Redirecting Code Blocks -->
<sect1 id="redirapps"><title>Applications</title>
<para>Clever use of I/O redirection permits parsing and stitching
together snippets of command output (see <xref
linkend="readredir">). This permits
generating report and log files.</para>
<example id="logevents">
<title>Logging events</title>
<programlisting>&logevents;</programlisting>
</example>
</sect1><!-- Applications -->
</chapter> <!-- I/O Redirection -->
<chapter id="here-docs">
<title>Here Documents</title>
<para><anchor id="heredocref"></para>
<indexterm>
<primary><<</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary><<</secondary>
</indexterm>
<para>A <firstterm>here document</firstterm> uses a special form
of <link linkend="ioredirref">I/O redirection</link>
to feed a command script to an interactive program, such
as <link linkend="ftpref">ftp</link>, <link
linkend="telnetref">telnet</link>, or <command>ex</command>.
Typically, the script consists of a command list to the
program, delineated by a limit string. The special symbol
<token><<</token> precedes the limit string. This has the effect
of redirecting the output of a file into the program, similar
to <userinput>interactive-program < command-file</userinput>,
where <filename>command-file</filename> contains
<programlisting>command #1
command #2
...</programlisting></para>
<para>The <quote>here document</quote> alternative looks like this:
<programlisting>#!/bin/bash
interactive-program &lt;&lt;LimitString
command #1
command #2
...
LimitString</programlisting></para>
<para>Choose a limit string sufficiently unusual that it will not occur anywhere
in the command list and confuse matters.</para>
<para>Note that <emphasis>here documents</emphasis> may sometimes
be used to good effect with non-interactive utilities and
commands.</para>
<example id="ex69">
<title><command>dummyfile</command>: Creates a 2-line dummy file</title>
<programlisting>&ex69;</programlisting>
</example>
<para>The above script could just as effectively have been implemented with
<command>ex</command>, rather than <command>vi</command>. Here documents
containing a list of <command>ex</command> commands are common enough to
form their own category, known as <firstterm>ex scripts</firstterm>.</para>
<example id="ex70">
<title><command>broadcast</command>: Sends message to everyone logged in</title>
<programlisting>&ex70;</programlisting>
</example>
<example id="ex71">
<title>Multi-line message using <command>cat</command></title>
<programlisting>&ex71;</programlisting>
</example>
<para>The <option>-</option> option to mark a here document limit string
(<userinput>&lt;&lt;-LimitString</userinput>) suppresses tabs (but
not spaces) in the output. This may be useful in making a script
more readable.</para>
<example id="ex71a">
<title>Multi-line message, with tabs suppressed</title>
<programlisting>&ex71a;</programlisting>
</example>
<para>A here document supports parameter and command substitution.
It is therefore possible to pass different parameters to the
body of the here document, changing its output accordingly.</para>
<example id="ex71b">
<title>Here document with parameter substitution</title>
<programlisting>&ex71b;</programlisting>
</example>
<para>Quoting or escaping the <quote>limit string</quote> at the
head of a here document disables parameter substitution within its
body. This has very limited usefulness.</para>
<example id="ex71c">
<title>Parameter substitution turned off</title>
<programlisting>&ex71c;</programlisting>
</example>
<para>This is a useful script containing a here document with
parameter substitution.</para>
<example id="ex72">
<title><command>upload</command>: Uploads a file pair to <quote>Sunsite</quote>
incoming directory</title>
<programlisting>&ex72;</programlisting>
</example>
<para>It is possible to use <token>:</token> as a dummy command
accepting output from a here document. This, in effect, creates an
<quote>anonymous</quote> here document.</para>
<example id="anonheredoc">
<title><quote>Anonymous</quote> Here Document</title>
<programlisting>#!/bin/bash
: &lt;&lt;TESTVARIABLES
${HOSTNAME?}${USER?}${MAIL?} # Print error message if one of the variables not set.
TESTVARIABLES
exit 0</programlisting>
</example>
<note>
<para>Here documents create temporary files, but these
files are deleted after opening and are not accessible to
any other process.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>bash -c 'lsof -a -p $$ -d0' << EOF</userinput>
<prompt>&gt; </prompt><userinput>EOF</userinput>
<computeroutput>lsof 1213 bozo 0r REG 3,5 0 30386 /tmp/t1213-0-sh (deleted)</computeroutput>
</screen>
</para>
</note>
<caution><para>Some utilities will not work in a here
document.</para></caution>
<para>For those tasks too complex for a <quote>here
document</quote>, consider using the <command>expect</command>
scripting language, which is specifically tailored for feeding
input into interactive programs.</para>
</chapter> <!-- Here Documents -->
<chapter id="Recess-Time">
<title>Recess Time</title>
<blockquote>
<literallayout>
This bizarre little intermission gives the reader a chance to
relax and maybe laugh a bit.
Fellow Linux user, greetings! You are reading something
which will bring you luck and good fortune. Just e-mail a
copy of this document to 10 of your friends. Before you make
the copies, send a 100-line Bash script to the first person
on the list given at the bottom of this letter. Then delete
their name and add yours to the bottom of the list.
Don't break the chain! Make the copies within 48 hours.
Wilfred P. of Brooklyn failed to send out his ten copies and
woke the next morning to find his job description changed
to "COBOL programmer." Howard L. of Newport News sent
out his ten copies and within a month had enough hardware
to build a 100-node Beowulf cluster dedicated to playing
<emphasis>xbill</emphasis>. Amelia V. of Chicago laughed at this letter and
broke the chain. Shortly thereafter, a fire broke out in her
terminal and she now spends her days writing documentation
for MS Windows.
Don't break the chain! Send out your ten copies today!
<emphasis>Courtesy 'NIX "fortune cookies", with some
alterations and many apologies</emphasis>
</literallayout>
</blockquote>
</chapter> <!-- Recess Time -->
</part> <!-- Part 3 (Beyond the Basics) -->
<part label="Part 4" id="part4">
<title>Advanced Topics</title>
<chapter id="regexp">
<title>Regular Expressions</title>
<para><anchor id="regexref"></para>
<para>To fully utilize the power of shell scripting, you need to
master Regular Expressions. Certain commands and
utilities commonly used in scripts, such as <link
linkend="exprref">expr</link>, <link linkend="sedref">sed</link>
and <link linkend="awkref">awk</link> interpret and use
REs.</para>
<sect1><title>A Brief Introduction to Regular Expressions</title>
<para>An expression is a string of characters. Those characters
that have an interpretation above and beyond their literal
meaning are called <emphasis>metacharacters</emphasis>. A
quote symbol, for example, may denote speech by a person,
<emphasis>ditto</emphasis>, or a meta-meaning for the symbols
that follow. Regular Expressions are sets of characters and/or
metacharacters that UNIX endows with special features.
<footnote><para>The simplest type of Regular Expression is a
character string that retains its literal meaning, not
containing any metacharacters.</para></footnote>
</para>
<para>The main uses for Regular Expressions (REs) are text
searches and string manipulation. An RE
<firstterm>matches</firstterm> a single character or a set of
characters (a substring or an entire string).</para>
<itemizedlist>
<listitem>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>*</secondary>
</indexterm>
<para>The asterisk <token>*</token> matches any number of
repeats of the character string or RE preceding it,
<emphasis>including zero</emphasis>.</para>
<para><quote>1133*</quote> matches <replaceable>11 +
one or more 3's + possibly other characters</replaceable>:
<replaceable>113</replaceable>, <replaceable>1133</replaceable>,
<replaceable>111312</replaceable>, and so forth.</para>
</listitem>
<listitem>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<para><anchor id="regexdot">The dot <token>.</token> matches
any one character, except a newline.
<footnote><para>Since <link linkend="sedref">sed</link>, <link
linkend="awkref">awk</link>, and <link
linkend="grepref">grep</link> process single lines, there
will usually not be a newline to match. In those cases where
there is a newline in a multiple line expression, the dot
will match the newline.
<programlisting>#!/bin/bash
sed -e 'N;s/.*/[&]/' &lt;&lt; EOF # Here Document
line1
line2
EOF
# OUTPUT:
# [line1
# line2]
echo
awk '{ $0=$1 "\n" $2; if (/line.1/) {print}}' &lt;&lt; EOF
line 1
line 2
EOF
# OUTPUT:
# line
# 1
# Thanks, S.C.
exit 0</programlisting></para></footnote>
</para>
<para><quote>13.</quote> matches <replaceable>13 + at
least one of any character (including a
space)</replaceable>: <replaceable>1133</replaceable>,
<replaceable>11333</replaceable>, but not
<replaceable>13</replaceable> (additional character
missing).</para>
</listitem>
<listitem>
<indexterm>
<primary>^</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>^</secondary>
</indexterm>
<para>The caret <token>^</token> matches the beginning of
a line, but sometimes, depending on context, negates the
meaning of a set of characters in an RE.
</para>
</listitem>
<listitem>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<para><anchor id="dollarsignref"></para>
<para>The dollar sign <token>$</token> at the end of an
RE matches the end of a line.</para>
<para><quote>^$</quote> matches blank lines.</para>
</listitem>
<listitem>
<indexterm>
<primary>[...]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[...]</secondary>
</indexterm>
<para>Brackets <token>[...]</token> enclose a set of characters
to match in a single RE.</para>
<para><quote>[xyz]</quote> matches the characters
<replaceable>x</replaceable>, <replaceable>y</replaceable>,
or <replaceable>z</replaceable>.</para>
<para><quote>[c-n]</quote> matches any of the
characters in the range <replaceable>c</replaceable>
to <replaceable>n</replaceable>.</para>
<para><quote>[B-Pk-y]</quote> matches any of the
characters in the ranges <replaceable>B</replaceable>
to <replaceable>P</replaceable>
and <replaceable>k</replaceable> to
<replaceable>y</replaceable>.</para>
<para><quote>[a-z0-9]</quote> matches any lowercase letter or any
digit.</para>
<para><quote>[^b-d]</quote> matches all characters
<emphasis>except</emphasis> those in
the range <replaceable>b</replaceable> to
<replaceable>d</replaceable>. This is an instance of
<token>^</token> negating or inverting the meaning
of the following RE (taking on a role similar to
<token>!</token> in a different context).</para>
<para>Combined sequences of bracketed characters match
common word patterns. <quote>[Yy][Ee][Ss]</quote> matches
<replaceable>yes</replaceable>, <replaceable>Yes</replaceable>,
<replaceable>YES</replaceable>, <replaceable>yEs</replaceable>,
and so forth.
<quote>[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]</quote>
matches any Social Security number.</para>
</listitem>
<listitem>
<indexterm>
<primary>\</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>\</secondary>
</indexterm>
<para>The backslash <token>\</token> <link
linkend="escp">escapes</link> a special character, which
means that character gets interpreted literally.</para>
<para>A <quote>\$</quote> reverts back to its
literal meaning of <quote>$</quote>, rather than its
RE meaning of end-of-line. Likewise a <quote>\\</quote>
has the literal meaning of <quote>\</quote>.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem override="square">
<formalpara>
<title>Extended REs</title>
<para>Used in <link linkend="egrepref">egrep</link>,
<link linkend="awkref">awk</link>, and <link
linkend="perlref">Perl</link></para>
</formalpara>
</listitem>
<listitem>
<indexterm>
<primary>?</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>?</secondary>
</indexterm>
<para>The question mark <token>?</token> matches zero or
one of the previous RE. It is generally used for matching
single characters.</para>
</listitem>
<listitem>
<indexterm>
<primary>+</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>+</secondary>
</indexterm>
<para><anchor id="plusref"></para>
<para>The plus <token>+</token> matches one or more of the
previous RE. It serves a role similar to the <token>*</token>, but
does <emphasis>not</emphasis> match zero occurrences.</para>
<para><programlisting># GNU versions of sed and awk can use "+",
# but it needs to be escaped.
echo a111b | sed -ne '/a1\+b/p'
echo a111b | grep 'a1\+b'
echo a111b | gawk '/a1+b/'
# All of above are equivalent.
# Thanks, S.C.</programlisting></para>
</listitem>
<listitem>
<indexterm>
<primary>\{ \}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>\{ \}</secondary>
</indexterm>
<para><link linkend="escp">Escaped</link> <quote>curly
brackets</quote> <token>\{ \}</token> indicate the number
of occurrences of a preceding RE to match.</para>
<para>It is necessary to escape the curly brackets since
they have only their literal character meaning
otherwise. This usage is technically not part of the basic
RE set.</para>
<para><quote>[0-9]\{5\}</quote> matches exactly five digits
(characters in the range of 0 to 9).</para>
<caution>
<para>Curly brackets are not available as an RE in the
<quote>classic</quote> version of <link
linkend="awkref">awk</link>. However,
<command>gawk</command> has the
<option>--re-interval</option> option that permits them
(without being escaped).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo 2222 | gawk --re-interval '/2{3}/'</userinput>
<computeroutput>2222</computeroutput>
</screen>
</para>
</caution>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem override="square">
<formalpara><title>POSIX Character Classes</title>
<para><userinput>[:class:]</userinput></para></formalpara>
<indexterm>
<primary>[:</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>:]</secondary>
</indexterm>
<para>This is an alternate method of specifying a range of
characters to match.</para>
</listitem>
<listitem>
<indexterm>
<primary>alnum</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>alphabetic numeric</secondary>
</indexterm>
<para><userinput>[:alnum:]</userinput> matches alphabetic or
numeric characters. This is equivalent to
<userinput>[A-Za-z0-9]</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>alpha</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>alphabetic</secondary>
</indexterm>
<para><userinput>[:alpha:]</userinput> matches alphabetic
characters. This is equivalent to
<userinput>[A-Za-z]</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>blank</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>space tab</secondary>
</indexterm>
<para><userinput>[:blank:]</userinput> matches a space or a
tab.</para>
</listitem>
<listitem>
<indexterm>
<primary>cntrl</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>control</secondary>
</indexterm>
<para><userinput>[:cntrl:]</userinput> matches control
characters.</para>
</listitem>
<listitem>
<indexterm>
<primary>digit</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>decimal digit</secondary>
</indexterm>
<para><userinput>[:digit:]</userinput> matches (decimal)
digits. This is equivalent to
<userinput>[0-9]</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>graph</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>graph</secondary>
</indexterm>
<para><userinput>[:graph:]</userinput> (graphic printable
characters). Matches characters in the range of ASCII 33 -
126. This is the same as <userinput>[:print:]</userinput>,
below, but excluding the space character.</para>
</listitem>
<listitem>
<indexterm>
<primary>lower</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>lowercase</secondary>
</indexterm>
<para><userinput>[:lower:]</userinput> matches lowercase
alphabetic characters. This is equivalent to
<userinput>[a-z]</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>print</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>printable</secondary>
</indexterm>
<para><userinput>[:print:]</userinput> (printable
characters). Matches characters in the range of ASCII 32 -
126. This is the same as <userinput>[:graph:]</userinput>,
above, but adding the space character.</para>
</listitem>
<listitem>
<indexterm>
<primary>space</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>whitespace</secondary>
</indexterm>
<para><userinput>[:space:]</userinput> matches whitespace
characters (space and horizontal tab).</para>
</listitem>
<listitem>
<indexterm>
<primary>upper</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>uppercase</secondary>
</indexterm>
<para><userinput>[:upper:]</userinput> matches uppercase
alphabetic characters. This is equivalent to
<userinput>[A-Z]</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>xdigit</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>hexadecimal</secondary>
</indexterm>
<para><userinput>[:xdigit:]</userinput> matches hexadecimal
digits. This is equivalent to
<userinput>[0-9A-Fa-f]</userinput>.</para>
<important>
<para>POSIX character classes generally require quoting
or <link linkend="dblbrackets">double brackets</link>
([[ ]]).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep [[:digit:]] test.file</userinput>
<computeroutput>abc=723</computeroutput>
</screen>
</para>
<para>These character classes may even be used with <link
linkend="globbingref">globbing</link>, to a limited
extent.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ls -l ?[[:digit:]][[:digit:]]?</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 21 14:47 a33b</computeroutput>
</screen>
</para>
<para>To see POSIX character classes used in scripts, refer to
<xref linkend="ex49"> and <xref linkend="lowercase">.</para>
</important>
</listitem>
</itemizedlist>
<para><link linkend="sedref">Sed</link>, <link
linkend="awkref">awk</link>, and <link
linkend="perlref">Perl</link>, used as filters in scripts, take
REs as arguments when "sifting" or transforming files or I/O
streams. See <xref linkend="behead"> and <xref linkend="tree">
for illustrations of this.</para>
<para>"Sed & Awk", by Dougherty and Robbins gives a very complete
and lucid treatment of REs (see the <xref
linkend="biblio">).</para>
</sect1> <!-- A Brief Introduction to Regular Expressions -->
<sect1 id="globbingref">
<title>Globbing</title>
<para>Bash itself cannot recognize Regular Expressions. In
scripts, commands and utilities, such as
<link linkend="sedref">sed</link> and <link
linkend="awkref">awk</link>, interpret RE's.</para>
<para>Bash does carry out filename expansion, a process known
as <quote>globbing</quote>, but this does
<emphasis>not</emphasis> use the standard RE set. Instead,
globbing recognizes and expands wildcards. Globbing interprets
the standard wildcard characters, <token>*</token> and
<token>?</token>, character lists in square brackets, and
certain other special characters (such as <token>^</token>
for negating the sense of a match). There are some important
limitations on wildcard characters in globbing, however.
Strings containing <replaceable>*</replaceable> will not
match filenames that start with a dot, as, for example,
<filename>.bashrc</filename>.
<footnote>
<para>
Filename expansion <emphasis>can</emphasis>
match dotfiles, but only if the pattern explicitly includes the dot.
<programlisting>~/[.]bashrc # Will not expand to ~/.bashrc
~/?bashrc # Neither will this.
# Wild cards and metacharacters will not expand to a dot in globbing.
~/.[b]ashrc # Will expand to ~./bashrc
~/.ba?hrc # Likewise.
~/.bashr* # Likewise.
# Setting the "dotglob" option turns this off.
# Thanks, S.C.</programlisting>
</para>
</footnote>
Likewise, the <replaceable>?</replaceable> has a different
meaning in globbing than as part of an RE.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 2
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l t?.sh</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [ab]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [a-c]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [^ab]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l {b*,c*,*est*}</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
</screen>
</para>
<para>See also <xref linkend="listglob">.</para>
</sect1> <!-- Globbing -->
</chapter> <!-- Regular Expressions -->
<chapter id="subshells">
<title>Subshells</title>
<para><anchor id="subshellsref"></para>
<para>Running a shell script launches another instance of the
command processor. Just as your commands are interpreted at the
command line prompt, similarly does a script batch process a list
of commands in a file. Each shell script running is, in effect,
a subprocess of the <link linkend="forkref">parent</link> shell,
the one that gives you the prompt at the console or in an xterm
window.</para>
<para>A shell script can also launch subprocesses. These
<emphasis>subshells</emphasis> let the script do
parallel processing, in effect executing multiple subtasks
simultaneously.</para>
<variablelist id="subshellparens">
<title><anchor id="subshellparens1">Command List in Parentheses</title>
<varlistentry>
<term>( command1; command2; command3; ... )</term>
<listitem>
<para>A command list embedded between
<replaceable>parentheses</replaceable> runs as a
subshell.</para>
</listitem>
</varlistentry>
</variablelist>
<note><para><anchor id="parvis">Variables in a subshell are
<emphasis>not</emphasis> visible outside the block of code
in the subshell. They are not accessible to the <link
linkend="forkref">parent process</link>, to the shell
that launched the subshell. These are, in effect, <link
linkend="localref">local variables</link>.</para></note>
<example id="subshell">
<title>Variable scope in a subshell</title>
<programlisting>&subshell;</programlisting>
</example>
<para>See also <xref linkend="subpit">.</para>
<para>+</para>
<para>Directory changes made in a subshell do not carry over to the
parent shell.</para>
<example id="allprofs">
<title>List User Profiles</title>
<programlisting>&allprofs;</programlisting>
</example>
<para>A subshell may be used to set up a <quote>dedicated
environment</quote> for a command group.
<programlisting>COMMAND1
COMMAND2
COMMAND3
(
IFS=:
PATH=/bin
unset TERMINFO
set -C
shift 5
COMMAND4
COMMAND5
exit 3 # Only exits the subshell.
)
# The parent shell has not been affected, and the environment is preserved.
COMMAND6
COMMAND7</programlisting>
One application of this is testing whether a variable is defined.
<programlisting>if (set -u; : $variable) 2&gt; /dev/null
then
echo "Variable is set."
fi
# Could also be written [[ ${variable-x} != x || ${variable-y} != y ]]
# or [[ ${variable-x} != x$variable ]]
# or [[ ${variable+x} = x ]])</programlisting>
Another application is checking for a lock file:
<programlisting>if (set -C; : &gt; lock_file) 2&gt; /dev/null
then
echo "Another user is already running that script."
exit 65
fi
# Thanks, S.C.</programlisting>
</para>
<para>Processes may execute in parallel within different
subshells. This permits breaking a complex task into subcomponents
processed concurrently.</para>
<example id="parallel-processes">
<title>Running parallel processes in subshells</title>
<programlisting>
(cat list1 list2 list3 | sort | uniq > list123) &
(cat list4 list5 list6 | sort | uniq > list456) &
# Merges and sorts both sets of lists simultaneously.
# Running in background ensures parallel execution.
#
# Same effect as
# cat list1 list2 list3 | sort | uniq > list123 &
# cat list4 list5 list6 | sort | uniq > list456 &
wait # Don't execute the next command until subshells finish.
diff list123 list456</programlisting>
</example>
<para>Redirecting I/O to a subshell uses the <quote>|</quote> pipe
operator, as in <userinput>ls -al | (command)</userinput>.</para>
<note><para>A command block between <replaceable>curly
braces</replaceable> does <emphasis>not</emphasis> launch
a subshell.</para>
<para>{ command1; command2; command3; ... }</para></note>
</chapter> <!-- Subshells -->
<chapter id="restricted-sh">
<title>Restricted Shells</title>
<variablelist id="disabledcommref0">
<title><anchor id="disabledcommref">Disabled commands in restricted
shells</title>
<varlistentry>
<term></term>
<listitem>
<formalpara><title></title>
<para>Running a script or portion of a script in
<emphasis>restricted</emphasis> mode disables certain commands that
would otherwise be available. This is a security measure intended
to limit the privileges of the script user and to minimize possible
damage from running the script.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<formalpara><title></title>
<para>Using <replaceable>cd</replaceable> to change the working
directory.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Changing the values of the <replaceable>$PATH</replaceable>,
<replaceable>$SHELL</replaceable>, <replaceable>$BASH_ENV</replaceable>,
or <replaceable>$ENV</replaceable> environmental variables.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Reading or changing the <replaceable>$SHELLOPTS</replaceable>,
shell environmental options.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Output redirection.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Invoking commands containing one or more
<token>/'s</token>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Invoking <emphasis>exec</emphasis> to substitute a different
process for the shell.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Various other commands that would enable monkeying
with or attempting to subvert the script for an unintended
purpose.</para>
</listitem>
</varlistentry>
<varlistentry>
<term></term>
<listitem>
<para>Getting out of restricted mode within the script.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="restricted">
<title>Running a script in restricted mode</title>
<programlisting>&restricted;</programlisting>
</example>
</chapter> <!-- Restricted Shells -->
<chapter id="process-sub">
<title>Process Substitution</title>
<para><anchor id="processsubref"><replaceable>Process
substitution</replaceable> is the counterpart to <link
linkend="commandsubref">command substitution</link>. Command
substitution sets a variable to the result of a command, as in
<command>dir_contents=`ls -al`</command> or <command>xref=$(
grep word datafile)</command>. Process substitution feeds the
output of a process to another process (in other words, it sends
the results of a command to another command).</para>
<variablelist id="commandsparens">
<title><anchor id="commandsparens1">Command substitution template</title>
<varlistentry>
<term>command within parentheses</term>
<listitem>
<para><command>&gt;(command)</command></para>
<para><command>&lt;(command)</command></para>
<para>These initiate process substitution. This uses
<filename>/dev/fd/&lt;n&gt;</filename> files to send the
results of the process within parentheses to another process.
<footnote><para>This has the same effect as a
<link linkend="namedpiperef">named pipe</link> (temp
file), and, in fact, named pipes were at one time used
in process substitution.</para></footnote>
</para>
<note><para>There is <emphasis>no</emphasis> space between the
the <quote><</quote> or <quote>></quote> and the parentheses.
Space there would give an error message.</para></note>
</listitem>
</varlistentry>
</variablelist>
<para>
<screen><prompt>bash$ </prompt><userinput>echo >(true)</userinput>
<computeroutput>/dev/fd/63</computeroutput>
<prompt>bash$ </prompt><userinput>echo <(true)</userinput>
<computeroutput>/dev/fd/63</computeroutput>
</screen>
Bash creates a pipe with two <link linkend="fdref">file
descriptors</link>, <filename>--fIn</filename> and
<filename>fOut--</filename>. The <filename>stdin</filename>
of <link linkend="trueref">true</link> connects
to <filename>fOut</filename> (dup2(fOut, 0)),
then Bash passes a <filename>/dev/fd/fIn</filename>
argument to <command>echo</command>. On systems lacking
<filename>/dev/fd/&lt;n&gt;</filename> files, Bash may use
temporary files. (Thanks, S.C.)</para>
<para><programlisting>cat <(ls -l)
# Same as ls -l | cat
sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin)
# Lists all the files in the 3 main 'bin' directories, and sorts by filename.
# Note that three (count 'em) distinct commands are fed to 'sort'.
diff <(command1) <(command2) # Gives difference in command output.
tar cf >(bzip2 -c > file.tar.bz2) dir
# Calls "tar cf /dev/fd/?? dir", and "bzip2 -c > file.tar.bz2".
#
# Because of the /dev/fd/&lt;n&gt; system feature,
# the pipe between both commands does not need to be named.
#
# This can be emulated.
#
bzip2 -c < pipe > file.tar.bz2&
tar cf pipe dir
rm pipe
# or
exec 3>&1
tar cf /dev/fd/4 dir 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&-
exec 3>&-
# Thanks, S.C.</programlisting></para>
<para>A reader of this document sent in the following interesting
example of process substitution.</para>
<para><programlisting># Script fragment taken from SuSE distribution:
while read des what mask iface; do
# Some commands ...
done < <(route -n)
# To test it, let's make it do something.
while read des what mask iface; do
echo $des $what $mask $iface
done < <(route -n)
# Output:
# Kernel IP routing table
# Destination Gateway Genmask Flags Metric Ref Use Iface
# 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
# As S.C. points out, an easier-to-understand equivalent is:
route -n |
while read des what mask iface; do # Variables set from output of pipe.
echo $des $what $mask $iface
done # Same output as above.</programlisting></para>
</chapter> <!-- Process Substitution -->
<chapter id="functions">
<title>Functions</title>
<para><anchor id="functionref"></para>
<para>Like <quote>real</quote> programming languages,
Bash has functions, though in a somewhat limited implementation.
A function is a subroutine, a <link linkend="codeblockref">code
block</link> that implements a set of operations, a <quote>black
box</quote> that performs a specified task. Wherever there is
repetitive code, when a task repeats with only slight variations,
then consider using a function.</para>
<para><cmdsynopsis>
<command>function</command>
<arg choice="plain"><replaceable>function_name</replaceable></arg>
<arg choice="plain">{</arg><sbr>
<arg rep="repeat" choice="plain"><replaceable>command</replaceable></arg><sbr>
<arg choice="plain">}</arg><sbr>
</cmdsynopsis>
or
<cmdsynopsis>
<arg choice="plain"><replaceable>function_name</replaceable></arg>
<arg choice="plain">()</arg>
<arg choice="plain">{</arg><sbr>
<arg rep="repeat" choice="plain"><replaceable>command</replaceable></arg><sbr>
<arg choice="plain">}</arg><sbr>
</cmdsynopsis>
</para>
<para>This second form will cheer the hearts of C programmers
(and is more portable).</para>
<para>As in C, the function's opening bracket may optionally appear
on the second line.</para>
<para><cmdsynopsis>
<arg choice="plain"><replaceable>function_name</replaceable></arg>
<arg choice="plain">()</arg><sbr>
<arg choice="plain">{</arg><sbr>
<arg rep="repeat" choice="plain"><replaceable>command</replaceable></arg><sbr>
<arg choice="plain">}</arg><sbr>
</cmdsynopsis>
</para>
<para>Functions are called, <firstterm>triggered</firstterm>, simply by
invoking their names.</para>
<example id="ex59">
<title>Simple function</title>
<programlisting>&ex59;</programlisting>
</example>
<para>The function definition must precede the first call to
it. There is no method of <quote>declaring</quote> the function,
as, for example, in C.
<programlisting># f1
# Will give an error message, since function "f1" not yet defined.
# However...
f1 ()
{
echo "Calling function \"f2\" from within function \"f1\"."
f2
}
f2 ()
{
echo "Function \"f2\"."
}
f1 # Function "f2" is not actually called until this point,
# although it is referenced before its definition.
# This is permissable.
# Thanks, S.C.</programlisting>
</para>
<para>It is even possible to nest a function within another function,
although this is not very useful.
<programlisting>f1 ()
{
f2 () # nested
{
echo "Function \"f2\", inside \"f1\"."
}
}
# f2
# Gives an error message.
f1 # Does nothing, since calling "f1" does not automatically call "f2".
f2 # Now, it's all right to call "f2",
# since its definition has been made visible by calling "f1".
# Thanks, S.C.</programlisting>
</para>
<para>Function declarations can appear in unlikely places, even where a
command would otherwise go.
<programlisting>ls -l | foo() { echo "foo"; } # Permissable, but useless.
if [ "$USER" = bozo ]
then
bozo_greet () # Function definition embedded in an if/then construct.
{
echo "Hello, Bozo."
}
fi
bozo_greet # Works only for Bozo, and other users get an error.
# Something like this might be useful in some contexts.
NO_EXIT=1 # Will enable function definition below.
[[ $NO_EXIT -eq 1 ]] && exit() { true; } # Function definition in an "and-list".
# If $NO_EXIT is 1, declares "exit ()".
# This disables the "exit" builtin by aliasing it to "true".
exit # Invokes "exit ()" function, not "exit" builtin.
# Thanks, S.C.</programlisting>
</para>
<sect1 id="complexfunct">
<title>Complex Functions and Function Complexities</title>
<para>Functions may process arguments passed to them and return
an <link linkend="exitstatusref">exit status</link> to the script
for further processing.</para>
<programlisting>function_name $arg1 $arg2</programlisting>
<para>The function refers to the passed arguments by position (as if they were
<link linkend="posparamref">positional parameters</link>),
that is, <varname>$1</varname>, <varname>$2</varname>, and
so forth.</para>
<example id="ex60">
<title>Function Taking Parameters</title>
<programlisting>&ex60;</programlisting>
</example>
<note><para>In contrast to certain other programming languages,
shell scripts normally pass only value parameters to functions.
<footnote><para><link linkend="ivrref">Indirect variable
references</link> (see <xref linkend="ex78">) provide a clumsy
sort of mechanism for passing variable pointers to functions.
<programlisting>&refparams;</programlisting>
</para></footnote>
Variable names (which are actually pointers), if passed as
parameters to functions, will be treated as string literals
and cannot be dereferenced. <emphasis>Functions interpret their
arguments literally.</emphasis></para></note>
<variablelist id="exitreturn">
<title><anchor id="exitreturn1">Exit and Return</title>
<varlistentry>
<term><command>exit status</command></term>
<listitem>
<para>Functions return a value, called an <firstterm>exit
status</firstterm>. The exit status may be explicitly
specified by a <command>return</command> statement,
otherwise it is the exit status of the last command
in the function (<returnvalue>0</returnvalue> if
successful, and a non-zero error code if not). This
<link linkend="exitstatusref">exit status</link>
may be used in the script by referencing it as
<link linkend="xstatvarref">$?</link>. This mechanism
effectively permits script functions to have a <quote>return
value</quote> similar to C functions.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>return</command></term>
<indexterm>
<primary>return</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>return</secondary>
</indexterm>
<listitem>
<para>Terminates a function. A <command>return</command> command
<footnote><para>The <command>return</command> command is a
Bash <link linkend="builtinref">builtin</link>.</para></footnote>
optionally takes an <emphasis>integer</emphasis>
argument, which is returned to the calling script as
the <quote>exit status</quote> of the function, and
this exit status is assigned to the variable <link
linkend="xstatvarref">$?</link>.</para>
<example id="max">
<title>Maximum of two numbers</title>
<programlisting>&max;</programlisting>
</example>
<tip>
<para>For a function to return a string or array, use a
dedicated variable.
<programlisting>count_lines_in_etc_passwd()
{
[[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd))
# If /etc/passwd is readable, set REPLY to line count.
# Returns both a parameter value and status information.
}
if count_lines_in_etc_passwd
then
echo "There are $REPLY lines in /etc/passwd."
else
echo "Cannot count lines in /etc/passwd."
fi
# Thanks, S.C.</programlisting>
</para>
</tip>
<example id="ex61">
<title>Converting numbers to Roman numerals</title>
<programlisting>&ex61;</programlisting>
</example>
<para>See also <xref linkend="isalpha">.</para>
<important>
<para>The largest positive integer a function can return is
256. The <command>return</command> command is closely
tied to the concept of <link linkend="exitstatusref">exit
status</link>, which accounts for this particular
limitation. Fortunately, there are workarounds for those
situations requiring a large integer return value from
a function.</para>
<example id="returntest">
<title>Testing large return values in a function</title>
<programlisting>&returntest;</programlisting>
</example>
<para>As we have seen, a function can return a large negative
value. This also permits returning large positive integer,
using a bit of trickery.</para>
<example id="max2">
<title>Comparing two large integers</title>
<programlisting>&max2;</programlisting>
</example>
<para>See also <xref linkend="daysbetween">.</para>
<para><userinput>Exercise for the reader:</userinput> Using
what we have just learned, extend the previous <link
linkend="ex61">Roman numerals example</link> to accept
arbitrarily large input.</para>
</important>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="redstdinfunc">
<title><anchor id="redstdinfunc1">Redirection</title>
<varlistentry>
<term><replaceable>Redirecting the stdin of a
function</replaceable></term>
<indexterm>
<primary>redirection</primary>
<secondary>stdin</secondary>
</indexterm>
<listitem>
<para>A function is essentially a <link
linkend="codeblockref">code block</link>, which means its
<filename>stdin</filename> can be redirected (as in <xref
linkend="ex8">).</para>
<example id="realname">
<title>Real name from username</title>
<programlisting>&realname;</programlisting>
</example>
<para>There is an alternative, and perhaps less confusing
method of redirecting a function's
<filename>stdin</filename>. This involves redirecting the
<filename>stdin</filename> to an embedded bracketed code
block within the function.
<programlisting># Instead of:
Function ()
{
...
} < file
# Try this:
Function ()
{
{
...
} < file
}
# Similarly,
Function () # This works.
{
{
echo $*
} | tr a b
}
Function () # This doesn't work.
{
echo $*
} | tr a b # A nested code block is mandatory here.
# Thanks, S.C.</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Complex Functions and Function Complexities -->
<sect1 id="localvar">
<title>Local Variables and Recursion</title>
<variablelist id="localref">
<title><anchor id="localref1">Local variables make recursion
possible.</title>
<varlistentry>
<term>local variables</term>
<indexterm>
<primary>variable</primary>
<secondary>local</secondary>
</indexterm>
<listitem>
<para>A variable declared as <firstterm>local</firstterm>
is one that is visible only within the <link
linkend="codeblockref">block of code</link> in which it
appears. It has local <quote>scope</quote>. In
a function, a <emphasis>local variable</emphasis> has
meaning only within that function block.</para>
<example id="ex62">
<title>Local variable visibility</title>
<programlisting>&ex62;</programlisting>
</example>
<para>Local variables permit recursion (a recursive function
is one that calls itself), but this practice generally
involves much computational overhead and is definitely
<emphasis>not</emphasis> recommended in a shell script.
<footnote><para>Too many levels of recursion may crash a
script with a segfault.
<programlisting>#!/bin/bash
recursive_function ()
{
(( $1 < $2 )) && f $(( $1 + 1 )) $2;
}
recursive_function 1 50000 # Segfaults.
# Recursion this deep might cause even a C program to segfault,
# by using up all the memory allotted to the stack.
# Thanks, S.C.
exit 0 # This script will not exit normally.</programlisting>
</para></footnote>
</para>
<example id="ex63">
<title>Recursion, using a local variable</title>
<programlisting>&ex63;</programlisting>
</example>
<para>See also <xref linkend="primes"> for an example of
recursion in a script. Be aware that recursion is
resource-intensive and executes slowly, especially in
a script.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Local Variables and Recursion -->
</chapter> <!-- Functions -->
<chapter id="aliases">
<title>Aliases</title>
<para><anchor id="aliasref"></para>
<indexterm>
<primary>alias</primary>
</indexterm>
<para>A Bash <emphasis>alias</emphasis> is essentially nothing more than
a keyboard shortcut, an abbreviation, a means of avoiding
typing a long command sequence. If, for example, we include
<command>alias lm="ls -l | more"</command> in the <link
linkend="filesref"><filename>~/.bashrc</filename> file</link>,
then each <userinput>lm</userinput> typed at the command
line will automatically be replaced by a <command>ls -l |
more</command>. This can save a great deal of typing at the
command line and avoid having to remember complex combinations of
commands and options. Setting <command>alias rm="rm -i"</command>
(interactive mode delete) may save a good deal of grief, since
it can prevent inadvertently losing important files.</para>
<para>In a script, aliases have very limited usefulness. It would be
quite nice if aliases could assume some of the functionality of
the C preprocessor, such as macro expansion, but unfortunately
Bash does not expand arguments within the alias body.
<footnote><para>However, aliases do seem to expand positional
parameters.</para></footnote>
Moreover, a script fails to expand an alias itself
within <quote>compound constructs</quote>, such as <link
linkend="ifthen">if/then</link> statements, loops, and
functions. An added limitation is that an alias will not expand
recursively. Almost invariably, whatever we would like an alias
to do could be accomplished much more effectively with a <link
linkend="functionref">function</link>.</para>
<example id="al">
<title>Aliases within a script</title>
<programlisting>&al;</programlisting>
</example>
<note><para>The <command>unalias</command> command removes a previously
set <emphasis>alias</emphasis>.</para></note>
<example id="unal">
<title><command>unalias</command>: Setting and unsetting an alias</title>
<programlisting>&unal;</programlisting>
</example>
<screen><prompt>bash$ </prompt><userinput>./unalias.sh</userinput>
<computeroutput>
total 6
drwxrwxr-x 2 bozo bozo 3072 Feb 6 14:04 .
drwxr-xr-x 40 bozo bozo 2048 Feb 6 14:04 ..
-rwxr-xr-x 1 bozo bozo 199 Feb 6 14:04 unalias.sh
./unalias.sh: llm: command not found
</computeroutput></screen>
</chapter> <!-- Aliases -->
<chapter id="list-cons">
<title>List Constructs</title>
<para><anchor id="listconsref"></para>
<indexterm>
<primary>&&</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&&</secondary>
</indexterm>
<indexterm>
<primary>AND</primary>
<secondary>list</secondary>
</indexterm>
<indexterm>
<primary>||</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>||</secondary>
</indexterm>
<indexterm>
<primary>OR</primary>
<secondary>list</secondary>
</indexterm>
<para>The <quote>and list</quote> and <quote>or list</quote>
constructs provide a means of processing a number of commands
consecutively. These can effectively replace complex
nested <command>if</command>/<command>then</command> or even
<command>case</command> statements.</para>
<variablelist id="lcons">
<title><anchor id="lcons1">Chaining together commands</title>
<varlistentry>
<term>and list</term>
<listitem>
<para><programlisting>command-1 && command-2 && command-3 && ... command-n</programlisting>
Each command executes in turn provided that
the previous command has given a return value of
<returnvalue>true</returnvalue> (zero). At the first
<returnvalue>false</returnvalue> (non-zero) return, the
command chain terminates (the first command returning
<returnvalue>false</returnvalue> is the last one to
execute).</para>
<example id="ex64">
<title>Using an <quote>and list</quote> to test for command-line arguments</title>
<programlisting>&ex64;</programlisting>
</example>
<example id="andlist2">
<title>Another command-line arg test using an <quote>and list</quote></title>
<programlisting>&andlist2;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term>or list</term>
<listitem>
<para><programlisting>command-1 || command-2 || command-3 || ... command-n</programlisting>
Each command executes in turn for as long as the previous
command returns <returnvalue>false</returnvalue>. At
the first <returnvalue>true</returnvalue> return, the
command chain terminates (the first command returning
<returnvalue>true</returnvalue> is the last one to
execute). This is obviously the inverse of the <quote>and
list</quote>.</para>
<example id="ex65">
<title>Using <quote>or lists</quote> in combination with an <quote>and list</quote></title>
<programlisting>&ex65;</programlisting>
</example>
<caution><para>If the first command in an <quote>or list</quote>
returns <returnvalue>true</returnvalue>, it
<replaceable>will</replaceable> execute.</para></caution>
</listitem>
</varlistentry>
</variablelist>
<important><para>The <link linkend="exitstatusref">exit
status</link> of an <userinput>and list</userinput> or an
<userinput>or list</userinput> is the exit status of the last
command executed.</para></important>
<para>Clever combinations of <quote>and</quote> and <quote>or</quote>
lists are possible, but the logic may easily become convoluted and
require extensive debugging.
<programlisting>false && true || echo false # false
# Same result as
( false && true ) || echo false # false
# But *not*
false && ( true || echo false ) # (nothing echoed)
# Note left-to-right grouping and evaluation of statements,
# since the logic operators "&&" and "||" have equal precedence.
# It's best to avoid such complexities, unless you know what you're doing.
# Thanks, S.C.</programlisting>
</para>
<para>See <xref linkend="daysbetween"> for an illustration of using
an <userinput>and / or list</userinput> to test variables.</para>
</chapter> <!-- List Constructs -->
<chapter id="arrays">
<title>Arrays</title>
<para><anchor id="arrayref"></para>
<para>Newer versions of <command>bash</command> support one-dimensional
arrays. Arrays may be declared with the <userinput>variable[xx]</userinput>
notation or explicitly by a <userinput>declare -a variable</userinput>
statement. To dereference (find the contents of) an array variable, use
<firstterm>curly bracket</firstterm> notation, that is, <userinput>${variable[xx]}</userinput>.</para>
<example id="ex66">
<title>Simple array usage</title>
<programlisting>&ex66;</programlisting>
</example>
<para>Arrays variables have a syntax all their own, and even
standard Bash commands have special options adapted for array
use.</para>
<example id="ex67">
<title>Some special properties of arrays</title>
<programlisting>&ex67;</programlisting>
</example>
<para>As seen in the previous example, either
<command>${array_name[@]}</command> or
<command>${array_name[*]}</command> refers to
<emphasis>all</emphasis> the elements of the array.
Similarly, to get a count of the number of elements in an
array, use either <command>${#array_name[@]}</command>
or <command>${#array_name[*]}</command>.
<command>${#array_name}</command> is the length (number of
characters) of <command>${array_name[0]}</command>, the first
element of the array.</para>
<example id="emptyarray">
<title>Of empty arrays and empty elements</title>
<programlisting>&emptyarray;</programlisting>
</example>
<para>The relationship of <command>${array_name[@]}</command>
and <command>${array_name[*]}</command> is analogous to that
between <link linkend="appref">$@ and $*</link>. This powerful
array notation has a number of uses.</para>
<para>
<programlisting># Copying an array.
array2=( "${array1[@]}" )
# Adding an element to an array.
array=( "${array[@]}" "new element" )
# or
array[${#array[*]}]="new element"
# Thanks, S.C.</programlisting>
</para>
<para>--</para>
<para>Arrays permit deploying old familiar algorithms as shell scripts.
Whether this is necessarily a good idea is left to the reader to
decide.</para>
<example id="bubble">
<title>An old friend:
<emphasis>The Bubble Sort</emphasis></title>
<programlisting>&bubble;</programlisting>
</example>
<para>--</para>
<para>Arrays enable implementing a shell script version of the <emphasis>Sieve of
Erastosthenes</emphasis>. Of course, a resource-intensive application of this
nature should really be written in a compiled language, such as C. It
runs excruciatingly slowly as a script.</para>
<example id="ex68">
<title>Complex array application:
<emphasis>Sieve of Erastosthenes</emphasis></title>
<programlisting>&ex68;</programlisting>
</example>
<para>Compare this array-based prime number generator with with an
alternative that does not use arrays, <xref
linkend="primes">.</para>
<para>--</para>
<para>Fancy manipulation of array <quote>subscripts</quote> may require
intermediate variables. For projects involving this, again consider
using a more powerful programming language, such as Perl or C.</para>
<example id="qfunction">
<title>Complex array application:
<emphasis>Exploring a weird mathematical series</emphasis></title>
<programlisting>&qfunction;</programlisting>
</example>
<para>--</para>
<para>Bash supports only one-dimensional arrays, however a little
trickery permits simulating multi-dimensional ones.</para>
<example id="twodim">
<title>Simulating a two-dimensional array, then tilting it</title>
<programlisting>&twodim;</programlisting>
</example>
</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 environmental variables
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 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">
<title>/dev and /proc</title>
<para><anchor id="devprocref"></para>
<para>A Linux or UNIX machine typically has two special-purpose
directories, <filename class="directory">/dev</filename> and
<filename class="directory">/proc</filename>. The <filename
class="directory">/dev</filename> directory contains entries
for the physical <emphasis>devices</emphasis> that may or may
not be present in the hardware.
<footnote>
<para>The entries in <filename class="directory">/dev</filename>
provide mount points for physical and virtual devices. These
entries use very little drive space.</para>
<para>Some devices, such as
<filename>/dev/null</filename>, <filename>/dev/zero</filename>,
and <filename>/dev/urandom</filename> are virtual. They
are not actual physical devices and exist only in
software.</para>
</footnote>
The <filename class="directory">/proc</filename> directory
is actually a pseudo-filesystem. The files in the <filename
class="directory">/proc</filename> directory mirror currently
running system and kernel <emphasis>processes</emphasis> and
contain information and statistics about them.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cat /proc/devices</userinput>
<computeroutput>Character devices:
1 mem
2 pty
3 ttyp
4 ttyS
5 cua
7 vcs
10 misc
14 sound
29 fb
36 netlink
128 ptm
136 pts
162 raw
254 pcmcia
Block devices:
1 ramdisk
2 fd
3 ide0
9 md</computeroutput>
<prompt>bash$ </prompt><userinput>cat /dev/sndstat | grep Audio</userinput>
<computeroutput>Audio devices:
0: ESS ES1688 AudioDrive (rev 11) (3.01)</computeroutput>
</screen>
</para>
<note><para>As of the 2.3 (and 2.4) kernel,
<filename>/dev/sndstat</filename> has been deprecated.</para></note>
<para><anchor id="loopbackref">The <filename
class="directory">/dev</filename> directory
contains <emphasis>loopback</emphasis> devices, such as
<filename>/dev/loop0</filename>. A loopback device is a gimmick
allows an ordinary file to be accessed as if it were a block
device.
<footnote><para>A <emphasis>block device</emphasis> reads and/or
writes data in chunks, or blocks, in contrast to a
<emphasis>character device</emphasis>, which acesses data
in character units. Examples of block devices are a hard
drive and CD ROM drive. An example of a character device is
a keyboard.</para></footnote>
This enables mounting an entire filesystem within a
single large file. See <xref linkend="createfs"> and <xref
linkend="isomountref">.</para>
<para>Shell scripts may extract data from certain of the files in
<filename class="directory">/dev</filename> and <filename
class="directory">/proc</filename>.
<footnote><para>Certain system commands, such as
<link linkend="procinforef">procinfo</link>,
<link linkend="freeref">free</link>,
<link linkend="vmstatref">vmstat</link>,
and <link linkend="uptimeref">uptime</link>
do this as well.</para></footnote></para>
<para><programlisting>kernel_version=$( awk '{ print $3 }' /proc/version )</programlisting></para>
<para><programlisting>CPU=$( awk '/model name/ {print $4}' < /proc/cpuinfo )
if [ $CPU = Pentium ]
then
run_some_commands
...
else
run_different_commands
...
fi</programlisting></para>
<para>A few of the pseudo-devices in <filename
class="directory">/dev</filename> have
other specialized uses, such as <link
linkend="zerosref"><filename>/dev/zero</filename></link>.</para>
<para>The <filename class="directory">/proc</filename> directory
contains subdirectories with unusual numerical
names. Every one of these names maps to the <link
linkend="ppidref">process ID</link> of a currently running
process. Within each of these subdirectories, there are
a number of files that hold useful information about the
corresponding process. The <filename>stat</filename> and
<filename>status</filename> files keep running statistics
on the process, the <filename>cmdline</filename> file holds
the command-line arguments the process was invoked with, and
the <filename>exe</filename> file is a symbolic link to the
complete path name of the invoking process. There are a few
more such files, but these seem to be the most interesting
from a scripting standpoint.</para>
<example id="pidid">
<title>Finding the process associated with a PID</title>
<programlisting>&pidid;</programlisting>
</example>
<example id="constat">
<title>On-line connect status</title>
<programlisting>&constat;</programlisting>
</example>
<warning><para>In general, it is dangerous to
<emphasis>write</emphasis> to the files in <filename
class="directory">/proc</filename>, as this can corrupt the
filesystem or crash the machine.</para></warning>
</chapter> <!-- /dev and /proc -->
<chapter id="zeros">
<title>Of Zeros and Nulls</title>
<para><anchor id="zerosref"></para>
<variablelist id="zeronull">
<title><anchor id="zeronull1"><filename>/dev/zero</filename>
and <filename>/dev/null</filename></title>
<varlistentry>
<term>Uses of <filename>/dev/null</filename></term>
<listitem>
<para>Think of <filename>/dev/null</filename> as a <quote>black
hole</quote>. It is the nearest equivalent to a
write-only file. Everything written to it disappears
forever. Attempts to read or output from it result in
nothing. Nevertheless, <filename>/dev/null</filename>
can be quite useful from both the command line and in
scripts.</para>
<para>Suppressing <filename>stdout</filename> or
<filename>stderr</filename> (from <xref linkend="ex57">):
<programlisting>rm $badname 2>/dev/null
# So error messages [stderr] deep-sixed.</programlisting>
</para>
<para>Deleting contents of a file, but preserving the file itself, with
all attendant permissions (from <xref linkend="ex1"> and <xref linkend="ex2">):
<programlisting>cat /dev/null > /var/log/messages
# : > /var/log/messages has same effect, but does not spawn a new process.
cat /dev/null > /var/log/wtmp</programlisting>
</para>
<para>Automatically emptying the contents of a log file
(especially good for dealing with those nasty
<quote>cookies</quote> sent by Web commercial sites):</para>
<example id="cookies">
<title>Hiding the cookie jar</title>
<programlisting>if [ -f ~/.netscape/cookies ] # Remove, if exists.
then
rm -f ~/.netscape/cookies
fi
ln -s /dev/null ~/.netscape/cookies
# All cookies now get sent to a black hole, rather than saved to disk.</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term>Uses of <filename>/dev/zero</filename></term>
<listitem>
<para>Like <filename>/dev/null</filename>,
<filename>/dev/zero</filename> is a pseudo file,
but it actually contains nulls (numerical zeros,
not the ASCII kind). Output written to it disappears,
and it is fairly difficult to actually read the nulls in
<filename>/dev/zero</filename>, though it can be done with
<link linkend="odref">od</link> or a hex editor. The chief
use for <filename>/dev/zero</filename> is in creating an
initialized dummy file of specified length intended as a
temporary swap file.</para>
<example id="ex73">
<title>Setting up a swapfile using <filename>/dev/zero</filename></title>
<programlisting>&ex73;</programlisting>
</example>
<para>Another application of <filename>/dev/zero</filename>
is to <quote>zero out</quote> a file of a designated
size for a special purpose, such as mounting a filesystem
on a <link linkend="loopbackref">loopback device</link>
(see <xref linkend="createfs">) or securely deleting a file
(see <xref linkend="blotout">).</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Zeros and Nulls -->
<chapter id="debugging">
<title>Debugging</title>
<para>The Bash shell contains no debugger, nor even any
debugging-specific commands or constructs. Syntax errors or
outright typos in the script generate cryptic error messages that
are often of no help in debugging a non-functional script.</para>
<example id="ex74">
<title>test23, a buggy script</title>
<programlisting>&ex74;</programlisting>
</example>
<para>Output from script:
<screen><computeroutput>./test23: [37: command not found</computeroutput></screen>
</para>
<para>What's wrong with the above script (hint: after the <command>if</command>)?</para>
<para>What if the script executes, but does not work as expected? This is the
all too familiar logic error.</para>
<example id="ex75">
<title>test24, another buggy script</title>
<programlisting>&ex75;</programlisting>
</example>
<para>Try to find out what's wrong with <xref linkend="ex75">
by uncommenting the <userinput>echo "$badname"</userinput> line. Echo
statements are useful for seeing whether what you expect is
actually what you get.</para>
<para>In this particular case, <userinput>rm "$badname"</userinput>
will not give the desired results because
<varname>$badname</varname> should not be quoted. Placing it
in quotes ensures that <command>rm</command> has only one
argument (it will match only one filename). A partial fix
is to remove to quotes from <varname>$badname</varname> and
to reset <varname>$IFS</varname> to contain only a newline,
<userinput>IFS=$'\n'</userinput>. However, there are simpler
ways of going about it.
<programlisting># Correct methods of deleting filenames containing spaces.
rm *\ *
rm *" "*
rm *' '*
# Thank you. S.C.</programlisting>
</para>
<para>Summarizing the symptoms of a buggy script,
<orderedlist>
<listitem>
<para>It bombs with an error message <errorname>syntax error</errorname>, or</para>
</listitem>
<listitem>
<para>It runs, but does not work as expected
(<errorname>logic error</errorname>)</para>
</listitem>
<listitem>
<para>It runs, works as expected, but has nasty side effects
(<errorname>logic bomb</errorname>).</para>
</listitem>
</orderedlist>
</para>
<para>Tools for debugging non-working scripts include
<orderedlist>
<listitem>
<para>echo statements at critical points in the script to trace the variables,
and otherwise give a snapshot of what is going on.</para>
</listitem>
<listitem>
<para>using the <command>tee</command> filter to check processes or
data flows at critical points.</para>
</listitem>
<listitem>
<para>setting option flags <option>-n -v -x</option></para>
<para><userinput>sh -n scriptname</userinput> checks for
syntax errors without actually running the script. This is
the equivalent of inserting <userinput>set -n</userinput> or
<userinput>set -o noexec</userinput> into the script. Note
that certain types of syntax errors can slip past this
check.</para>
<para><userinput>sh -v scriptname</userinput> echoes each
command before executing it. This is the equivalent of
inserting <userinput>set -v</userinput> or <userinput>set
-o verbose</userinput> in the script.</para>
<para>The <option>-n</option> and <option>-v</option>
flags work well together. <userinput>sh -nv
scriptname</userinput> gives a verbose syntax check.</para>
<para><userinput>sh -x scriptname</userinput> echoes the result each
command, but in an abbreviated manner. This is the equivalent of
inserting <userinput>set -x</userinput> or
<userinput>set -o xtrace</userinput> in the script.</para>
<para>Inserting <userinput>set -u</userinput> or
<userinput>set -o nounset</userinput> in the script runs it, but
gives an <errorname>unbound variable</errorname> error message
at each attempt to use an undeclared variable.</para>
</listitem>
<listitem>
<para>trapping at exit.</para>
<para>The <command>exit</command> command in a script triggers a
signal <returnvalue>0</returnvalue>, terminating the
process, that is, the script itself.
<footnote><para>By convention, <replaceable>signal
0</replaceable> is assigned to <link
linkend="exitcommandref">exit</link>. </para></footnote>
It is often useful to trap the
<command>exit</command>, forcing a <quote>printout</quote>
of variables, for example. The <command>trap</command>
must be the first command in the script.</para>
</listitem>
</orderedlist>
</para>
<variablelist id="trapref">
<title><anchor id="trapref1">Trapping signals</title>
<varlistentry>
<term><command>trap</command></term>
<listitem>
<para>Specifies an action on receipt of a signal; also
useful for debugging.
<note><para><anchor id="signald">A
<emphasis>signal</emphasis> is simply a message
sent to a process, either by the kernel or another
process, telling it to take some specified action
(usually to terminate). For example, hitting a
<keycombo><keycap>Control</keycap><keycap>C</keycap></keycombo>,
sends a user interrupt, an INT signal, to a running
program.</para></note>
<programlisting>trap '' 2
# Ignore interrupt 2 (Control-C), with no action specified.
trap 'echo "Control-C disabled."' 2
# Message when Control-C pressed.</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex76">
<title>Trapping at exit</title>
<programlisting>&ex76;</programlisting>
</example>
<example id="online">
<title>Cleaning up after Control-C</title>
<programlisting>&online;</programlisting>
</example>
<note>
<para>The <option>DEBUG</option> argument to
<command>trap</command> causes a specified action to execute
after every command in a script. This permits tracing variables,
for example.
<example id="vartrace">
<title>Tracing a variable</title>
<programlisting>&vartrace;</programlisting>
</example>
</para>
</note>
<note><para><userinput>trap '' SIGNAL</userinput> (two adjacent
apostrophes) disables SIGNAL for the remainder of the
script. <userinput>trap SIGNAL</userinput> restores
the functioning of SIGNAL once more. This is useful to
protect a critical portion of a script from an undesirable
interrupt.</para></note>
<para><programlisting>
trap '' 2 # Signal 2 is Control-C, now disabled.
command
command
command
trap 2 # Reenables Control-C
</programlisting></para>
</chapter> <!-- Debugging -->
<chapter id="options">
<title>Options</title>
<para><anchor id="optionsref"></para>
<para>Options are settings that change shell and/or script
behavior.</para>
<para>The <link linkend="setref">set</link> command
enables options within a script. At the point in the script
where you want the options to take effect, use <command>set
-o option-name</command> or, in short form, <command>set
-option-abbrev</command>. These two forms are equivalent.</para>
<para><programlisting>
#!/bin/bash
set -o verbose
# Echoes all commands before executing.
</programlisting></para>
<para><programlisting>
#!/bin/bash
set -v
# Exact same effect as above.
</programlisting></para>
<note><para>To <emphasis>disable</emphasis> an option within a script,
use <command>set +o option-name</command> or <command>set
+option-abbrev</command>.</para></note>
<para><programlisting>
#!/bin/bash
set -o verbose
# Command echoing on.
command
...
command
set +o verbose
# Command echoing off.
command
# Not echoed.
set -v
# Command echoing on.
command
...
command
set +v
# Command echoing off.
command
exit 0
</programlisting></para>
<para>An alternate method of enabling options in a script is
to specify them immediately following the
<replaceable>#!</replaceable> script header.</para>
<para><programlisting>
#!/bin/bash -x
#
# Body of script follows.
</programlisting></para>
<para>It is also possible to enable script options from the command
line. Some options that will not work with
<command>set</command> are available this way. Among these
are <replaceable>-i</replaceable>, force script to run
interactive.</para>
<para><userinput>bash -v script-name</userinput></para>
<para><userinput>bash -o verbose script-name</userinput></para>
<para>The following is a listing of some useful options. They may be
specified in either abbreviated form or by complete name.</para>
<table>
<title>bash options</title>
<tgroup cols="3">
<thead>
<row>
<entry>Abbreviation</entry>
<entry>Name</entry>
<entry>Effect</entry>
</row>
</thead>
<tbody>
<row>
<entry><anchor id="noclobberref"><option>-C</option></entry>
<entry>noclobber</entry>
<entry>Prevent overwriting of files by redirection (may be
overridden by <token>>|</token>)</entry>
</row>
<row>
<entry><option>-D</option></entry>
<entry>(none)</entry>
<entry>List double-quoted strings prefixed by <token>$</token>,
but do not execute commands in script</entry>
</row>
<row>
<entry><option>-a</option></entry>
<entry>allexport</entry>
<entry>Export all defined variables</entry>
</row>
<row>
<entry><option>-b</option></entry>
<entry>notify</entry>
<entry>Notify when jobs running in background terminate (not of
much use in a script)</entry>
</row>
<row>
<entry><option>-c ...</option></entry>
<entry>(none)</entry>
<entry>Read commands from <command>...</command></entry>
</row>
<row>
<entry><option>-f</option></entry>
<entry>noglob</entry>
<entry>Filename expansion (globbing) disabled</entry>
</row>
<row>
<entry><option>-i</option></entry>
<entry>interactive</entry>
<entry>Script runs in <emphasis>interactive</emphasis> mode</entry>
</row>
<row>
<entry><option>-p</option></entry>
<entry>privileged</entry>
<entry>Script runs as <quote>suid</quote> (caution!)</entry>
</row>
<row>
<entry><option>-r</option></entry>
<entry>restricted</entry>
<entry>Script runs in <emphasis>restricted</emphasis>
mode (see <xref linkend="restricted-sh">).</entry>
</row>
<row>
<entry><option>-u</option></entry>
<entry>nounset</entry>
<entry>Attempt to use undefined variable
outputs error message, and forces an exit</entry>
</row>
<row>
<entry><option>-v</option></entry>
<entry>verbose</entry>
<entry>Print each command to <filename>stdout</filename> before executing it</entry>
</row>
<row>
<entry><option>-x</option></entry>
<entry>xtrace</entry>
<entry>Similar to <option>-v</option>, but expands commands</entry>
</row>
<row>
<entry><option>-e</option></entry>
<entry>errexit</entry>
<entry>Abort script at first error (when a command exits with
non-zero status)</entry>
</row>
<row>
<entry><option>-n</option></entry>
<entry>noexec</entry>
<entry>Read commands in script, but do not execute them (syntax check)</entry>
</row>
<row>
<entry><option>-s</option></entry>
<entry>stdin</entry>
<entry>Read commands from <filename>stdin</filename></entry>
</row>
<row>
<entry><option>-t</option></entry>
<entry>(none)</entry>
<entry>Exit after first command</entry>
</row>
<row>
<entry><option>-</option></entry>
<entry>(none)</entry>
<entry>End of options flag. All other arguments
are <link linkend="posparamref">positional
parameters</link>.</entry>
</row>
<row>
<entry><option>--</option></entry>
<entry>(none)</entry>
<entry>Unset positional parameters.
If arguments given (<varname>-- arg1 arg2</varname>),
positional parameters set to arguments.</entry>
</row>
</tbody>
</tgroup>
</table>
</chapter> <!-- Options -->
<chapter id="gotchas">
<title>Gotchas</title>
<epigraph>
<attribution>Puccini</attribution>
<para>Turandot: Gli enigmi sono tre, la morte una!</para>
<para>Caleph: No, no! Gli enigmi sono tre, una la vita!</para>
</epigraph>
<para>Assigning reserved words or characters to variable names.
<programlisting>case=value0 # Causes problems.
23skidoo=value1 # Also problems.
# Variable names starting with a digit are reserved by the shell.
# Try _23skidoo=value1. Starting variables with an underscore is o.k.
# However... using just the underscore will not work.
_=25
echo $_ # $_ is a special variable set to last arg of last command.
xyz((!*=value2 # Causes severe problems.</programlisting>
</para>
<para>Using a hyphen or other reserved characters in a variable name.
<programlisting>var-1=23
# Use 'var_1' instead.</programlisting>
</para>
<para>Using the same name for a variable and a function. This can make a
script difficult to understand.
<programlisting>do_something ()
{
echo "This function does something with \"$1\"."
}
do_something=do_something
do_something do_something
# All this is legal, but highly confusing.</programlisting>
</para>
<para><anchor id="wsbad">Using <link
linkend="whitespaceref">whitespace</link> inappropriately
(in contrast to other programming languages, Bash can be quite
finicky about whitespace).
<programlisting>var1 = 23 # 'var1=23' is correct.
# On line above, Bash attempts to execute command "var1"
# with the arguments "=" and "23".
let c = $a - $b # 'let c=$a-$b' or 'let "c = $a - $b"' are correct.
if [ $a -le 5] # if [ $a -le 5 ] is correct.
# if [ "$a" -le 5 ] is even better.
# [[ $a -le 5 ]] also works.</programlisting>
</para>
<para>Assuming uninitialized variables (variables before a value is
assigned to them) are <quote>zeroed out</quote>. An
uninitialized variable has a value of <quote>null</quote>,
<emphasis>not</emphasis> zero.</para>
<para>Mixing up <emphasis>=</emphasis> and <emphasis>-eq</emphasis> in
a test. Remember, <emphasis>=</emphasis> is for comparing literal
variables and <emphasis>-eq</emphasis> for integers.
<programlisting>if [ "$a" = 273 ] # Is $a an integer or string?
if [ "$a" -eq 273 ] # If $a is an integer.
# Sometimes you can mix up -eq and = without adverse consequences.
# However...
a=273.0 # Not an integer.
if [ "$a" = 273 ]
then
echo "Comparison works."
else
echo "Comparison does not work."
fi # Comparison does not work.
# Same with a=" 273" and a="0273".
# Likewise, problems trying to use "-eq" with non-integer values.
if [ "$a" -eq 273.0 ]
then
echo "a = $a'
fi # Aborts with an error message.
# test.sh: [: 273.0: integer expression expected</programlisting>
</para>
<para>Sometimes variables within <quote>test</quote> brackets
([ ]) need to be quoted (double quotes). Failure to do so may
cause unexpected behavior. See <xref linkend="strtest">, <xref
linkend="redir2">, and <xref linkend="arglist">.</para>
<para>Commands issued from a script may fail to execute because
the script owner lacks execute permission for them. If a user
cannot invoke a command from the command line, then putting it
into a script will likewise fail. Try changing the attributes of
the command in question, perhaps even setting the suid bit
(as root, of course).</para>
<para>Attempting to use <command>-</command> as a redirection
operator (which it is not) will usually result in an unpleasant
surprise.
<programlisting>command1 2&gt; - | command2 # Trying to redirect error output of command1 into a pipe...
# ...will not work.
command1 2&gt;&amp; - | command2 # Also futile.
Thanks, S.C.</programlisting></para>
<para>Using Bash <link linkend="bash2ref">version 2</link>
functionality in a script headed with
<userinput>#!/bin/bash</userinput> may cause a bailout
with error messages. Older Linux machines may have version
1.x of Bash as the default installation (<command>echo
$BASH_VERSION</command>). Try changing the header of the script
to <userinput>#!/bin/bash2</userinput>.</para>
<para>Using Bash-specific functionality in a Bourne shell script
(<userinput>#!/bin/sh</userinput>) on a non-Linux machine
may cause unexpected behavior. A Linux system usually aliases
<command>sh</command> to <command>bash</command>, but this does
not necessarily hold true for a generic UNIX machine.</para>
<para>A script with DOS-type newlines (<replaceable>\r\n</replaceable>)
will fail to execute, since <userinput>#!/bin/bash\r\n</userinput>
is not recognized, <emphasis>not</emphasis> the same as the
expected <userinput>#!/bin/bash\n</userinput>. The fix is to
convert the script to UNIX-style newlines.</para>
<para>A shell script headed by <userinput>#!/bin/sh</userinput>
may not run in full Bash-compatibility mode. Some Bash-specific
functions might be disabled. Scripts that need complete
access to all the Bash-specific extensions should start with
<userinput>#!/bin/bash</userinput>.</para>
<para>A script may not <command>export</command> variables back
to its <link linkend="forkref">parent process</link>, the shell,
or to the environment. Just as we learned in biology, a child
process can inherit from a parent, but not vice versa.
<programlisting>WHATEVER=/home/bozo
export WHATEVER
exit 0</programlisting>
<screen><prompt>bash$ </prompt><command>echo $WHATEVER</command>
<computeroutput>
</computeroutput>
<prompt>bash$ </prompt></screen>
Sure enough, back at the command prompt, $WHATEVER remains unset.
</para>
<para>Setting and manipulating variables in a subshell, then attempting
to use those same variables outside the scope of the subshell will
result an unpleasant surprise.</para>
<example id="subpit">
<title>Subshell Pitfalls</title>
<programlisting>&subpit;</programlisting>
</example>
<para>Using <quote>suid</quote> commands in scripts is risky,
as it may compromise system security.
<footnote><para>Setting the <emphasis>suid</emphasis> permission on
a script has no effect.</para></footnote>
</para>
<para>Using shell scripts for CGI programming may be problematic. Shell
script variables are not <quote>typesafe</quote>, and this can cause
undesirable behavior as far as CGI is concerned. Moreover, it is
difficult to <quote>hacker-proof</quote> shell scripts.</para>
<epigraph>
<attribution>A.J. Lamb and H.W. Petrie</attribution>
<para>Danger is near thee --</para>
<para>Beware, beware, beware, beware.</para>
<para>Many brave hearts are asleep in the deep.</para>
<para>So beware --</para>
<para>Beware.</para>
</epigraph>
</chapter> <!-- Gotchas -->
<chapter id="scrstyle">
<title>Scripting With Style</title>
<para>Get into the habit of writing shell scripts in a structured and
systematic manner. Even <quote>on-the-fly</quote> and
<quote>written on the back of an envelope</quote> scripts will
benefit if you take a few minutes to plan and organize your
thoughts before sitting down and coding.</para>
<para>Herewith are a few stylistic guidelines. This is not
intended as an <emphasis>Official Shell Scripting
Stylesheet</emphasis>.</para>
<sect1 id="unofficialst">
<title>Unofficial Shell Scripting Stylesheet</title>
<itemizedlist>
<listitem>
<para>Comment your code. This makes it easier for others to
understand (and appreciate), and easier for you to maintain.
<programlisting>PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
# It made perfect sense when you wrote it last year, but now it's a complete mystery.
# (From Antek Sawicki's "pw.sh" script.)</programlisting>
</para>
<para>Add descriptive headers to your scripts and functions.
<programlisting>#!/bin/bash
#************************************************#
# xyz.sh
# written by Bozo Bozeman
# July 05, 2001
# Clean up project files.
#************************************************#
BADDIR=65 # No such directory.
projectdir=/home/bozo/projects # Directory to clean up.
#-------------------------------------------------------#
# cleanup_pfiles ()
# Removes all files in designated directory.
# Parameter: $target_directory
# Returns: 0 on success, $BADDIR if something went wrong.
#-------------------------------------------------------#
cleanup_pfiles ()
{
if [ ! -d "$1" ] # Test if target directory exists.
then
echo "$1 is not a directory."
return $BADDIR
fi
rm -f "$1"/*
return 0 # Success.
}
cleanup_pfiles $projectdir
exit 0</programlisting>
Be sure to put the <emphasis>#!/bin/bash</emphasis> at the
beginning of the first line of the script, preceding any
comment headers.</para>
</listitem>
<listitem>
<para>Avoid using <quote>magic numbers</quote>,
<footnote><para>In this context, <quote> magic
numbers</quote> have an entirely different meaning than
the <link linkend="magnumref">magic numbers</link> used
to designate file types.</para></footnote>
that is, <quote>hard-wired</quote> literal constants. Use
meaningful variable names instead. This makes the script
easier to understand and permits making changes and updates
without breaking the application.
<programlisting>if [ -f /var/log/messages ]
then
...
fi
# A year later, you decide to change the script to check /var/log/syslog.
# It is now necessary to manually change the script, instance by instance,
# and hope nothing breaks.
# A better way:
LOGFILE=/var/log/messages # Only line that needs to be changed.
if [ -f $LOGFILE ]
then
...
fi</programlisting>
</para>
</listitem>
<listitem>
<para>Choose descriptive names for variables and functions.
<programlisting>fl=`ls -al $dirname` # Cryptic.
file_listing=`ls -al $dirname` # Better.
MAXVAL=10 # All caps used for a script constant.
while [ "$index" -le "$MAXVAL" ]
...
E_NOTFOUND=75 # Uppercase for an errorcode,
# and name begins with "E_".
if [ ! -e "$filename" ]
then
echo "File $filename not found."
exit $E_NOTFOUND
fi
MAIL_DIRECTORY=/var/spool/mail/bozo # Uppercase for an environmental variable.
export MAIL_DIRECTORY
GetAnswer () # Mixed case works well for a function.
{
prompt=$1
echo -n $prompt
read answer
return $answer
}
GetAnswer "What is your favorite number? "
favorite_number=$?
echo $favorite_number
_uservariable=23 # Permissable, but not recommended.
# It's better for user-defined variables not to start with an underscore.
# Leave that for system variables.</programlisting>
</para>
</listitem>
<listitem>
<para>Use <link linkend="exitcommandref">exit codes</link>
in a systematic and meaningful way.
<programlisting>E_WRONG_ARGS=65
...
...
exit $E_WRONG_ARGS</programlisting>
See also <xref linkend="exitcodes">.</para>
</listitem>
<listitem>
<para>Break complex scripts into simpler modules. Use functions
where appropriate. See <xref linkend="ex79">.</para>
</listitem>
<listitem>
<para>Don't use a complex construct where a simpler one will do.
<programlisting>COMMAND
if [ $? -eq 0 ]
...
# Redundant and non-intuitive.
if COMMAND
...
# More concise (if perhaps not quite as legible).</programlisting>
</para>
</listitem>
</itemizedlist>
</sect1> <!-- Unofficial Shell Scripting Stylesheet -->
</chapter> <!-- Scripting With Style -->
<chapter id="miscellany">
<title>Miscellany</title>
<epigraph>
<attribution>Tom Duff</attribution>
<para>Nobody really knows what the Bourne shell's grammar is. Even
examination of the source code is little help.
</para>
</epigraph>
<sect1 id="intandnonint">
<title>Interactive and non-interactive shells and scripts</title>
<para>An <emphasis>interactive</emphasis> shell reads commands from
user input on a <filename>tty</filename>. Among other things,
such a shell reads startup files on activation, displays
a prompt, and enables job control by default. The user can
<emphasis>interact</emphasis> with the shell.</para>
<para>A shell running a script is always a non-interactive
shell. All the same, the script can still access its
<filename>tty</filename>. It is even possible to emulate an
interactive shell in a script.
<programlisting>#!/bin/bash
MY_PROMPT='$ '
while :
do
echo -n "$MY_PROMPT"
read line
eval "$line"
done
exit 0
# This example script, and much of the above explanation supplied by
# Stephane Chazelas (thanks again).</programlisting></para>
<para>Let us consider an <emphasis>interactive</emphasis> script
to be one that requires input from the user, usually with
<link linkend="readref">read</link> statements (see <xref
linkend="ex36">). <quote>Real life</quote> is actually a
bit messier than that. For now, assume an interactive script
is bound to a tty, a script that a user has invoked from the
console or an xterm.</para>
<para>Init and startup scripts are necessarily non-interactive,
since they must run without human intervention. Many
administrative and system maintenance scripts are likewise
non-interactive. Unvarying repetitive tasks cry out for
automation by non-interactive scripts.</para>
<para>Non-interactive scripts can run in the background, but
interactive ones hang, waiting for input that never comes.
Handle that difficulty by having an <command>expect</command>
script or embedded <link linkend="heredocref">here
document</link> feed input to an interactive script running
as a background job. In the simplest case, redirect a
file to supply input to a <command>read</command> statement
(<command>read variable &lt;file</command>). These particular
workarounds make possible general purpose scripts that run
in either interactive or non-interactive modes.</para>
<para>If a script needs to test whether it is running in an
interactive shell, it is simply a matter of finding
whether the <emphasis>prompt</emphasis> variable, <link
linkend="ps1ref">$PS1</link> is set. (If the user is being
prompted for input, then the script needs to display a prompt.)
<programlisting>if [ -z $PS1 ] # no prompt?
then
# non-interactive
...
else
# interactive
...
fi</programlisting>
<anchor id="iitest">Alternatively, the script can test
for the presence of option <quote>i</quote> in the <link
linkend="flpref">$-</link> flag.
<programlisting>case $- in
*i*) # interactive shell
;;
*) # non-interactive shell
;;
# (Thanks to "UNIX F.A.Q.", 1993)</programlisting></para>
<note><para>Scripts may be forced to run in interactive
mode with the <token>-i</token> option or with a
<emphasis>#!/bin/bash -i</emphasis> header. Be aware that this
can cause erratic script behavior or show error messages even
when no error is present.</para></note>
</sect1> <!-- Interactive and non-interactive scripts -->
<sect1 id="testsandcomparisons">
<title>Tests and Comparisons</title>
<para>For tests, the <link linkend="dblbrackets">[[ ]]</link>
construct may be more appropriate than <userinput>[
]</userinput>. Likewise, arithmetic comparisons might benefit
from the <link linkend="dblparens">(( ))</link> construct.
<programlisting>a=8
# All of the comparisons below are equivalent.
test "$a" -lt 16 && echo "yes, $a < 16" # "and list"
/bin/test "$a" -lt 16 && echo "yes, $a < 16"
[ "$a" -lt 16 ] && echo "yes, $a < 16"
[[ $a -lt 16 ]] && echo "yes, $a < 16" # Quoting variables within
(( a < 16 )) && echo "yes, $a < 16" # [[ ]] and (( )) not necessary.
city="New York"
# Again, all of the comparisons below are equivalent.
test "$city" \< Paris && echo "Yes, Paris is greater than $city" # Greater ASCII order.
/bin/test "$city" \< Paris && echo "Yes, Paris is greater than $city"
[ "$city" \< Paris ] && echo "Yes, Paris is greater than $city"
[[ $city < Paris ]] && echo "Yes, Paris is greater than $city" # Need not quote $city.
# Thank you, S.C.</programlisting></para>
</sect1> <!-- Tests and Comparisons -->
<sect1 id="optimizations">
<title>Optimizations</title>
<para>Most shell scripts are quick 'n dirty solutions to non-complex
problems. As such, optimizing them for speed is not much of an
issue. Consider the case, though, where a script carries out
an important task, does it well, but runs too slowly. Rewriting
it in a compiled language may not be a palatable option. The
simplest fix would be to rewrite the parts of the script
that slow it down. Is it possible to apply principles of code
optimization even to a lowly shell script?</para>
<para>Check the loops in the script. Time consumed by repetitive
operations adds up quickly. Use the <link
linkend="timref">time</link> and <link
linkend="timesref">times</link> tools to profile
computation-intensive commands. Consider rewriting time-critical
code sections in C, or even in assembler.</para>
<para>Try to minimize file i/o. Bash is not particularly efficient at
handling files, so consider using more appropriate tools for
this within the script, such as awk or Perl.</para>
<para>Try to write your scripts in a structured, coherent form, so
they can be reorganized and tightened up as necessary. Some of the
optimization techniques applicable to high-level languages may work
for scripts, but others, such as loop unrolling, are mostly
irrelevant. Above all, use common sense.</para>
</sect1> <!-- Optimizations -->
<sect1 id="assortedtips">
<title>Assorted Tips</title>
<itemizedlist>
<listitem>
<para>To keep a record of which user scripts have run
during a particular sesssion or over a number of sessions,
add the following lines to each script you want to keep track
of. This will keep a continuing file record of the script
names and invocation times. </para>
<para>
<programlisting># Append (>>) following to end of each script tracked.
date>> $SAVE_FILE #Date and time.
echo $0>> $SAVE_FILE #Script name.
echo>> $SAVE_FILE #Blank line as separator.
# Of course, SAVE_FILE defined and exported as environmental variable in ~/.bashrc
# (something like ~/.scripts-run)</programlisting>
</para>
</listitem>
<listitem>
<para>A shell script may act as an embedded command inside
another shell script, a <emphasis>Tcl</emphasis> or
<emphasis>wish</emphasis> script, or even a Makefile. It can
be invoked as as an external shell command in a C program
using the <replaceable>system()</replaceable> call, i.e.,
<replaceable>system("script_name");</replaceable>.</para>
</listitem>
<listitem>
<para>Put together files containing your favorite and most useful
definitions and functions. As necessary,
<quote>include</quote> one or more of these
<quote>library files</quote> in scripts with either the
<link linkend="dotref">dot</link> (<command>.</command>)
or <link linkend="sourceref">source</link> command.</para>
<para>
<programlisting># SCRIPT LIBRARY
# ------ -------
# Note:
# No "#!" here.
# No "live code" either.
# Useful variable definitions
ROOT_UID=0 # Root has $UID 0.
E_NOTROOT=101 # Not root user error.
MAXRETVAL=256 # Maximum (positive) return value of a function.
SUCCESS=0
FAILURE=-1
# Functions
Usage () # "Usage:" message.
{
if [ -z "$1" ] # No arg passed.
then
msg=filename
else
msg=$@
fi
echo "Usage: `basename $0` "$msg""
}
Check_if_root () # Check if root running script.
{ # From "ex39.sh" example.
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi
}
CreateTempfileName () # Creates a "unique" temp filename.
{ # From "ex51.sh" example.
prefix=temp
suffix=`eval date +%s`
Tempfilename=$prefix.$suffix
}
isalpha2 () # Tests whether *entire string* is alphabetic.
{ # From "isalpha.sh" example.
[ $# -eq 1 ] || return $FAILURE
case $1 in
*[!a-zA-Z]*|"") return $FAILURE;;
*) return $SUCCESS;;
esac # Thanks, S.C.
}
abs () # Absolute value.
{ # Caution: Max return value = 256.
E_ARGERR=-999999
if [ -z "$1" ] # Need arg passed.
then
return $E_ARGERR # Obvious error value returned.
fi
if [ "$1" -ge 0 ] # If non-negative,
then #
absval=$1 # stays as-is.
else # Otherwise,
let "absval = (( 0 - $1 ))" # change sign.
fi
return $absval
}</programlisting>
</para>
</listitem>
<listitem>
<para>Use special-purpose comment headers to increase clarity
and legibility in scripts.</para>
<para><programlisting>## Caution.
rm -rf *.zzy ## The "-rf" options to "rm" are very dangerous,
##+ especially with wildcards.
#+ Line continuation.
# This is line 1
#+ of a multi-line comment,
#+ and this is the final line.
#* Note.
#o List item.
#> Another point of view.
while [ "$var1" != "end" ] #> while test "$var1" != "end"</programlisting></para>
</listitem>
<listitem>
<para>Using the <link linkend="xstatvarref">$? exit status
variable</link>, a script may test if a parameter contains
only digits, so it can be treated as an integer.</para>
<para>
<programlisting>#!/bin/bash
SUCCESS=0
E_BADINPUT=65
test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null
# An integer is either equal to 0 or not equal to 0.
# 2>/dev/null suppresses error message.
if [ $? -ne "$SUCCESS" ]
then
echo "Usage: `basename $0` integer-input"
exit $E_BADINPUT
fi
let "sum = $1 + 25" # Would give error if $1 not integer.
echo "Sum = $sum"
# Any variable, not just a command line parameter, can be tested this way.
exit 0</programlisting>
</para>
</listitem>
<listitem>
<para>Using the double parentheses construct, it is possible
to use C-like syntax for setting and incrementing variables
and in <link linkend="forloopref">for</link> and <link
linkend="whileloopref">while</link> loops. See <xref
linkend="forloopc"> and <xref linkend="whloopc">.</para>
</listitem>
<listitem>
<para>The <link linkend="runpartsref">run-parts</link>
command is handy for running a set of command
scripts in sequence, particularly in combination
with <link linkend="cronref">cron</link> or <link
linkend="atref">at</link>.</para>
</listitem>
<listitem>
<para>It would be nice to be able to invoke X-Windows widgets
from a shell script. There happen to exist
several packages that purport to do so, namely
<emphasis>Xscript</emphasis>, <emphasis>Xmenu</emphasis>,
and <emphasis>widtools</emphasis>. The first two of
these no longer seem to be maintained. Fortunately, it is
still possible to obtain <emphasis>widtools</emphasis> <ulink
url="http://www.batse.msfc.nasa.gov/~mallozzi/home/software/xforms/src/widtools-2.0.tgz">here</ulink>.
</para>
<caution><para>The <emphasis>widtools</emphasis> (widget tools)
package requires the <emphasis>XForms</emphasis> library to
be installed. Additionally, the <filename>Makefile</filename>
needs some judicious editing before the package will build
on a typical Linux system. Finally, three of the six widgets
offered do not work (and, in fact, segfault).</para></caution>
<para>For more effective scripting with widgets, try
<emphasis>Tk</emphasis> or <emphasis>wish</emphasis>
(<emphasis>Tcl</emphasis> derivatives),
<emphasis>PerlTk</emphasis> (Perl with Tk extensions),
<emphasis>tksh</emphasis> (ksh with Tk extensions),
<emphasis>XForms4Perl</emphasis> (Perl with XForms
extensions), <emphasis>Gtk-Perl</emphasis> (Perl with Gtk
extensions), or <emphasis>PyQt</emphasis> (Python with
Qt extensions).</para>
</listitem>
</itemizedlist>
</sect1> <!-- Assorted Tips -->
<sect1 id="portabilityissues">
<title>Portability Issues</title>
<para>This book deals specifically with Bash scripting on
a GNU/Linux system. All the same, users of <command>sh</command>
and <command>ksh</command> will find much of value here.</para>
<para>As it happens, many of the various shells and scripting
languages seem to be converging toward the POSIX 1003.2
standard. Invoking Bash with the <option>--posix</option>
option or inserting a <command>set -o posix</command> at the
head of a script causes Bash to conform very closely to this
standard. Even lacking this measure, most Bash scripts will run
as-is under <command>ksh</command>, and vice-versa, since
Chet Ramey has been busily porting <command>ksh</command>
features to the latest versions of Bash.</para>
<para>On a commercial UNIX machine, scripts using GNU-specific
features of standard commands may not work. This has become
less of a problem in the last few years, as the GNU utilities
have pretty much displaced their proprietary counterparts even
on <quote>big-iron</quote> UNIX. Caldera's recent release of
the source to many of the original UNIX utilities will only
accelerate the trend.</para>
</sect1> <!-- Portability Issues -->
</chapter> <!-- Miscellany -->
<chapter id="bash2">
<title>Bash, version 2</title>
<para><anchor id="bash2ref"></para>
<para>The current version of <emphasis>Bash</emphasis>, the one
you have running on your machine, is actually version 2. This
update of the classic Bash scripting language added array
variables,
<footnote><para>Chet Ramey promises associative arrays (a Perl
feature) in a future Bash release.</para></footnote>
string and parameter expansion, and a better method
of indirect variable references, among other features.</para>
<example id="ex77">
<title>String expansion</title>
<programlisting>&ex77;</programlisting>
</example>
<example id="ex78">
<title>Indirect variable references - the new way</title>
<programlisting>&ex78;</programlisting>
</example>
<example id="ex79">
<title>Using arrays and other miscellaneous trickery
to deal four random hands from a deck of cards</title>
<programlisting>&ex79;</programlisting>
</example>
</chapter> <!-- Bash, version 2 -->
</part> <!-- Part 4 (Advanced Topics) -->
<chapter id="credits">
<title>Credits</title>
<para><ulink url="mailto:feloy@free.fr">Philippe Martin</ulink>
translated this document into DocBook/SGML. While not on
the job at a small French company as a software developer,
he enjoys working on GNU/Linux documentation and software,
reading literature, playing music, and for his peace of mind
making merry with friends. You may run across him somewhere
in France or in the Basque Country, or email him at <ulink
url="mailto:feloy@free.fr">feloy@free.fr</ulink>.</para>
<para>Philippe Martin also pointed out that positional parameters
past $9 are possible using {bracket} notation, see <xref
linkend="ex17">.</para>
<para><ulink url="mailto:stephane_chazelas@yahoo.fr">Stephane
Chazelas</ulink> sent a long list of corrections, additions, and
example scripts. More than a contributor, he has, in effect, taken
on the role of <command>editor</command> for this document. Merci
beaucoup !</para>
<para>I would like to especially thank <emphasis>Patrick
Callahan</emphasis>, <emphasis>Mike Novak</emphasis>, and
<emphasis>Pal Domokos</emphasis> for catching bugs, pointing out
ambiguities, and for suggesting clarifications and changes.
Their lively discussion of shell scripting and general
documentation issues inspired me to try to make this document
more readable.</para>
<para>I'm grateful to Jim Van Zandt for pointing out errors and
omissions in version 0.2 of this document. He also contributed
an instructive example script.</para>
<para>Many thanks to <ulink
url="mailto:mikaku@arrakis.es">Jordi Sanfeliu</ulink>
for giving permission to use his fine tree script (<xref
linkend="tree">).</para>
<para>Kudos to <ulink
url="mailto:friedman@prep.ai.mit.edu">Noah Friedman</ulink>
for permission to use his string function script (<xref
linkend="string">).</para>
<para>Emmanuel Rouat suggested corrections and additions on
<link linkend="commandsubref">command substitution</link> and
<link linkend="aliasref">aliases</link>. He also contributed
a very nice sample <filename>.bashrc</filename> file (<xref
linkend="sample-bashrc">).</para>
<para><ulink url="mailto:heiner.steven@odn.de">Heiner Steven</ulink>
kindly gave permission to use his base conversion script, <xref
linkend="base">. He also made a number of corrections and many
helpful suggestions. Special thanks.</para>
<para>Florian Wisser enlightened me on some of the fine points of
testing strings (see <xref linkend="strtest">), and on other
matters.</para>
<para>Marc-Jano Knopp sent corrections on DOS batch files.</para>
<para>Hyun Jin Cha found several typos in the document in the
process of doing a Korean translation. Thanks for pointing
these out.</para>
<para>Others making helpful suggestions and pointing out errors
were Gabor Kiss, Leopold Toetsch, and Nick Drage (script
ideas!).</para>
<para>My gratitude to <ulink url="mailto:chet@po.cwru.edu">Chet
Ramey</ulink> and Brian Fox for writing <command>Bash</command>,
an elegant and powerful scripting tool.</para>
<para>Thanks most of all to my wife, Anita, for her encouragement and
emotional support.</para>
</chapter> <!-- Credits -->
<chapter id="endnotes">
<title>Endnotes</title>
<sect1 id="authorsnote">
<title>Author's Note</title>
<para>How did I come to write a Bash scripting book? It's a strange
tale. It seems that a couple of years back, I needed to learn
shell scripting, and what better way to do that than to read
a good book on the subject. I was looking to buy a tutorial
and reference covering all aspects of scripting. In fact,
I was looking for this very book, or something much like
it. Unfortunately, it didn't exist, so if I wanted it, I had to
write it. And so, here we are, folks.</para>
<para>This reminds me of the apocryphal story about the mad
professor. Crazy as a loon, the fellow was. At the sight of a
book, any book, at the library, at a bookstore, anywhere, he
would become totally obsessed with the idea that he could have
written it, should have written it, and done a better job of it to
boot. He would thereupon rush home and proceed to do just that,
write a book with the same title. When he died some years later,
he allegedly had several thousand books to his credit, probably
putting even Asimov to shame. The books might not have been any
good, who knows, but does that really matter? Here's a fellow
who lived his dream, even if he was driven by it, and I can't
help admiring the old coot...</para>
</sect1> <!-- Author's Note -->
<sect1 id="aboutauthor">
<title>About the Author</title>
<subtitle>Who is this guy anyhow?</subtitle>
<para>The author claims no credentials or special qualifications,
other than a compulsion to write.
<footnote><para>Those who can, do. Those who can't... get an
MCSE.</para></footnote>
This book is somewhat of a departure from his other major work,
<ulink url="http://personal.riverusers.com/~thegrendel/hmw45.zip">
HOW-2 Meet Women: The Shy Man's Guide to
Relationships</ulink>.</para>
<para>A Linux user since 1995 (Slackware 2.2, kernel 1.2.1),
the author has written a few
software truffles, including the <ulink
url="http://ibiblio.org/pub/Linux/utils/file/cruft-0.2.tar.gz">cruft</ulink>
one-time pad encryption utility, the <ulink
url="http://ibiblio.org/pub/Linux/apps/financial/mcalc-1.6.tar.gz">mcalc</ulink>
mortgage calculator, the <ulink
url="http://ibiblio.org/pub/Linux/games/amusements/judge-1.0.tar.gz">judge</ulink>
Scrabble&reg; adjudicator, and the <ulink
url="http://ibiblio.org/pub/Linux/libs/yawl-0.2.tar.gz">yawl</ulink>
word gaming list package. He got his start in programming using
FORTRAN IV on a CDC 3800, but is not the least bit nostalgic
for those days.</para>
<para>Living in a secluded desert community with wife and dog,
he cherishes human frailty.</para>
</sect1> <!-- About the Author -->
<sect1 id="toolsused">
<title>Tools Used to Produce This Book</title>
<sect2 id="software-printware">
<title>Software and Printware</title>
<orderedlist id="software-printware2" numeration="lowerroman">
<listitem>
<para>Bram Moolenaar's powerful SGML-aware <ulink
url="http://www.vim.org">vim</ulink> text editor.</para>
</listitem>
<listitem>
<para><ulink
url="http://www.netfolder.com/DSSSL/">OpenJade</ulink>,
a DSSSL rendering engine for converting SGML documents into other
formats.</para>
</listitem>
<listitem>
<para><ulink url="http://nwalsh.com/docbook/dsssl/"> Norman
Walsh's DSSSL stylesheets</ulink>.</para>
</listitem>
<listitem>
<para><emphasis>DocBook, The Definitive Guide</emphasis>, by Norman
Walsh and Leonard Muellner (O'Reilly, ISBN 1-56592-580-7). This is
the standard reference for anyone attempting to write a document in
Docbook SGML format.</para>
</listitem>
</orderedlist>
</sect2> <!-- Software and Printware -->
</sect1> <!-- Tools Used -->
</chapter> <!-- End Notes -->
<bibliography id="biblio">
<anchor id="biblioref">
<biblioentry>
<authorgroup>
<author><firstname>Dale</firstname><surname>Dougherty</surname></author>
<author><firstname>Arnold</firstname><surname>Robbins</surname></author>
</authorgroup>
<title>Sed and Awk</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>1997</year>
</copyright>
<isbn>1-156592-225-5</isbn>
<abstract><para>
To unfold the full power of shell scripting, you need at least a passing
familiarity with <command>sed</command> and
<command>awk</command>. This is the standard tutorial. It
includes an excellent introduction to <quote>regular expressions</quote>. Read this
book.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Aeleen</firstname><surname>Frisch</surname></author>
</authorgroup>
<title>Essential System Administration</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>1995</year>
</copyright>
<isbn>1-56592-127-5</isbn>
<abstract><para>This excellent sys admin manual has a decent introduction to shell
scripting for sys administrators and does a nice job of explaining the
startup and initialization scripts. The book is long overdue for a third
edition (are you listening, Tim O'Reilly?).</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Stephen</firstname><surname>Kochan</surname></author>
<author><firstname>Patrick</firstname><surname>Woods</surname></author>
</authorgroup>
<title>Unix Shell Programming</title>
<publisher>
<publishername>Hayden</publishername>
</publisher>
<copyright>
<year>1990</year>
</copyright>
<isbn>067248448X</isbn>
<abstract><para>The standard reference, though a bit dated by now.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Neil</firstname><surname>Matthew</surname></author>
<author><firstname>Richard</firstname><surname>Stones</surname></author>
</authorgroup>
<title>Beginning Linux Programming</title>
<publisher>
<publishername>Wrox Press</publishername>
</publisher>
<copyright>
<year>1996</year>
</copyright>
<isbn>1874416680</isbn>
<abstract><para>Good in-depth coverage of various programming
languages available for Linux, including a fairly strong chapter
on shell scripting.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Herbert</firstname><surname>Mayer</surname></author>
</authorgroup>
<title>Advanced C Programming on the IBM PC</title>
<publisher>
<publishername>Windcrest Books</publishername>
</publisher>
<copyright>
<year>1989</year>
</copyright>
<isbn>0830693637</isbn>
<abstract><para>Excellent coverage of algorithms and general
programming practices.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>David</firstname><surname>Medinets</surname></author>
</authorgroup>
<title>Unix Shell Programming Tools</title>
<publisher>
<publishername>McGraw-Hill</publishername>
</publisher>
<copyright>
<year>1999</year>
</copyright>
<isbn>0070397333</isbn>
<abstract><para>Good info on shell scripting, with examples, and a short
intro to Tcl and Perl.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Cameron</firstname><surname>Newham</surname></author>
<author><firstname>Bill</firstname><surname>Rosenblatt</surname></author>
</authorgroup>
<title>Learning the Bash Shell</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>1998</year>
</copyright>
<isbn>1-56592-347-2</isbn>
<abstract><para>This is a valiant effort at a decent shell primer, but somewhat deficient
in coverage on programming topics and lacking sufficient examples.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Anatole</firstname><surname>Olczak</surname></author>
</authorgroup>
<title>Bourne Shell Quick Reference Guide</title>
<publisher>
<publishername>ASP, Inc.</publishername>
</publisher>
<copyright>
<year>1991</year>
</copyright>
<isbn>093573922X</isbn>
<abstract><para>A very handy pocket reference, despite lacking
coverage of Bash-specific features.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Jerry</firstname><surname>Peek</surname></author>
<author><firstname>Tim</firstname><surname>O'Reilly</surname></author>
<author><firstname>Mike</firstname><surname>Loukides</surname></author>
</authorgroup>
<title>Unix Power Tools</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<publisher>
<publishername>Random House</publishername>
</publisher>
<copyright>
<year>1997</year>
</copyright>
<isbn>1-56592-260-3</isbn>
<abstract><para>Contains a couple of sections of very informative
in-depth articles on shell programming, but falls short of being
a tutorial. It reproduces much of the regular expressions tutorial
from the Dougherty and Robbins book, above.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Arnold</firstname><surname>Robbins</surname></author>
</authorgroup>
<title>Bash Reference Card</title>
<publisher>
<publishername>SSC</publishername>
</publisher>
<copyright>
<year>1998</year>
</copyright>
<isbn>1-58731-010-5</isbn>
<abstract>
<para>Excellent Bash pocket reference (don't leave home
without it). A bargain at $4.95, but
also available for free download <ulink
url="http://www.ssc.com/ssc/bash/">on-line</ulink> in pdf
format.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Arnold</firstname><surname>Robbins</surname></author>
</authorgroup>
<title>Effective Awk Programming</title>
<publisher>
<publishername>Free Software Foundation / O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2000</year>
</copyright>
<isbn>1-882114-26-4</isbn>
<abstract>
<para>The absolute best <command>awk</command> tutorial and
reference. The free electronic version of this book is part of the
<command>awk</command> documentation, and printed copies of the
latest version are available from O'Reilly and Associates.</para>
<para>This book has served as an inspiration for the author of this
document.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Paul</firstname><surname>Sheer</surname></author>
</authorgroup>
<title>LINUX: Rute User's Tutorial and Exposition</title>
<edition>1st edition</edition>
<publisher>
<publishername></publishername>
</publisher>
<copyright>
<year>2002</year>
</copyright>
<isbn>0-13-033351-4</isbn>
<abstract>
<para>Very detailed and readable introduction to Linux system
administration.</para>
<para>The book is available in print, or
<ulink url="http://rute.sourceforge.net/">on-line</ulink>.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Ellen</firstname><surname>Siever</surname></author>
<author><surname>and the Staff of O'Reilly and Associates</surname></author>
</authorgroup>
<title>Linux in a Nutshell</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>1999</year>
</copyright>
<isbn>1-56592-585-8</isbn>
<abstract><para>The all-around best Linux command reference, even has a Bash section.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<title>The UNIX CD Bookshelf</title>
<edition>2nd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2000</year>
</copyright>
<isbn>1-56592-815-6</isbn>
<abstract><para>An array of six UNIX books on CD ROM, including
<emphasis>UNIX Power Tools</emphasis>, <emphasis>Sed
and Awk</emphasis>, and <emphasis>Learning the Korn
Shell</emphasis>. A complete set of all the UNIX references
and tutorials you would ever need at about $70. Buy this one,
even if it means going into debt and not paying the rent.</para>
<para>Unfortunately, out of print at present.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The O'Reilly books on Perl. (Actually, any O'Reilly books.)</para>
<para>-----------------------------------------------------------------------</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Ben Okopnik's well-written <emphasis>introductory Bash
scripting</emphasis> articles in issues 53, 54, 55, 57, and 59
of the <ulink url="http://www.linuxgazette.com">Linux Gazette
</ulink>, and his explanation of <quote>The Deep, Dark Secrets
of Bash</quote> in issue 56.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract><para>Chet Ramey's <emphasis>bash - The GNU Shell</emphasis>,
a two-part series published in issues 3 and 4 of the <ulink
url="http://www.linuxjournal.com">Linux Journal</ulink>,
July-August 1994.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Mike G's <ulink
url="http://www.linuxdoc.org/HOWTO/Bash-Prog-Intro-HOWTO.html">Bash-Programming-Intro
HOWTO</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Richard's <ulink url="http://www.injunea.demon.co.uk/index.htm">UNIX
Scripting Universe</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Chet Ramey's <ulink
url="ftp://ftp.cwru.edu/pub/bash/FAQ">Bash F.A.Q.</ulink></para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://alge.anart.no/linux/scripts/">Lucc's Shell Scripts
</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://www.oase-shareware.org/shell/scripts">SHELLdorado
</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://clri6f.gsi.de/gnu/bash-2.01/examples/scripts.noah/">Noah
Friedman's script site</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://sourceforge.net/snippet/browse.php?by=lang&amp;lang=7">
SourceForge Snippet Library - shell scrips</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Giles Orr's <ulink
url="http://www.linuxdoc.org/HOWTO/Bash-Prompt-HOWTO.html">Bash-Prompt
HOWTO</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The <ulink
url="http://www.cornerstonemag.com/sed/sedfaq.html">sed
F.A.Q.</ulink></para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Carlos Duarte's instructive <ulink
url="http://www.dbnet.ece.ntua.gr/~george/sed/sedtut_1.html"><quote>Do
It With Sed</quote></ulink> tutorial.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The GNU <command>gawk</command> <ulink
url="http://sunsite.ualberta.ca/Documentation/Gnu/gawk-3.0.6/gawk.html">
reference manual</ulink> (<command>gawk</command> is the extended
GNU version of <command>awk</command> available on Linux and
BSD systems).</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Trent Fisher's <ulink
url="http://www.cs.pdx.edu/~trent/gnu/groff/groff.html">groff
tutorial</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Mark Komarinski's <ulink
url="http://www.linuxdoc.org/HOWTO/Printing-Usage-HOWTO.html">Printing-Usage
HOWTO</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>There is some nice material on I/O redirection (<xref
linkend="io-redirection">) in <ulink
url="http://sunsite.ualberta.ca/Documentation/Gnu/textutils-2.0/html_chapter/textutils_10.html">
chapter 10 of the textutils documentation</ulink> at the <ulink
url="http://sunsite.ualberta.ca/Documentation"> University of
Alberta site</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="mailto:humbubba@smarty.smart.net">Rick
Hohensee</ulink> has written the <ulink
url="ftp://ftp.gwdg.de/pub/linux/install/clienux/interim/osimpa.tgz">
osimpa</ulink> i386 assembler entirely as Bash scripts.</para>
<para>-------------------------------------------------------------------------</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The excellent "Bash Reference Manual", by Chet Ramey and Brian Fox,
distributed as part of the "bash-2-doc" package (available as an rpm).
See especially the instructive example scripts in this package.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The manpages for <command>bash</command> and
<command>bash2</command>, <command>date</command>,
<command>expect</command>, <command>expr</command>,
<command>find</command>, <command>grep</command>,
<command>gzip</command>, <command>ln</command>,
<command>patch</command>, <command>tar</command>,
<command>tr</command>, <command>bc</command>,
<command>xargs</command>. The texinfo documentation
on <command>bash</command>, <command>dd</command>,
<command>gawk</command>, and <command>sed</command>.</para>
</abstract>
</biblioentry>
</bibliography>
<appendix id="contributed-scripts">
<title>Contributed Scripts</title>
<para>These scripts, while not fitting into the text of this document, do
illustrate some interesting shell programming techniques. They are useful,
too. Have fun analyzing and running them.</para>
<example id="manview">
<title><command>manview</command>: Viewing formatted manpages
</title>
<programlisting>&manview;</programlisting>
</example>
<example id="mailformat">
<title><command>mailformat</command>: Formatting an e-mail message</title>
<programlisting>&mailformat;</programlisting>
</example>
<example id="rn">
<title><command>rn</command>: A simple-minded file rename utility</title>
<para>This script is a modification of <xref
linkend="lowercase">.</para>
<programlisting>&rn;</programlisting>
</example>
<example id="encryptedpw">
<title><command>encryptedpw</command>: Uploading to an ftp site,
using a locally encrypted password</title>
<programlisting>&encryptedpw;</programlisting>
</example>
<example id="copycd">
<title><command>copy-cd</command>: Copying a data CD</title>
<programlisting>&copycd;</programlisting>
</example>
<example id="daysbetween">
<title><command>days-between</command>: Calculate number of days between two dates</title>
<programlisting>&daysbetween;</programlisting>
</example>
<para>+</para>
<para>The following two scripts are by Mark Moraes of the University
of Toronto. See the enclosed file <quote>Moraes-COPYRIGHT</quote>
for permissions and restrictions.</para>
<example id="behead">
<title><command>behead</command>: Removing mail and news message headers
</title>
<programlisting>&behead;</programlisting>
</example>
<example id="ftpget">
<title><command>ftpget</command>: Downloading files via ftp
</title>
<programlisting>&ftpget;</programlisting>
</example>
<para>+</para>
<para>Antek Sawicki contributed the following script, which makes very
clever use of the parameter substitution operators discussed in
<xref linkend="Parameter-Substitution">.</para>
<example id="pw">
<title><command>password</command>: Generating random
8-character passwords</title>
<programlisting>&pw;</programlisting>
</example>
<para>+</para>
<para>James R. Van Zandt contributed this script, which uses named pipes
and, in his words, <quote>really exercises quoting and escaping</quote>.
</para>
<example id="fifo">
<title><command>fifo</command>: Making daily backups, using named pipes</title>
<programlisting>&fifo;</programlisting>
</example>
<para>+</para>
<para>Stephane Chazelas contributed the following script to
demonstrate that generating prime numbers does not require
arrays.</para>
<example id="primes">
<title>Generating prime numbers using the modulo operator</title>
<programlisting>&primes;</programlisting>
</example>
<para>+</para>
<para>Jordi Sanfeliu gave permission to use his elegant
<emphasis>tree</emphasis> script.</para>
<example id="tree">
<title><command>tree</command>: Displaying a directory tree</title>
<programlisting>&tree;</programlisting>
</example>
<para>Noah Friedman gave permission to use his <emphasis>string
function</emphasis> script, which essentially reproduces some of the
C-library string manipulation functions.</para>
<example id="string">
<title><command>string functions</command>: C-like string functions</title>
<programlisting>&string;</programlisting>
</example>
<para>Stephane Chazelas demonstrates object-oriented programming a
Bash script.</para>
<example id="objoriented">
<title>Object-oriented database</title>
<programlisting>&objoriented;</programlisting>
</example>
</appendix>
<!-- End Contributed Scripts appendix -->
<appendix id="sedawk">
<title>A Sed and Awk Micro-Primer</title>
<para><anchor id="sedref"></para>
<para>This is a very brief introduction to the <command>sed</command>
and <command>awk</command> text processing utilities. We will
deal with only a few basic commands here, but that will suffice
for understanding simple sed and awk constructs within shell
scripts.</para>
<para><command>sed</command>: a non-interactive text file editor</para>
<para><command>awk</command>: a field-oriented pattern processing
language with a C-like syntax</para>
<para>For all their differences, the two utilities share a similar
invocation syntax, both use <link linkend="regexref">regular
expressions </link>, both read input by default
from <filename>stdin</filename>, and both output to
<filename>stdout</filename>. These are well-behaved UNIX tools,
and they work together well. The output from one can be piped
into the other, and their combined capabilities give shell
scripts some of the power of Perl.</para>
<note><para>One important difference between the utilities is
that while shell scripts can easily pass arguments to sed, it
is more complicated for awk (see <xref linkend="coltotaler">
and <xref linkend="coltotaler2">).
</para></note>
<sect1>
<title>Sed</title>
<para>Sed is a non-interactive line editor. It receives text
input, whether from <filename>stdin</filename> or from a
file, performs certain operations on specified lines of
the input, one line at a time, then outputs the result to
<filename>stdout</filename> or to a file. Within a shell script,
sed is usually one of several tool components in a pipe.</para>
<para>Sed determines which lines of its input that it will
operate on from the <emphasis>address range</emphasis> passed
to it.
<footnote><para>If no address range is specified, the default
is <emphasis>all</emphasis> lines.</para></footnote>
Specify this address range either by line number or by a
pattern to match. For example, <replaceable>3d</replaceable>
signals sed to delete line 3 of the input, and
<replaceable>/windows/d</replaceable> tells sed that
you want every line of the input containing a match to
<quote>windows</quote> deleted.</para>
<para>Of all the operations in the sed toolkit, we will focus
primarily on the three most commonly used
ones. These are <command>p</command>rinting (to
<filename>stdout</filename>), <command>d</command>eletion,
and <command>s</command>ubstitution.</para>
<table>
<title>Basic sed operators</title>
<tgroup cols="3">
<thead>
<row>
<entry>Operator</entry>
<entry>Name</entry>
<entry>Effect</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>[address-range]/p</option></entry>
<entry>print</entry>
<entry>Print [specified address range]</entry>
</row>
<row>
<entry><option>[address-range]/d</option></entry>
<entry>delete</entry>
<entry>Delete [specified address range]</entry>
</row>
<row>
<entry><option>s/pattern1/pattern2/</option></entry>
<entry>substitute</entry>
<entry>Substitute pattern2 for first instance of pattern1 in a line</entry>
</row>
<row>
<entry><option>[address-range]/s/pattern1/pattern2/</option></entry>
<entry>substitute</entry>
<entry>Substitute pattern2 for first instance of pattern1 in a
line, over <replaceable>address-range</replaceable></entry>
</row>
<row>
<entry><option>[address-range]/y/pattern1/pattern2/</option></entry>
<entry>transform</entry>
<entry>replace any character in pattern1 with the
corresponding character in pattern2, over
<replaceable>address-range</replaceable> (equivalent of
<command>tr</command>)</entry>
</row>
<row>
<entry><option>g</option></entry>
<entry>global</entry>
<entry>Operate on <emphasis>every</emphasis> pattern match
within each matched line of input</entry>
</row>
</tbody>
</tgroup>
</table>
<note><para>Unless the <option>g</option>
(<emphasis>global</emphasis>) operator is appended to a
<emphasis>substitute</emphasis> command, the substitution
operates only on the first instance of a pattern match within
each line.</para></note>
<para>From the command line and in a shell script, a sed operation may
require quoting and certain options.</para>
<para><programlisting>sed -e '/^$/d'
# The -e option causes the next string to be interpreted as an editing instruction.
# (If passing only a single instruction to "sed", the "-e" is optional.)
# The "strong" quotes ('') protect the RE characters in the instruction
# from reinterpretation as special characters by the body of the script.
# (This reserves RE expansion of the instruction for sed.)
</programlisting></para>
<note><para>Both sed and awk use the <option>-e</option> option
to specify that the following string is an instruction or set
of instructions. If there is only a single instruction contained
in the string, then this option may be omitted.</para></note>
<para><programlisting>sed -n '/xzy/p'
# The -n option tells sed to print only those lines matching the pattern.
# Otherwise all input lines would print.
# The -e option not necessary here since there is only a single editing instruction.
</programlisting></para>
<table>
<title>Examples</title>
<tgroup cols="2">
<thead>
<row>
<entry>Notation</entry>
<entry>Effect</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>8d</option></entry>
<entry>Delete 8th line of input.</entry>
</row>
<row>
<entry><option>/^$/d</option></entry>
<entry>Delete all blank lines.</entry>
</row>
<row>
<entry><option>1,/^$/d</option></entry>
<entry>Delete from beginning of input up to, and including
first blank line.</entry>
</row>
<row>
<entry><option>/Jones/p</option></entry>
<entry>Print only lines containing <quote>Jones</quote> (with
<token>-n</token> option).</entry>
</row>
<row>
<entry><option>s/Windows/Linux/</option></entry>
<entry>Substitute <quote>Linux</quote> for first instance
of<quote>Windows</quote> found in each input line.</entry>
</row>
<row>
<entry><option>s/BSOD/stability/g</option></entry>
<entry>Substitute <quote>stability</quote> for every instance
of<quote>BSOD</quote> found in each input line.</entry>
</row>
<row>
<entry><option>s/ *$//</option></entry>
<entry>Delete all spaces at the end of every line.</entry>
</row>
<row>
<entry><option>s/00*/0/g</option></entry>
<entry>Compress all consecutive sequences of zeroes into
a single zero.</entry>
</row>
<row>
<entry><option>/GUI/d</option></entry>
<entry>Delete all lines containing <quote>GUI</quote>.</entry>
</row>
<row>
<entry><option>s/GUI//g</option></entry>
<entry>Delete all instances of <quote>GUI</quote>, leaving the
remainder of each line intact.</entry>
</row>
</tbody>
</tgroup>
</table>
<note><para>Substituting a zero-length string for another is equivalent
to deleting that string within a line of input. This leaves the
remainder of the line intact. Applying <userinput>s/GUI//</userinput>
to the line <userinput>The most important parts of any application are
its GUI and sound effects</userinput> results in
<screen><computeroutput>The most important parts of any application are its and sound effects</computeroutput></screen></para></note>
<tip><para>A quick way to double-space a text file is <userinput>sed G
filename</userinput>.</para></tip>
<para>For illustrative examples of sed within shell scripts, see:
<orderedlist>
<listitem><para><xref linkend="ex3"></para></listitem>
<listitem><para><xref linkend="ex4"></para></listitem>
<listitem><para><xref linkend="ex57"></para></listitem>
<listitem><para><xref linkend="rn"></para></listitem>
<listitem><para><xref linkend="grp"></para></listitem>
<listitem><para><xref linkend="col"></para></listitem>
<listitem><para><xref linkend="behead"></para></listitem>
<listitem><para><xref linkend="tree"></para></listitem>
<listitem><para><xref linkend="stripc"></para></listitem>
<listitem><para><xref linkend="findstring"></para></listitem>
<listitem><para><xref linkend="base"></para></listitem>
<listitem><para><xref linkend="mailformat"></para></listitem>
<listitem><para><xref linkend="rnd"></para></listitem>
</orderedlist>
</para>
<para>For a more extensive treatment of sed, check the appropriate
references in the <xref linkend="biblio">.</para>
</sect1>
<!-- End sed primer -->
<sect1 id="awk">
<title>Awk</title>
<para><anchor id="awkref">Awk</para>
<para><command>Awk</command> is a full-featured text processing
language with a syntax reminiscent of <command>C</command>. While
it possesses an extensive set of operators and capabilities,
we will cover only a couple of these here - the ones most useful
for shell scripting.</para>
<para>Awk breaks each line of input passed to it into
<emphasis>fields</emphasis>. By default, a field is
a string of consecutive characters separated by <link
linkend="whitespaceref">whitespace</link>, though there are
options for changing the delimiter. Awk parses and operates on each
separate field. This makes awk ideal for handling structured text
files, especially tables, data organized into consistent chunks,
such as rows and columns.</para>
<para>Strong quoting (single quotes) and curly brackets enclose
segments of awk code within a shell script.</para>
<para><programlisting>awk '{print $3}'
# Prints field #3 to stdout.
awk '{print $1 $5 $6}'
# Prints fields #1, #5, and #6.</programlisting></para>
<para>We have just seen the awk <command>print</command> command
in action. The only other feature of awk we need to deal with
here is variables. Awk handles variables similarly to shell
scripts, though a bit more flexibly.</para>
<para><programlisting>{ total += ${column_number} }</programlisting>
This adds the value of <emphasis>column_number</emphasis> to
the running total of <quote>total</quote>. Finally, to print
<quote>total</quote>, there needs to be an <command>END</command>
command to terminate the processing.
<programlisting>END { print total }</programlisting></para>
<para>Corresponding to the <command>END</command>, there is a
<command>BEGIN</command>, for a code block to be performed before awk
starts processing its input.</para>
<para>For examples of awk within shell scripts, see:
<orderedlist>
<listitem><para><xref linkend="ex44"></para></listitem>
<listitem><para><xref linkend="redir4"></para></listitem>
<listitem><para><xref linkend="stripc"></para></listitem>
<listitem><para><xref linkend="coltotaler"></para></listitem>
<listitem><para><xref linkend="coltotaler2"></para></listitem>
<listitem><para><xref linkend="coltotaler3"></para></listitem>
<listitem><para><xref linkend="pidid"></para></listitem>
<listitem><para><xref linkend="constat"></para></listitem>
<listitem><para><xref linkend="fileinfo"></para></listitem>
<listitem><para><xref linkend="blotout"></para></listitem>
<listitem><para><xref linkend="seedingrandom"></para></listitem>
</orderedlist>
</para>
<para>That's all the awk we'll cover here, folks, but there's lots
more to learn. See the appropriate references in the <xref
linkend="biblio">.</para>
</sect1>
<!-- End awk primer -->
</appendix>
<!-- End sed/awk appendix -->
<appendix id="exitcodes">
<title>Exit Codes With Special Meanings</title>
<para><anchor id="exitcodesref"></para>
<table>
<title><quote>Reserved</quote> Exit Codes</title>
<tgroup cols="4">
<thead>
<row>
<entry>Exit Code Number</entry>
<entry>Meaning</entry>
<entry>Example</entry>
<entry>Comments</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>1</option></entry>
<entry>catchall for general errors</entry>
<entry>let "var1 = 1/0"</entry>
<entry>miscellaneous errors, such as <quote>divide by
zero</quote></entry>
</row>
<row>
<entry><option>2</option></entry>
<entry>misuse of shell builtins, according to Bash documentation</entry>
<entry></entry>
<entry>Seldom seen, usually defaults to exit code 1</entry>
</row>
<row>
<entry><option>126</option></entry>
<entry>command invoked cannot execute</entry>
<entry></entry>
<entry>permission problem or command is not an executable</entry>
</row>
<row>
<entry><option>127</option></entry>
<entry><quote>command not found</quote></entry>
<entry></entry>
<entry>possible problem with <varname>$PATH</varname> or a typo</entry>
</row>
<row>
<entry><option>128</option></entry>
<entry>invalid argument to <link linkend="exitcommandref">exit</link></entry>
<entry>exit 3.14159</entry>
<entry><command>exit</command> takes only integer args in the
range 0 - 255</entry>
</row>
<row>
<entry><option>128+n</option></entry>
<entry>fatal error signal <quote>n</quote></entry>
<entry><command>kill -9</command> <varname>$PPID</varname>of script</entry>
<entry><userinput>$?</userinput> returns 137 (128 + 9)</entry>
</row>
<row>
<entry><option>130</option></entry>
<entry>script terminated by Control-C</entry>
<entry></entry>
<entry>Control-C is fatal error signal 2, (130 = 128 + 2,
see above)</entry>
</row>
<row>
<entry><option>255</option></entry>
<entry>exit status out of range</entry>
<entry>exit -1</entry>
<entry><command>exit</command> takes only integer args in the
range 0 - 255</entry>
</row>
</tbody>
</tgroup>
</table>
<para>According to the table, exit codes 1 - 2, 126 - 165, and 255 have
special meanings, and should therefore be avoided
as user-specified exit parameters. Ending a script with
<command>exit 127</command> would certainly cause confusion
when troubleshooting (is the error a <quote>command not
found</quote> or a user-defined one?). However, many scripts use
an <command>exit 1</command> as a general bailout upon error.
Since exit code <returnvalue>1</returnvalue> signifies so many
possible errors, this might not add any additional ambiguity,
but, on the other hand, it probably would not be very informative
either.</para>
<para>There has been an attempt to systematize exit status numbers
(see <filename
class="headerfile">/usr/include/sysexits.h</filename>), but this
is intended mostly for C and C++ programmers. It would be well
to support a similar standard for scripts. The author of this
document proposes restricting user-defined exit codes to the
range 64 - 113 (in addition to <returnvalue>0</returnvalue>,
for success), to conform with the C/C++ standard. This would
still leave 50 valid codes, and make troubleshooting scripts
more straightforward.</para>
<para>All user-defined exit codes in the accompanying examples
to this document now conform to this standard, except
where overriding circumstances exist, as in <xref
linkend="tmdin">.</para>
<note><para>Issuing a <link linkend="xstatvarref">$?</link> from
the command line after a shell script exits gives results
consistent with the table above only from the Bash or
<emphasis>sh</emphasis> prompt. Running the C-shell or
<emphasis>tcsh</emphasis> may give different values in some
cases.</para></note>
</appendix>
<!-- End Reserved Exit Code appendix -->
<appendix id="ioredirintro">
<title>A Detailed Introduction to I/O and I/O Redirection</title>
<para><emphasis>written by Stephane Chazelas, and revised by the
document author</emphasis></para>
<para>A command expects the first three <link linkend="fdref">file
descriptors</link> to be available. The first, <emphasis>fd
0</emphasis> (standard input, <filename>stdin</filename>),
is for reading. The other two (<emphasis>fd 1</emphasis>,
<filename>stdout</filename> and <emphasis>fd 2</emphasis>,
<filename>stderr</filename>) are for writing.</para>
<para>There is a <filename>stdin</filename>, <filename>stdout</filename>,
and a <filename>stderr</filename> associated with each command.
<userinput>ls 2&gt;&1</userinput> means temporarily connecting the
<filename>stderr</filename> of the <command>ls</command> command to the
same <quote>resource</quote> as the shell's
<filename>stdout</filename>.</para>
<para>By convention, a command reads its input from fd 0
(<filename>stdin</filename>), prints normal output to fd
1 (<filename>stdout</filename>), and error ouput to fd 2
(<filename>stderr</filename>). If one of those three fd's is
not open, you may encounter problems:</para>
<screen>
<prompt>bash$ </prompt><userinput>cat /etc/passwd >&-</userinput>
<computeroutput>cat: standard output: Bad file descriptor</computeroutput>
</screen>
<para>For example, when <command>xterm</command> runs, it first
initializes itself. Before running the user's shell,
<command>xterm</command> opens the terminal device
(/dev/pts/&lt;n&gt; or something similar) three times.</para>
<para>At this point, Bash inherits these three file descriptors,
and each command (child process) run by Bash inherits
them in turn, except when you redirect the command. <link
linkend="ioredirref">Redirection</link> means reassigning
one of the file descriptors to another file (or a pipe, or
anything permissable). File descriptors may be reassigned
locally (for a command, a command group, a subshell, a <link
linkend="redirref">while or if or case or for loop</link>...),
or globally, for the remainder of the shell (using <link
linkend="execref">exec</link>).</para>
<para><userinput>ls &gt; /dev/null</userinput> means
running <command>ls</command> with its fd 1 connected to
<filename>/dev/null</filename>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>lsof -a -p $$ -d0,1,2</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
bash 363 bozo 0u CHR 136,1 3 /dev/pts/1
bash 363 bozo 1u CHR 136,1 3 /dev/pts/1
bash 363 bozo 2u CHR 136,1 3 /dev/pts/1</computeroutput>
<prompt>bash$ </prompt><userinput>exec 2&gt; /dev/null</userinput>
<prompt>bash$ </prompt><userinput>lsof -a -p $$ -d0,1,2</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
bash 371 bozo 0u CHR 136,1 3 /dev/pts/1
bash 371 bozo 1u CHR 136,1 3 /dev/pts/1
bash 371 bozo 2w CHR 1,3 120 /dev/null</computeroutput>
<prompt>bash$ </prompt><userinput>bash -c 'lsof -a -p $$ -d0,1,2' | cat</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
lsof 379 root 0u CHR 136,1 3 /dev/pts/1
lsof 379 root 1w FIFO 0,0 7118 pipe
lsof 379 root 2u CHR 136,1 3 /dev/pts/1</computeroutput>
<prompt>bash$ </prompt><userinput>echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2&gt;&1)"</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
lsof 426 root 0u CHR 136,1 3 /dev/pts/1
lsof 426 root 1w FIFO 0,0 7520 pipe
lsof 426 root 2w FIFO 0,0 7520 pipe</computeroutput>
</screen>
</para>
<para>This works for different types of redirection.</para>
<para><userinput>Exercise:</userinput> analyze the following script.
<programlisting>#! /usr/bin/env bash
mkfifo /tmp/fifo1 /tmp/fifo2
while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 &
exec 7> /tmp/fifo1
exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)
exec 3>&1
(
(
(
while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr | tee /dev/fd/4 | tee /dev/fd/5 | tee /dev/fd/6 >&7 &
exec 3> /tmp/fifo2
echo 1st, to stdout
sleep 1
echo 2nd, to stderr >&2
sleep 1
echo 3rd, to fd 3 >&3
sleep 1
echo 4th, to fd 4 >&4
sleep 1
echo 5th, to fd 5 >&5
sleep 1
echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5
sleep 1
echo 7th, to fd 6 >&6
sleep 1
echo 8th, to fd 7 >&7
sleep 1
echo 9th, to fd 8 >&8
) 4>&1 >&3 3>&- | while read a; do echo "FD4: $a"; done 1>&3 5>&- 6>&-
) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&-
) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&-
rm -f /tmp/fifo1 /tmp/fifo2
# For each command and subshell, figure out which fd points to what.
exit 0</programlisting>
</para>
</appendix>
<!-- A Detailed Introduction to I/O and I/O Redirection -->
<appendix id="localization">
<title>Localization</title>
<para>Localization is an undocumented Bash feature.</para>
<para>A localized shell script echoes its text output in the
language defined as the system's locale. A Linux user in Berlin,
Germany, would get script output in German, whereas his cousin
in Berlin, Maryland, would get output from the same script in
English.</para>
<para>To create a localized script, use the following template to
write all messages to the user (error messages, prompts,
etc.).</para>
<para>
<programlisting>#!/bin/bash
# localized.sh
E_CDERROR=65
error()
{
printf "$@" >&2
exit $E_CDERROR
}
cd $var || error $"Can't cd to %s." "$var"
read -p $"Enter the value: " var
# ...</programlisting>
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>bash -D localized.sh</userinput>
<computeroutput>"Can't cd to %s."
"Enter the value: "</computeroutput></screen>
This lists all the localized text. (The <option>-D</option>
option lists double-quoted strings prefixed by a <token>$</token>,
without executing the script.)</para>
<para>
<screen><prompt>bash$ </prompt><userinput>bash --dump-po-strings localized.sh</userinput>
<computeroutput>#: a:6
msgid "Can't cd to %s."
msgstr ""
#: a:7
msgid "Enter the value: "
msgstr ""</computeroutput></screen>
The <option>--dump-po-strings</option> option to Bash
resembles the <option>-D</option> option, but uses <link
linkend="gettextref">gettext</link> <quote>po</quote> format.
</para>
<para>Now, build a <filename>language.po</filename>
file for each language that the script will be translated
into, specifying the <replaceable>msgstr</replaceable>. As an
example:</para>
<para>fr.po:
<programlisting>#: a:6
msgid "Can't cd to %s."
msgstr "Impossible de se positionner dans le r<>pertoire %s."
#: a:7
msgid "Enter the value: "
msgstr "Entrez la valeur : "</programlisting>
</para>
<para>Then, run <command>msgfmt</command>.</para>
<para><userinput>msgfmt -o localized.sh.mo fr.po</userinput></para>
<para>Place the resulting <filename>localized.sh.mo</filename> file in the
<filename class="directory">/usr/local/share/locale/fr/LC_MESSAGES</filename>
directory, and at the beginning of the script, insert the lines:
<programlisting>TEXTDOMAINDIR=/usr/local/share/locale
TEXTDOMAIN=localized.sh</programlisting>
</para>
<para>If a user on a French system runs the script, she will get
French messages.</para>
<note>
<para>With older versions of Bash or other shells, localization requires
<link linkend="gettextref">gettext</link>, using the
<option>-s</option> option. In this case, the script becomes:</para>
<para><anchor id="gettextexample">
<programlisting>#!/bin/bash
# localized.sh
E_CDERROR=65
error() {
local format=$1
shift
printf "$(gettext -s "$format")" "$@" >&2
exit $E_CDERROR
}
cd $var || error "Can't cd to %s." "$var"
read -p "$(gettext -s "Enter the value: ")" var
# ...</programlisting>
</para>
</note>
<para>The <varname>TEXTDOMAIN</varname> and
<varname>TEXTDOMAINDIR</varname> variables need to be exported
to the environment.</para>
<para>---</para>
<para>This appendix written by Stephane Chazelas.</para>
</appendix>
<!-- Localization -->
<appendix id="sample-bashrc">
<title>A Sample <filename>.bashrc</filename> File</title>
<para>The <filename>~/.bashrc</filename> file determines the
behavior of interactive shells. A good look at this file can
lead to a better understanding of Bash.</para>
<para>Emmanuel Rouat contributed the following very elaborate
<filename>.bashrc</filename> file, written for a Linux system.
Study this file carefully, and feel free to reuse code snippets
and functions from it in your own <filename>.bashrc</filename>
file and even in your scripts.</para>
<example id="bashrc">
<title>Sample <filename>.bashrc</filename> file</title>
<programlisting>&bashrc;</programlisting>
</example>
</appendix>
<!-- End Sample .bashrc File appendix -->
<appendix id="dosbatch">
<title>Converting DOS Batch Files to Shell Scripts</title>
<para>Quite a number of programmers learned scripting on a PC running
DOS. Even the crippled DOS batch file language allowed writing some
fairly powerful scripts and applications, though they often required
extensive kludges and workarounds. Occasionally, the need still
arises to convert an old DOS batch file to a UNIX shell script. This
is generally not difficult, as DOS batch file operators are only a
limited subset of the equivalent shell script ones.</para>
<table>
<title>Batch file keywords / variables / operators, and their shell equivalents</title>
<tgroup cols="3">
<thead>
<row>
<entry>Batch File Operator</entry>
<entry>Shell Script Equivalent</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>%</option></entry>
<entry>$</entry>
<entry>command-line parameter prefix</entry>
</row>
<row>
<entry><option>/</option></entry>
<entry>-</entry>
<entry>command option flag</entry>
</row>
<row>
<entry><option>\</option></entry>
<entry>/</entry>
<entry>directory path separator</entry>
</row>
<row>
<entry><option>==</option></entry>
<entry>=</entry>
<entry>(equal-to) string comparison test</entry>
</row>
<row>
<entry><option>!==!</option></entry>
<entry>!=</entry>
<entry>(not equal-to) string comparison test</entry>
</row>
<row>
<entry><option>|</option></entry>
<entry>|</entry>
<entry>pipe</entry>
</row>
<row>
<entry><option>@</option></entry>
<entry>set <option>+v</option></entry>
<entry>do not echo current command</entry>
</row>
<row>
<entry><option>*</option></entry>
<entry>*</entry>
<entry>filename <quote>wild card</quote></entry>
</row>
<row>
<entry><option>&gt;</option></entry>
<entry>&gt;</entry>
<entry>file redirection (overwrite)</entry>
</row>
<row>
<entry><option>&gt;&gt;</option></entry>
<entry>&gt;&gt;</entry>
<entry>file redirection (append)</entry>
</row>
<row>
<entry><option>&lt;</option></entry>
<entry>&lt;</entry>
<entry>redirect <filename>stdin</filename></entry>
</row>
<row>
<entry><option>%VAR%</option></entry>
<entry>$VAR</entry>
<entry>environmental variable</entry>
</row>
<row>
<entry><option>REM</option></entry>
<entry>#</entry>
<entry>comment</entry>
</row>
<row>
<entry><option>NOT</option></entry>
<entry>!</entry>
<entry>negate following test</entry>
</row>
<row>
<entry><option>NUL</option></entry>
<entry><filename>/dev/null</filename></entry>
<entry><quote>black hole</quote> for burying command output</entry>
</row>
<row>
<entry><option>ECHO</option></entry>
<entry>echo</entry>
<entry>echo (many more option in Bash)</entry>
</row>
<row>
<entry><option>ECHO.</option></entry>
<entry>echo</entry>
<entry>echo blank line</entry>
</row>
<row>
<entry><option>ECHO OFF</option></entry>
<entry>set <option>+v</option></entry>
<entry>do not echo command(s) following</entry>
</row>
<row>
<entry><option>FOR %%VAR IN (LIST) DO</option></entry>
<entry>for var in [list]; do</entry>
<entry><quote>for</quote> loop</entry>
</row>
<row>
<entry><option>:LABEL</option></entry>
<entry>none (unnecessary)</entry>
<entry>label</entry>
</row>
<row>
<entry><option>GOTO</option></entry>
<entry>none (use a function)</entry>
<entry>jump to another location in the script</entry>
</row>
<row>
<entry><option>PAUSE</option></entry>
<entry>sleep</entry>
<entry>pause or wait an interval</entry>
</row>
<row>
<entry><option>CHOICE</option></entry>
<entry>case or select</entry>
<entry>menu choice</entry>
</row>
<row>
<entry><option>IF</option></entry>
<entry>if</entry>
<entry>if-test</entry>
</row>
<row>
<entry><option>IF EXIST <replaceable>FILENAME</replaceable></option></entry>
<entry>if [ -e filename ]</entry>
<entry>test if file exists</entry>
</row>
<row>
<entry><option>IF !%N==!</option></entry>
<entry>if [ -z "$N" ]</entry>
<entry>if replaceable parameter <quote>N</quote> not present</entry>
</row>
<row>
<entry><option>CALL</option></entry>
<entry>source or . (dot operator)</entry>
<entry><quote>include</quote> another script</entry>
</row>
<row>
<entry><option>COMMAND /C</option></entry>
<entry>source or . (dot operator)</entry>
<entry><quote>include</quote> another script (same as
CALL)</entry>
</row>
<row>
<entry><option>SET</option></entry>
<entry>export</entry>
<entry>set an environmental variable</entry>
</row>
<row>
<entry><option>SHIFT</option></entry>
<entry>shift</entry>
<entry>left shift command-line argument list</entry>
</row>
<row>
<entry><option>SGN</option></entry>
<entry>-lt or -gt</entry>
<entry>sign (of integer)</entry>
</row>
<row>
<entry><option>ERRORLEVEL</option></entry>
<entry>$?</entry>
<entry>exit status</entry>
</row>
<row>
<entry><option>CON</option></entry>
<entry><filename>stdin</filename></entry>
<entry><quote>console</quote> (<filename>stdin</filename>)</entry>
</row>
<row>
<entry><option>PRN</option></entry>
<entry><filename>/dev/lp0</filename></entry>
<entry>(generic) printer device</entry>
</row>
<row>
<entry><option>LPT1</option></entry>
<entry><filename>/dev/lp0</filename></entry>
<entry>first printer device</entry>
</row>
<row>
<entry><option>COM1</option></entry>
<entry><filename>/dev/ttyS0</filename></entry>
<entry>first serial port</entry>
</row>
</tbody>
</tgroup>
</table>
<para>Batch files usually contain DOS commands. These must be
translated into their UNIX equivalents in order to convert a
batch file into a shell script.</para>
<table>
<title>DOS Commands and Their UNIX Equivalents</title>
<tgroup cols="3">
<thead>
<row>
<entry>DOS Command</entry>
<entry>UNIX Equivalent</entry>
<entry>Effect</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>ASSIGN</option></entry>
<entry>ln</entry>
<entry>link file or directory</entry>
</row>
<row>
<entry><option>ATTRIB</option></entry>
<entry>chmod</entry>
<entry>change file permissions</entry>
</row>
<row>
<entry><option>CD</option></entry>
<entry>cd</entry>
<entry>change directory</entry>
</row>
<row>
<entry><option>CHDIR</option></entry>
<entry>cd</entry>
<entry>change directory</entry>
</row>
<row>
<entry><option>CLS</option></entry>
<entry>clear</entry>
<entry>clear screen</entry>
</row>
<row>
<entry><option>COMP</option></entry>
<entry>diff, comm, cmp</entry>
<entry>file compare</entry>
</row>
<row>
<entry><option>COPY</option></entry>
<entry>cp</entry>
<entry>file copy</entry>
</row>
<row>
<entry><option>Ctl-C</option></entry>
<entry>Ctl-C</entry>
<entry>break (signal)</entry>
</row>
<row>
<entry><option>Ctl-Z</option></entry>
<entry>Ctl-D</entry>
<entry>EOF (end-of-file)</entry>
</row>
<row>
<entry><option>DEL</option></entry>
<entry>rm</entry>
<entry>delete file(s)</entry>
</row>
<row>
<entry><option>DELTREE</option></entry>
<entry>rm -rf</entry>
<entry>delete directory recursively</entry>
</row>
<row>
<entry><option>DIR</option></entry>
<entry>ls -l</entry>
<entry>directory listing</entry>
</row>
<row>
<entry><option>ERASE</option></entry>
<entry>rm</entry>
<entry>delete file(s)</entry>
</row>
<row>
<entry><option>EXIT</option></entry>
<entry>exit</entry>
<entry>exit current process</entry>
</row>
<row>
<entry><option>FC</option></entry>
<entry>comm, cmp</entry>
<entry>file compare</entry>
</row>
<row>
<entry><option>FIND</option></entry>
<entry>grep</entry>
<entry>find strings in files</entry>
</row>
<row>
<entry><option>MD</option></entry>
<entry>mkdir</entry>
<entry>make directory</entry>
</row>
<row>
<entry><option>MKDIR</option></entry>
<entry>mkdir</entry>
<entry>make directory</entry>
</row>
<row>
<entry><option>MORE</option></entry>
<entry>more</entry>
<entry>text file paging filter</entry>
</row>
<row>
<entry><option>MOVE</option></entry>
<entry>mv</entry>
<entry>move</entry>
</row>
<row>
<entry><option>PATH</option></entry>
<entry>$PATH</entry>
<entry>path to executables</entry>
</row>
<row>
<entry><option>REN</option></entry>
<entry>mv</entry>
<entry>rename (move)</entry>
</row>
<row>
<entry><option>RENAME</option></entry>
<entry>mv</entry>
<entry>rename (move)</entry>
</row>
<row>
<entry><option>RD</option></entry>
<entry>rmdir</entry>
<entry>remove directory</entry>
</row>
<row>
<entry><option>RMDIR</option></entry>
<entry>rmdir</entry>
<entry>remove directory</entry>
</row>
<row>
<entry><option>SORT</option></entry>
<entry>sort</entry>
<entry>sort file</entry>
</row>
<row>
<entry><option>TIME</option></entry>
<entry>date</entry>
<entry>display system time</entry>
</row>
<row>
<entry><option>TYPE</option></entry>
<entry>cat</entry>
<entry>output file to <filename>stdout</filename></entry>
</row>
<row>
<entry><option>XCOPY</option></entry>
<entry>cp</entry>
<entry>(extended) file copy</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>Virtually all UNIX and shell operators and commands have
many more options and enhancements than their DOS and batch file
equivalents. Many DOS batch files rely on auxiliary utilities,
such as <command>ask.com</command>, a crippled counterpart to
<link linkend="readref">read</link>.</para>
<para>DOS supports a very limited and incompatible subset of
filename <link linkend="globbingref">wildcard expansion</link>,
recognizing only the <token>*</token> and <token>?</token>
characters.</para>
</note>
<para>Converting a DOS batch file into a shell script is generally
straightforward, and the result ofttimes reads better than the
original.</para>
<example id="VIEWDAT">
<title>VIEWDATA.BAT: DOS Batch File</title>
<programlisting>&VIEWDAT;</programlisting>
</example>
<para>The script conversion is somewhat of an improvement.</para>
<example id="viewdata">
<title>viewdata.sh: Shell Script Conversion of VIEWDATA.BAT</title>
<programlisting>&viewdata;</programlisting>
</example>
<para>Ted Davis' <ulink url="http://www.maem.umr.edu/~batch/">Shell
Scripts on the PC</ulink> site has a set of comprehensive
tutorials on the old-fashioned art of batch file
programming. Certain of his ingenious techniques could conceivably
have relevance for shell scripts.</para>
</appendix>
<!-- End DOS Batch File Conversion appendix -->
<appendix id="copyright">
<title>Copyright</title>
<para>The <quote>Advanced Bash-Scripting Guide</quote> is copyright,
(c) 2000, by Mendel Cooper. This document may only be distributed
subject to the terms and conditions set forth in the <ulink
url="http://www.linuxdoc.org/manifesto.html">LDP License</ulink>
These are very liberal terms, and they should not hinder any
legitimate distribution or use of this document. The author
especially encourages the use of this document, or portions thereof,
for instructional purposes.</para>
<para>Hyun Jin Cha has done a <ulink
url="http://kldp.org/HOWTO/html/Adv-Bash-Scr-HOWTO/index.html">Korean
translation</ulink> of an earlier version of this document. If you
wish to translate it into another language, please feel free to do
so, subject to the terms stated above. The author would appreciate
being notified of such efforts.</para>
<para>If this document is printed as a hard-copy book, the author
requests a courtesy copy. This is a request, not a
requirement.</para>
</appendix> <!-- End Copyright appendix -->
<!-- Uncomment line below to generate index. -->
<!--
&indice;
-->
</book>
<!--
</article>
-->
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-indent-step:2
sgml-indent-data:t
End:
-->