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

17253 lines
552 KiB
Plaintext
Raw Normal View History

2001-07-10 14:25:50 +00:00
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!--
Uncomment line below to generate index.
-->
<!ENTITY doc-index SYSTEM "index.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 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 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 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>v0.4, 08 July 2001</pubdate>
<revhistory>
<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.3</revnumber>
<date>12 February 2001</date>
<authorinitials>mc</authorinitials>
<revremark>Another major update.</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.1</revnumber>
<date>14 June 2000</date>
<authorinitials>mc</authorinitials>
<revremark>Initial release.</revremark>
</revision>
</revhistory>
<abstract>
<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. Still, it is a work in progress. The intention
is to add much supplementary material in future updates to this
document, as it evolves into a comprehensive book that matches
or surpasses any of the shell scripting manuals in print.</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-HOWTO-0.4.tar.gz">
the author's home site</ulink>.</para>
<para>This is a major update on version 0.3. -- more bugs swatted,
plus much additional material and example scripts added.
This project has now reached the equivalent of a 300-page book.
See <filename>NEWS</filename> for a revision history.</para>
</abstract>
</bookinfo>
<chapter id="why-shell">
<title>Why Shell Programming?</title>
<para>The shell is a command interpreter. It is the insulating layer between
the operating system kernel and the user. Yet, it is also a fairly
powerful programming language. A shell program, called a
<firstterm>
<indexterm>
<primary>script</primary>
</indexterm>
script
</firstterm>,
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>
<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 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 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 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 portable
to non-Linux machines, though you may have to sacrifice a few
Bash-specific features (the script will conform to the POSIX
<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 -->
<chapter id="tutorial">
<title>Tutorial / Reference</title>
<epigraph>
<attribution>Chet Ramey</attribution>
<para>...there are dark corners in the Bourne shell, and people use all
of them.</para>
</epigraph>
<sect1 id="exit-status">
<title>Exit and Exit Status</title>
<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>
</sect1> <!-- exit and exit status -->
<sect1 id="special-chars">
<title>Special characters used in shell scripts</title>
<variablelist>
<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>
</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>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><anchor id="dotref"><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>
<formalpara><title><quote>dot</quote> command</title>
<para>Equivalent to <link linkend="sourceref">source</link>
(see <xref linkend="ex38">). This is a bash
<link linkend="builtinref">builtin</link>.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>"</token></term>
<listitem><formalpara><title><link linkend="dblquo">partial
quoting</link></title>
<para><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><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="escp">escape</link></title>
<para><emphasis>\X</emphasis> <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>
</listitem>
</varlistentry>
<varlistentry>
<term><token>`</token></term>
<listitem><formalpara><title><link
linkend="commandsubref">command substitution</link></title>
<para><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><anchor id="nullref"><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>
<formalpara><title>null command</title>
<para>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>. Note
that <token>:</token> is a Bash builtin, and its
<link linkend="exitstatusref">exit status</link> is
<returnvalue>0</returnvalue>.</para>
</formalpara>
<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>,
see <xref linkend="ex6">:</para>
<para><programlisting>: ${HOSTNAME?} ${USER?} ${MAIL?}
#Prints error message if one or more of essential environmental variables not set.</programlisting>
</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><anchor id="notref"><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><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>
</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>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>
</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>positional parameters</title>
<para>The list of the <link linkend="appref">positional
parameters</link> (command-line arguments) passed to a
script.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>$_</token></term>
<listitem><formalpara><title>last argument</title>
<para>Internal shell variable set to last argument
of previous command executed. See <xref
linkend="uscref">.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whitespaceref">Whitespace</term>
<listitem><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>
</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><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>
<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><anchor id="codeblockref"><token>{}</token></term>
<indexterm>
<primary>{}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>block of code</primary>
</indexterm>
<listitem><formalpara><title>Block of code</title>
<para>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>
</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><anchor id="piperef"><token>|</token></term>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>|</secondary>
</indexterm>
<indexterm>
<primary>pipe</primary>
</indexterm>
<listitem><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</programlisting>
passes the output of <quote>echo ls -l</quote> to the shell,
with the same result as a simple <quote>ls -l</quote>.</para>
<para><programlisting>cat *.lst | sort | uniq</programlisting>
sorts the output of all the <filename>.lst</filename> files and
deletes duplicate lines.</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.</para>
<para>
<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>-</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></para>
</formalpara>
<para><anchor id="coxex"><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><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>home directory</title>
<para>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>Blank lines</term>
<listitem><para>Blank lines have no effect on the action of a script, and are therefore useful
for visually separating functional sections of the script.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Special characters used in shell scripts -->
<sect1 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>
<variablelist>
<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>
<formalpara><title>variable substitution</title>
<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>
</formalpara>
<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>
<sect2 id="Parameter-Substitution">
<title>Parameter Substitution</title>
<para><anchor id="paramsubref"></para>
<variablelist>
<varlistentry>
<term>
<userinput>${parameter}</userinput></term>
<listitem>
<para>Same as <replaceable>$parameter</replaceable>, i.e.,
value of the variable <replaceable>parameter</replaceable>.</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`, but 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><anchor id="defparam"><userinput>${parameter=default}</userinput></term>
<term><userinput>${parameter:=default}</userinput></term>
<listitem><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+otherwise}</userinput></term>
<term><userinput>${parameter:+otherwise}</userinput></term>
<listitem><para>If parameter set, use 'otherwise", 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, as above.</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>
<varlistentry>
<term><userinput>${#var}</userinput></term>
<listitem>
<para>String length (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></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>back end</replaceable>
of <varname>$var</varname>.
</para></listitem>
</varlistentry>
</variablelist>
<para>Version 2 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>
<varlistentry>
<term><userinput>${var:pos}</userinput></term>
<listitem>
<para>Variable <replaceable>var</replaceable> expanded,
starting from offset <replaceable>pos</replaceable>.
</para>
<para>This expansion adopted from <emphasis>ksh93</emphasis>.</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>
<para>This expansion adopted from <emphasis>ksh93</emphasis>.</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>
<para>All matches of <replaceable>patt</replaceable>,
within <replaceable>var</replaceable> replaced with
<replaceable>replacement</replaceable>.</para>
<para>Similar to above, if
<replaceable>replacement</replaceable> is omitted,
then all occurrences <replaceable>patt</replaceable>
are replaced by <emphasis>nothing</emphasis>, that
is, deleted.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex7">
<title>Using pattern matching to parse arbitrary strings</title>
<programlisting>&ex7;</programlisting>
</example>
</sect2> <!-- Parameter Substitution -->
</sect1> <!-- Variables -->
<sect1 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>
<title>Special meanings of certain escaped characters used with
<command>echo</command> and <command>sed</command></title>
<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>
</sect1> <!-- Quoting -->
<sect1 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>
<itemizedlist>
<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>Bash also introduces the <link linkend="dblbrackets">[[ ... ]]</link>
construct, 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>
<varlistentry>
<term><token>elif</token></term>
<listitem><para> This is a contraction for <token>else if</token>.
The effect is to nest an inner <token>if/then</token> construction
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></note>
<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>
<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>. No filename
expansion or word splitting take place between <token>[[</token>
and <token>]]</token>, but there is parameter expansion and
command substitution.
<programlisting>file=/etc/passwd
if [[ -e $file ]]
then
echo "Password file exists."
fi</programlisting>
</para>
<tip>
<para>Using the newer <command>[[ ... ]]</command> test construct,
rather than <command>[ ... ]</command> can prevent many logic
errors in scripts.</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>
<sect2>
<title>File test operators</title>
<variablelist>
<title>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 should not be set 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>
</sect2> <!-- File test operators -->
<sect2>
<title>Comparison operators (binary)</title>
<variablelist>
<title><anchor id="equalref">integer comparison</title>
<varlistentry>
<term><token>-eq</token></term>
<listitem>
<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>
<title><anchor id="equalsignref">string comparison</title>
<varlistentry>
<term><token>=</token></term>
<listitem>
<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><token>\&lt;</token></term>
<listitem>
<para>is less than, in ASCII alphabetical order</para>
<para><userinput>if [ "$a" \&lt; "$b" ]</userinput></para>
<para>Note that the <quote>&lt;</quote> needs to be
escaped.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>\&gt;</token></term>
<listitem>
<para>is greater than, in ASCII alphabetical order</para>
<para><userinput>if [ "$a" \&gt; "$b" ]</userinput></para>
<para>Note that the <quote>&gt;</quote> needs to be
escaped.</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>
<title>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
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>
</sect2> <!-- Comparison operators (binary) -->
<sect2 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>
</sect2> <!-- Nested if/then Tests -->
</sect1> <!-- Tests -->
<sect1 id="operations">
<title>Operations and Related Topics</title>
<sect2 id="ops">
<title>Operators</title>
<variablelist><title>assignment</title>
<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><title>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><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>
<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>
<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><title>relational tests</title>
<varlistentry>
<term><token><</token></term>
<indexterm>
<primary><</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary><</secondary>
</indexterm>
<indexterm>
<primary>less than</primary>
</indexterm>
<listitem><para>less than</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>></token></term>
<indexterm>
<primary>></primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>></secondary>
</indexterm>
<indexterm>
<primary>greater than</primary>
</indexterm>
<listitem><para>greater than</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token><=</token></term>
<indexterm>
<primary><=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary><=</secondary>
</indexterm>
<indexterm>
<primary>less than or equal to</primary>
</indexterm>
<listitem><para>less than or equal to</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>=</token></term>
<indexterm>
<primary>>=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>>=</secondary>
</indexterm>
<indexterm>
<primary>greater than or equal to</primary>
</indexterm>
<listitem><para>greater than or equal to</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>==</token></term>
<indexterm>
<primary>==</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>==</secondary>
</indexterm>
<indexterm>
<primary>equal to</primary>
</indexterm>
<listitem><para>equal to (test)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>!=</token></term>
<indexterm>
<primary>!=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>!=</secondary>
</indexterm>
<indexterm>
<primary>not equal to</primary>
</indexterm>
<listitem><para>not equal to</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist><title>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...</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...</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>
</sect2>
<sect2 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>
</sect2>
</sect1> <!-- Operations -->
<sect1 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>
<variablelist>
<varlistentry>
<term><replaceable>Internal (<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><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>$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>$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 within last entry -->
</listitem>
</varlistentry>
<!-- Last entry of intrinsic BASH variables -->
<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"> and
<xref linkend="online">)</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">.</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>
<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 $_ # bash
# Just executed "bash" to run script.
du
echo $_ # du
ls -al
echo $_ # -al
# (last argument)
:
echo $_ # :</programlisting></example>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>variable assignment</replaceable></term>
<listitem><para>Initializing or changing the value of a variable</para>
</listitem>
</varlistentry>
<varlistentry>
<term><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>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>
<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 man page 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>, whois 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>
<sect2>
<indexterm>
<primary><anchor id="declareref">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>
keywords (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>
<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 var2</programlisting></para>
<para>The script treats subsequent occurrences of
<varname>var2</varname> as an integer. 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>
</variablelist>
<example id="ex20">
<title>Using <command>declare</command> to type variables</title>
<programlisting>&ex20;</programlisting>
</example>
</sect2> <!-- Typing variables: declare or typeset -->
<sect2 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). 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>
</sect2> <!-- Indirect References to Variables -->
<sect2>
<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).</para></note>
</sect2> <!-- RANDOM: generate random integer -->
<sect2 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>
</sect2> <!-- The Double Parentheses Construct -->
</sect1> <!-- Variables Revisited -->
<sect1 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>
<sect2 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>
<varlistentry>
<term><anchor id="forloopref"><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>
<note><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></note>
<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>
</sect2> <!-- Loops -->
<sect2 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>
</sect2> <!-- Nested Loops -->
<sect2 id="loopcontrol">
<title>Loop Control Commands</title>
<variablelist>
<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>
</sect2> <!-- Loop Control Commands -->
<sect2 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 a
code block. Like loops, however, they direct program flow according
to conditions at the top or bottom of the block.</para>
<variablelist>
<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>
</sect2> <!-- Testing and Branching -->
</sect1> <!-- Loops -->
<sect1 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>
<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>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>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.
<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>
</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> man page (of the system command)
for in-depth coverage.</para>
<caution><para>Older versions of <command>bash</command>
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>
<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="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>
<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><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="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><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><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>
<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>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="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>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>
<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><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>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>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><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>
<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>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>
<sect2>
<title>Job Control Commands</title>
<variablelist>
<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><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>
</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>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>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><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>. The <option>-a</option>
option lists all the shell builtins, indicating whether
or not they are enabled.</para>
</listitem>
</varlistentry>
</variablelist>
</sect2> <!-- Job Control Commands -->
</sect1> <!-- Internal Commands and Builtins -->
<sect1 id="external">
<title>External Filters, Programs and Commands</title>
<para>This is a descriptive listing of standard UNIX commands useful in shell
scripts. The power of scripts comes from coupling system commands and
shell directives with simple programming constructs.</para>
<sect2 id="basic">
<title>Basic Commands</title>
<variablelist>
<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.</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>
</sect2> <!-- End Basic Commands -->
<sect2 id="moreadv">
<title>Complex Commands</title>
<variablelist>
<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 '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
# 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 man page 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 arg1 operation arg2 ...</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>
<para>Note that <link linkend="nullref">:</link> can
substitute for <command>match</command>. Indeed,
<userinput>b=`expr $a : [0-9]*`</userinput> is the
exact equivalent of <userinput>b=`expr match $a
[0-9]*`</userinput> in the above example.</para>
</listitem>
</varlistentry>
</variablelist>
</sect2> <!-- End Complex Commands -->
<sect2 id="timedate">
<title>Time / Date Commands</title>
<variablelist>
<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.</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>
</sect2> <!-- End Time / Date Commands -->
<sect2 id="textproc">
<title>Text Processing Commands</title>
<variablelist>
<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><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><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>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>Man pages</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>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 <emphasis>wc</emphasis> 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>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>
<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>
</variablelist>
</sect2> <!-- End Text Processing Commands -->
<sect2 id="filearchiv">
<title>File and Archiving Commands</title>
<variablelist>
<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>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>
</listitem>
</varlistentry>
<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><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>
<varlistentry>
<term><command>shar</command></term>
<indexterm>
<primary>shar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>shar</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>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><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>man page</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>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 file
name, printing only the path information.</para>
<note>
<para><command>basename</command> and <command>dirname</command>
can operate on any arbitrary string. The filename given as an
argument does not need to refer to an existing file.</para>
</note>
<example id="ex35">
<title><command>basename</command> and <command>dirname</command></title>
<programlisting>&ex35;</programlisting>
</example>
</listitem>
</varlistentry>
<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>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>
<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>
<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>
<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>
</sect2> <!-- End File and Archiving Commands -->
<sect2 id="communications">
<title>Communications Commands</title>
<variablelist>
<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>rcp</command></term>
<indexterm>
<primary>rcp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rcp</secondary>
</indexterm>
<listitem>
<para><quote>Remote copy</quote>, 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 an <command>expect</command> script.</para>
</listitem>
</varlistentry>
<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="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>
<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>
</variablelist>
</sect2> <!-- End Communications Commands -->
<sect2 id="terminalccmds">
<title>Terminal Control Commands</title>
<variablelist>
<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><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>
</sect2> <!-- End Terminal Control Commands -->
<sect2 id="mathc">
<title>Math Commands</title>
<variablelist>
<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>
</sect2> <!-- End Math Commands -->
<sect2 id="extmisc">
<title>Miscellaneous Commands</title>
<variablelist>
<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><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><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,
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>
</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>
</sect2> <!-- End Miscellaneous Commands -->
</sect1> <!-- External Filters, Programs and Commands -->
<sect1 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>
<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>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><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>
</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><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>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>
<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="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>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>
<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><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>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>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><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>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
permitting only restricted access (or no access) to other
processes. 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>
</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 daemon (background process) and executes scheduled
entries from <filename>/etc/crontab</filename>.</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>
<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 telnetting 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>
<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>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>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="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>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>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>
<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><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>
</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><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>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="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>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>
</listitem>
</varlistentry>
<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>
<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>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><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>
<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>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>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><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><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><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>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>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>
<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>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>
<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>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>
<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>
</sect1> <!-- System and Administrative Commands -->
<sect1 id="commandsub">
<title>Command Substitution</title>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>`</secondary>
</indexterm>
<variablelist>
<varlistentry>
<term>Command substitution</term>
<indexterm>
<primary>command</primary>
<secondary>substitution</secondary>
</indexterm>
<listitem>
<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>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Command Substitution -->
<sect1 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 backticks, double parentheses,
or <command>let</command>.</para>
<variablelist>
<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 <link linkend="letref">let</link></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>
</sect1> <!-- Arithmetic Expansion -->
<sect1 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></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>
<title>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>
<sect2><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>
</sect2><!-- Using exec For Redirection -->
<sect2><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>
</sect2><!-- Redirecting Code Blocks -->
<sect2><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>
</sect2><!-- Log Files -->
</sect1> <!-- I/O Redirection -->
<sect1 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 <command>ftp</command>, <command>telnet</command>, 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>
</sect1> <!-- Here Documents -->
<sect1 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>
</sect1> <!-- Recess Time -->
<sect1 id="regexp">
<title>Regular Expressions</title>
<para><anchor id="regexref"></para>
<para>In order 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="sedref">sed</link> and <link
linkend="awkref">awk</link> interpret and use REs.</para>
<sect2><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 (<token>"</token>), for example, may denote
speech by a person, ditto, 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 or 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>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>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>
<formalpara><title>Extended REs</title>
<para>Used in <command>egrep</command>, <link linkend="awkref">awk</link>,
and <link linkend="perlref">Perl</link></para></formalpara>
<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>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>
<para>"Sed & Awk", by Dougherty and Robbins gives a very complete
and lucid treatment of REs (see the <xref
linkend="biblio">).</para>
</sect2> <!-- A Brief Introduction to Regular Expressions -->
<sect2>
<title>Using REs in Scripts</title>
<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>
<caution>
<para>Bash itself does not recognize Regular Expressions. In
scripts, commands and utilities, such as <command>sed</command>
and <command>awk</command>, interpret RE's.</para>
<para><anchor id="globbingref">Bash does carry out filename
expansion, but does <emphasis>not</emphasis> use the
standard RE set. Instead, it recognizes and expands
wildcards, a process known as <quote>globbing</quote>.
Globbing interprets the standard wildcard characters,
<token>*</token> and <token>?</token>, in addition to certain
other special characters. There are some important limitations
on wildcard characters in globbing, however. Strings containing
<replaceable>*</replaceable> will not match filenames that start
with a dot (such as <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>See <xref linkend="listglob">.</para>
</caution>
</sect2> <!-- Using REs in Scripts -->
</sect1> <!-- Regular Expressions -->
<!-- *************************** -->
<sect1 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>
<itemizedlist>
<listitem>
<para>( command1; command2; command3; ... )</para>
<para>A command list embedded between
<replaceable>parentheses</replaceable> runs as a
subshell.</para>
</listitem>
</itemizedlist>
<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>
</sect1> <!-- Subshells -->
<sect1 id="restricted-sh">
<title>Restricted Shells</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>
<para>Disabled commands in restricted shells
<itemizedlist>
<listitem>
<para>Using <replaceable>cd</replaceable> to change the working
directory.</para>
</listitem><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><listitem>
<para>Reading or changing the <replaceable>$SHELLOPTS</replaceable>,
shell environmental options.</para>
</listitem><listitem>
<para>Output redirection.</para>
</listitem><listitem>
<para>Invoking commands containing one or more
<token>/'s</token>.</para>
</listitem><listitem>
<para>Invoking <emphasis>exec</emphasis> to substitute a different
process for the shell.</para>
</listitem><listitem>
<para>Various other commands that would enable monkeying
with or attempting to subvert the script for an unintended
purpose.</para>
</listitem><listitem>
<para>Getting out of restricted mode within the script.</para>
</listitem>
</itemizedlist></para>
<example id="restricted">
<title>Running a script in restricted mode</title>
<programlisting>&restricted;</programlisting>
</example>
</sect1> <!-- Restricted Shells -->
<sect1 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>
<itemizedlist>
<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>
</listitem>
</itemizedlist>
<note><para>There are <emphasis>no</emphasis> spaces between the
parentheses and the <quote><</quote> or <quote>></quote>.
Space there would give an error message.</para></note>
<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>
</sect1> <!-- Process Substitution -->
<sect1 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>
<sect2>
<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>
<varlistentry>
<term>exit status</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 integer 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>
</listitem>
</varlistentry>
<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>
</sect2> <!-- Complex Functions and Function Complexities -->
<sect2>
<title>Local Variables and Recursion</title>
<variablelist>
<varlistentry>
<term><anchor id="localref"><replaceable>local variables</replaceable></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. 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>
</sect2> <!-- Local Variables and Recursion -->
</sect1> <!-- Functions -->
<sect1 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. 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. 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>
</sect1> <!-- Aliases -->
<sect1 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>
<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>
</sect1> <!-- List Constructs -->
<sect1 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 operators 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>
</sect1> <!-- Arrays -->
<sect1 id="files">
<title>Files</title>
<para><anchor id="filesref"></para>
<itemizedlist>
<listitem>
<para><filename>/etc/profile</filename></para>
<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>
<listitem>
<para><filename>/etc/bashrc</filename></para>
<para>systemwide functions and and <link
linkend="aliasref">aliases</link> for Bash</para>
</listitem>
<listitem>
<para><filename><varname>$HOME</varname>/.bash_profile</filename></para>
<para>user-specific Bash environmental default settings,
found in each user's home directory (the local counterpart
to <filename>/etc/profile</filename>)</para>
</listitem>
<listitem>
<para><filename><varname>$HOME</varname>/.bashrc</filename></para>
<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>
</itemizedlist>
<para>These are the <emphasis>startup files</emphasis> for Bash. They
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>
<itemizedlist>
<listitem>
<para><filename><varname>$HOME</varname>/.bash_logout</filename></para>
<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>
</itemizedlist>
</sect1> <!-- Files -->
<sect1 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>, 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>
</sect1> <!-- /dev and /proc -->
<sect1 id="zeros">
<title>Of Zeros and Nulls</title>
<para><anchor id="zerosref"></para>
<variablelist>
<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="ex74">):
<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 <command>od</command> 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">.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Zeros and Nulls -->
<sect1 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><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>
<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>
</sect1> <!-- Debugging -->
<sect1 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</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>
</sect1> <!-- Options -->
<sect1 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.
xyz((!*=value2
# Causes even worse 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><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>
</sect1> <!-- Gotchas -->
<sect1 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>
<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.
<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
_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> <!-- Scripting With Style -->
<sect1 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>
<sect2>
<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>
</sect2> <!-- Interactive and non-interactive scripts -->
<sect2>
<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>
</sect2> <!-- Tests and Comparisons -->
<sect2>
<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>
</sect2> <!-- Optimizations -->
<sect2>
<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 a file of your favorite and most useful
definitions and functions, then <quote>include</quote>
this file in scripts as necessary with either the <link
linkend="dotref">dot</link> (<command>.</command>) or <link
linkend="sourceref">source</link> command.</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>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>
</sect2> <!-- Assorted Tips -->
</sect1> <!-- miscellany -->
<sect1 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, 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>
</sect1> <!-- Bash, version 2 -->
</chapter> <!-- Tutorial / Reference -->
<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>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> 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>End Notes</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="toolsused">
<title>Tools Used to Produce This Book</title>
<orderedlist 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://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>
</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>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>Rick Hohensee has written a virtual machine plus assembler,
entirely as shell scripts. See his <ulink
url="http://linux01.gwdg.de/~rhohen/H3sm.html">H3sm site</ulink>
or <ulink url="ftp://linux01.gwdg.de/pub/linux/clienux/interim">ftp
site</ulink>.</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 man pages 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>: A script for viewing formatted man pages
</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>: A script for uploading to an ftp site,
using a locally encrypted password</title>
<programlisting>&encryptedpw;</programlisting>
</example>
<example id="copycd">
<title><command>copy-cd</command>: A script for copying a data CD</title>
<programlisting>&copycd;</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>: A script for removing mail and news message headers
</title>
<programlisting>&behead;</programlisting>
</example>
<example id="ftpget">
<title><command>ftpget</command>: A script for 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>: A script for 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>: A script for 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>: A script for 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="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 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
<command>CALL</command>)</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>LP1</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>cmp or diff</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>DIR</option></entry>
<entry>ls -l</entry>
<entry>directory listing</entry>
</row>
<row>
<entry><option>EXIT</option></entry>
<entry>exit</entry>
<entry>exit current process</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 Bash (and <emphasis>sh</emphasis>)
operators and commands have many more options and
enhancements than their batch file and DOS equivalents.
Many batch file scripts 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 usually
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 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 incorporated into a printed 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. -->
&doc-index;
</book>
<!--
</article>
-->
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-indent-step:2
sgml-indent-data:t
End:
-->