LDP/LDP/guide/docbook/abs-guide/abs-guide.xml

40334 lines
1.3 MiB

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
[
<!ENTITY INDEX00 SYSTEM "INDEX00.xml">
<!ENTITY TABEXP SYSTEM "TABEXP.xml">
<!ENTITY ex1 SYSTEM "ex1.sh">
<!ENTITY ex1a SYSTEM "ex1a.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 ex30a SYSTEM "ex30a.sh">
<!ENTITY ex31 SYSTEM "ex31.sh">
<!ENTITY ex32 SYSTEM "ex32.sh">
<!ENTITY ex33 SYSTEM "ex33.sh">
<!ENTITY ex33a SYSTEM "ex33a.sh">
<!ENTITY ex34 SYSTEM "ex34.sh">
<!ENTITY ex35 SYSTEM "ex35.sh">
<!ENTITY ex36 SYSTEM "ex36.sh">
<!ENTITY ex37 SYSTEM "ex37.sh">
<!ENTITY ex38 SYSTEM "ex38.sh">
<!ENTITY ex38bis SYSTEM "data-file">
<!ENTITY ex39 SYSTEM "ex39.sh">
<!ENTITY ex40 SYSTEM "ex40.sh">
<!ENTITY ex41 SYSTEM "ex41.sh">
<!ENTITY ex42 SYSTEM "ex42.sh">
<!ENTITY ex43 SYSTEM "ex43.sh">
<!ENTITY ex44 SYSTEM "ex44.sh">
<!ENTITY ex45 SYSTEM "ex45.sh">
<!ENTITY ex45a SYSTEM "ex45a.sh">
<!ENTITY ex46 SYSTEM "ex46.sh">
<!ENTITY ex47 SYSTEM "ex47.sh">
<!ENTITY ex48 SYSTEM "ex48.sh">
<!ENTITY ex49 SYSTEM "ex49.sh">
<!ENTITY ex50 SYSTEM "ex50.sh">
<!ENTITY ex51 SYSTEM "ex51.sh">
<!ENTITY ex52 SYSTEM "ex52.sh">
<!ENTITY ex53 SYSTEM "ex53.sh">
<!ENTITY ex54 SYSTEM "ex54.sh">
<!ENTITY ex55 SYSTEM "ex55.sh">
<!ENTITY ex56 SYSTEM "ex56.sh">
<!ENTITY ex56py SYSTEM "ex56py.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 ex68a SYSTEM "ex68a.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 cards SYSTEM "cards.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 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 tree2 SYSTEM "tree2.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 redir4a SYSTEM "redir4a.sh">
<!ENTITY redir5 SYSTEM "redir5.sh">
<!ENTITY wipedir SYSTEM "wipedir.sh">
<!ENTITY grp SYSTEM "grp.sh">
<!ENTITY killprocess SYSTEM "kill-process.sh">
<!ENTITY killbyname SYSTEM "kill-byname.sh">
<!ENTITY strtest SYSTEM "str-test.sh">
<!ENTITY colm SYSTEM "colm.sh">
<!ENTITY lookup SYSTEM "lookup.sh">
<!ENTITY arglist SYSTEM "arglist.sh">
<!ENTITY rot13 SYSTEM "rot13.sh">
<!ENTITY rot13a SYSTEM "rot13a.sh">
<!ENTITY rot14 SYSTEM "rot14.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 symlinks2 SYSTEM "symlinks2.sh">
<!ENTITY string SYSTEM "string.sh">
<!ENTITY nestedloop SYSTEM "nested-loop.sh">
<!ENTITY casecmd SYSTEM "case-cmd.sh">
<!ENTITY uns SYSTEM "unset.sh">
<!ENTITY base SYSTEM "base.sh">
<!ENTITY allprofs SYSTEM "allprofs.sh">
<!ENTITY pidid SYSTEM "pid-identifier.sh">
<!ENTITY constat SYSTEM "connect-stat.sh">
<!ENTITY subpit SYSTEM "subshell-pitfalls.sh">
<!ENTITY readredir SYSTEM "read-redir.sh">
<!ENTITY andlist2 SYSTEM "and-list2.sh">
<!ENTITY qfunction SYSTEM "q-function.sh">
<!ENTITY viewdata SYSTEM "viewdata.sh">
<!ENTITY VIEWDAT SYSTEM "VIEWDATA.BAT">
<!ENTITY what SYSTEM "what.sh">
<!ENTITY max SYSTEM "max.sh">
<!ENTITY max2 SYSTEM "max2.sh">
<!ENTITY findstring SYSTEM "findstring.sh">
<!ENTITY listglob SYSTEM "list-glob.sh">
<!ENTITY realname SYSTEM "realname.sh">
<!ENTITY escaped SYSTEM "escaped.sh">
<!ENTITY fileinfo SYSTEM "file-info.sh">
<!ENTITY fileinfo01 SYSTEM "file-info01.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 indfunc SYSTEM "ind-func.sh">
<!ENTITY primes SYSTEM "primes.sh">
<!ENTITY primes2 SYSTEM "primes2.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 emptyarray SYSTEM "empty-array.sh">
<!ENTITY length SYSTEM "length.sh">
<!ENTITY monthlypmt SYSTEM "monthlypmt.sh">
<!ENTITY derpm SYSTEM "de-rpm.sh">
<!ENTITY blotout SYSTEM "blot-out.sh">
<!ENTITY readr SYSTEM "read-r.sh">
<!ENTITY cryptoquote SYSTEM "crypto-quote.sh">
<!ENTITY erase SYSTEM "erase.sh">
<!ENTITY returntest SYSTEM "return-test.sh">
<!ENTITY daysbetween SYSTEM "days-between.sh">
<!ENTITY varmatch SYSTEM "var-match.sh">
<!ENTITY recurse SYSTEM "recurse.sh">
<!ENTITY assert SYSTEM "assert.sh">
<!ENTITY intorstring SYSTEM "int-or-string.sh">
<!ENTITY ramdisk SYSTEM "ramdisk.sh">
<!ENTITY m4 SYSTEM "m4.sh">
<!ENTITY idelete SYSTEM "idelete.sh">
<!ENTITY matchstring SYSTEM "match-string.sh">
<!ENTITY bashandperl SYSTEM "bashandperl.sh">
<!ENTITY cvt SYSTEM "cvt.sh">
<!ENTITY wf SYSTEM "wf.sh">
<!ENTITY wf2 SYSTEM "wf2.sh">
<!ENTITY hypot SYSTEM "hypotenuse.sh">
<!ENTITY random2 SYSTEM "random2.sh">
<!ENTITY altbc SYSTEM "alt-bc.sh">
<!ENTITY substringex SYSTEM "substring-extraction.sh">
<!ENTITY stupscr SYSTEM "stupid-script-tricks.sh">
<!ENTITY resistor SYSTEM "resistor-inventory.sh">
<!ENTITY stackex SYSTEM "stack.sh">
<!ENTITY gcd SYSTEM "gcd.sh">
<!ENTITY selfmailer SYSTEM "self-mailer.sh">
<!ENTITY collatz SYSTEM "collatz.sh">
<!ENTITY wstrings SYSTEM "wstrings.sh">
<!ENTITY multiplication SYSTEM "multiplication.sh">
<!ENTITY sumproduct SYSTEM "sum-product.sh">
<!ENTITY userlist SYSTEM "userlist.sh">
<!ENTITY bgloop SYSTEM "background-loop.sh">
<!ENTITY tout SYSTEM "t-out.sh">
<!ENTITY csubloop SYSTEM "csubloop.sh">
<!ENTITY arrfunc SYSTEM "array-function.sh">
<!ENTITY lifeslow SYSTEM "life.sh">
<!ENTITY commentblock SYSTEM "commentblock.sh">
<!ENTITY selfdocument SYSTEM "self-document.sh">
<!ENTITY hf SYSTEM "here-function.sh">
<!ENTITY fileintegrity SYSTEM "file-integrity.sh">
<!ENTITY readnovar SYSTEM "read-novar.sh">
<!ENTITY setpos SYSTEM "set-pos.sh">
<!ENTITY revposparams SYSTEM "revposparams.sh">
<!ENTITY badread SYSTEM "badread.sh">
<!ENTITY selfexec SYSTEM "self-exec.sh">
<!ENTITY selfdestruct SYSTEM "self-destruct.sh">
<!ENTITY reassignstdout SYSTEM "reassign-stdout.sh">
<!ENTITY upperconv SYSTEM "upperconv.sh">
<!ENTITY pbook SYSTEM "pb.sh">
<!ENTITY makedict SYSTEM "makedict.sh">
<!ENTITY missingkeyword SYSTEM "missing-keyword.sh">
<!ENTITY blankrename SYSTEM "blank-rename.sh">
<!ENTITY scriptdetector SYSTEM "script-detector.sh">
<!ENTITY hexconvert SYSTEM "hexconvert.sh">
<!ENTITY factr SYSTEM "factr.sh">
<!ENTITY cannon SYSTEM "cannon.sh">
<!ENTITY agram SYSTEM "agram.sh">
<!ENTITY agram2 SYSTEM "agram2.sh">
<!ENTITY poem SYSTEM "poem.sh">
<!ENTITY soundex SYSTEM "soundex.sh">
<!ENTITY tempfilename SYSTEM "tempfile-name.sh">
<!ENTITY unitconversion SYSTEM "unit-conversion.sh">
<!ENTITY usagemessage SYSTEM "usage-message.sh">
<!ENTITY colorecho SYSTEM "color-echo.sh">
<!ENTITY selfsource SYSTEM "self-source.sh">
<!ENTITY selfcopy SYSTEM "self-copy.sh">
<!ENTITY arrowdetect SYSTEM "arrow-detect.sh">
<!ENTITY paragraphspace SYSTEM "paragraph-space.sh">
<!ENTITY brokenlink SYSTEM "broken-link.sh">
<!ENTITY continuenex SYSTEM "continue-n.example">
<!ENTITY pickcard SYSTEM "pick-card.sh">
<!ENTITY copyarray SYSTEM "CopyArray.sh">
<!ENTITY directoryinfo SYSTEM "directory-info.sh">
<!ENTITY embarr SYSTEM "embedded-arrays.sh">
<!ENTITY generatescript SYSTEM "generate-script.sh">
<!ENTITY scriptarray SYSTEM "script-array.sh">
<!ENTITY randombetween SYSTEM "random-between.sh">
<!ENTITY arrayops SYSTEM "array-ops.sh">
<!ENTITY arraystrops SYSTEM "array-strops.sh">
<!ENTITY arrayappend SYSTEM "array-append.bash">
<!ENTITY arrayassign SYSTEM "array-assign.bash">
<!ENTITY lettercount SYSTEM "letter-count.sh">
<!ENTITY lettercount2 SYSTEM "letter-count2.sh">
<!ENTITY protectliteral SYSTEM "protect_literal.sh">
<!ENTITY unprotectliteral SYSTEM "unprotect_literal.sh">
<!ENTITY usbinst SYSTEM "usb.sh">
<!ENTITY speech0 SYSTEM "speech.sh">
<!ENTITY basicsreviewed SYSTEM "basics-reviewed.bash">
<!ENTITY readpipe SYSTEM "readpipe.sh">
<!ENTITY usrmnt SYSTEM "usrmnt.sh">
<!ENTITY stopwatch SYSTEM "sw.sh">
<!ENTITY dialog SYSTEM "dialog.sh">
<!ENTITY hellol SYSTEM "hello.sh">
<!ENTITY hanoi SYSTEM "hanoi.bash">
<!ENTITY hanoi2 SYSTEM "hanoi2.bash">
<!ENTITY hanoi2a SYSTEM "hanoi2a.bash">
<!ENTITY horserace SYSTEM "horserace.sh">
<!ENTITY remote SYSTEM "remote.bash">
<!ENTITY musicscr SYSTEM "music.sh">
<!ENTITY prependex SYSTEM "prepend.sh">
<!ENTITY setnewpw SYSTEM "setnew-passwd.sh">
<!ENTITY badop SYSTEM "bad-op.sh">
<!ENTITY dereferencecl SYSTEM "dereference.sh">
<!ENTITY archiveweblogs SYSTEM "archiveweblogs.sh">
<!ENTITY devtcp SYSTEM "dev-tcp.sh">
<!ENTITY multipleproc SYSTEM "multiple-processes.sh">
<!ENTITY funccmdlinearg SYSTEM "func-cmdlinearg.sh">
<!ENTITY isspammer SYSTEM "is-spammer.sh">
<!ENTITY isspammer2 SYSTEM "is_spammer.bash">
<!ENTITY wgetter2 SYSTEM "wgetter2.bash">
<!ENTITY exercisingdd SYSTEM "exercising-dd.sh">
<!ENTITY quotefetch SYSTEM "quote-fetch.sh">
<!ENTITY avoidsubshell SYSTEM "avoid-subshell.sh">
<!ENTITY loggingwrapper SYSTEM "logging-wrapper.sh">
<!ENTITY dictlookup SYSTEM "dict-lookup.sh">
<!ENTITY bashpodder SYSTEM "bashpodder.sh">
<!ENTITY drawbox SYSTEM "Draw-box.sh">
<!ENTITY testcgi SYSTEM "test-cgi.sh">
<!ENTITY spawnscr SYSTEM "spawn.sh">
<!ENTITY iscan SYSTEM "iscan.sh">
<!ENTITY ra2ogg SYSTEM "ra2ogg.sh">
<!ENTITY namesdata SYSTEM "names.data">
<!ENTITY hashlib SYSTEM "Hash.lib">
<!ENTITY hashexample SYSTEM "hash-example.sh">
<!ENTITY hashex2 SYSTEM "ha.sh">
<!ENTITY getoptsimple SYSTEM "getopt-simple.sh">
<!ENTITY usegetopt SYSTEM "UseGetOpt.sh">
<!ENTITY usegetopt2 SYSTEM "UseGetOpt-2.sh">
<!ENTITY usegetoptex SYSTEM "UseGetOpt-2">
<!ENTITY insertionsort SYSTEM "insertion-sort.bash">
<!ENTITY findsplit SYSTEM "find-splitpara.sh">
<!ENTITY prasc SYSTEM "pr-asc.sh">
<!ENTITY whx SYSTEM "whx.sh">
<!ENTITY fifteen SYSTEM "fifteen.sh">
<!ENTITY spamlookup SYSTEM "spam-lookup.sh">
<!ENTITY testsuite SYSTEM "test-suite.sh">
<!ENTITY mailboxgrep SYSTEM "mailbox_grep.sh">
<!ENTITY fc4upd SYSTEM "fc4upd.sh">
<!ENTITY ipscript SYSTEM "ip.sh">
<!ENTITY nightlybackup SYSTEM "nightly-backup.sh">
<!ENTITY echoparams SYSTEM "echo-params.sh">
<!ENTITY randstring SYSTEM "rand-string.sh">
<!ENTITY splitcopy SYSTEM "splitcopy.sh">
<!ENTITY padsw SYSTEM "pad.sh">
<!ENTITY soundcardon SYSTEM "soundcard-on.sh">
<!ENTITY recursiondemo SYSTEM "recursion-demo.sh">
<!ENTITY recursiondemo2 SYSTEM "recursion-def.sh">
<!ENTITY cwsolver SYSTEM "cw-solver.sh">
<!ENTITY progressbar SYSTEM "progress-bar.sh">
<!ENTITY progressbar2 SYSTEM "progress-bar2.sh">
<!ENTITY tohtml SYSTEM "tohtml.sh">
<!ENTITY datecalc SYSTEM "date-calc.sh">
<!ENTITY brownian SYSTEM "brownian.sh">
<!ENTITY fromsh SYSTEM "from.sh">
<!ENTITY fibo SYSTEM "fibo.sh">
<!ENTITY homework SYSTEM "homework.sh">
<!ENTITY arrchoice SYSTEM "arr-choice.sh">
<!ENTITY bashek SYSTEM "BashExtraKeys.sh">
<!ENTITY fetchaddress SYSTEM "fetch_address.sh">
<!ENTITY fetchaddress2 SYSTEM "fetch_address-2.sh">
<!ENTITY case4 SYSTEM "case4.sh">
<!ENTITY asciish SYSTEM "ascii.sh">
<!ENTITY ascii2sh SYSTEM "ascii2.sh">
<!ENTITY ascii3sh SYSTEM "ascii3.sh">
<!ENTITY petals SYSTEM "petals.sh">
<!ENTITY qky SYSTEM "qky.sh">
<!ENTITY maned SYSTEM "maned.sh">
<!ENTITY ktour SYSTEM "ktour.sh">
<!ENTITY msquare SYSTEM "msquare.sh">
<!ENTITY readn SYSTEM "read-N.sh">
<!ENTITY herecommsub SYSTEM "here-commsub.sh">
<!ENTITY negarray SYSTEM "neg-array.sh">
<!ENTITY negoffset SYSTEM "neg-offset.sh">
<!ENTITY lastpipeopt SYSTEM "lastpipe-option.sh">
<!ENTITY nim SYSTEM "nim.sh">
<!ENTITY psubp SYSTEM "psub.bash">
<!ENTITY base64 SYSTEM "base64.sh">
<!ENTITY bingo SYSTEM "bingo.sh">
<!ENTITY wrps SYSTEM "wr-ps.bash">
<!ENTITY ipaddresses SYSTEM "ip-addresses.sh">
<!ENTITY assocarrtest SYSTEM "assoc-arr-test.sh">
<!ENTITY stddev SYSTEM "sd.sh">
<!ENTITY samorse SYSTEM "sam.sh">
<!ENTITY backlight SYSTEM "backlight.sh">
<!ENTITY showallc SYSTEM "show-all-colors.sh">
<!ENTITY sedappend SYSTEM "sedappend.sh">
<!ENTITY gronsfeld SYSTEM "gronsfeld.bash">
<!ENTITY rpsdcard SYSTEM "rp.sdcard.sh">
<!ENTITY testexectime SYSTEM "test-execution-time.sh">
<!ENTITY gen0data SYSTEM "gen0">
<!ENTITY cdll SYSTEM "cdll">
<!ENTITY bashrc SYSTEM "bashrc">
<!ENTITY bashprof SYSTEM "bash-profile.snippet">
<!ENTITY opprectable SYSTEM "opprec-table.xml">
]>
<book id="abs-guide">
<bookinfo>
<title>Advanced Bash-Scripting Guide</title>
<subtitle>An in-depth exploration of the art of shell scripting</subtitle>
<author>
<firstname>Mendel</firstname>
<surname>Cooper</surname>
<affiliation>
<orgname></orgname>
<address><email>thegrendel.abs@gmail.com</email></address>
</affiliation>
</author>
<releaseinfo>10</releaseinfo>
<pubdate>10 Mar 2014</pubdate>
<isbn>978-1-4357-5219-1</isbn>
<revhistory id="revhistory">
<revision>
<revnumber>6.5</revnumber>
<date>05 Apr 2012</date>
<authorinitials>mc</authorinitials>
<revremark>'TUNGSTENBERRY' release</revremark>
</revision>
<revision>
<revnumber>6.6</revnumber>
<date>27 Nov 2012</date>
<authorinitials>mc</authorinitials>
<revremark>'YTTERBIUMBERRY' release</revremark>
</revision>
<revision>
<revnumber>10</revnumber>
<date>10 Mar 2014</date>
<authorinitials>mc</authorinitials>
<revremark>'PUBLICDOMAIN' release</revremark>
</revision>
</revhistory>
<abstract>
<para>This tutorial assumes no previous knowledge of
scripting or programming, yet progresses rapidly toward an
intermediate/advanced level of instruction <emphasis>. . . all
the while sneaking in little nuggets of <trademark
class="registered">UNIX</trademark> wisdom and lore</emphasis>. It
serves as a textbook, a manual for self-study, and as a reference and
source of knowledge on shell scripting techniques. The exercises
and heavily-commented examples invite active reader participation,
under the premise that <userinput>the only way to really learn
scripting is to write scripts</userinput>.</para>
<para>This book is suitable for classroom use as a
general introduction to programming concepts.</para>
<para>This document is herewith granted to the Public Domain.
<userinput>No copyright!</userinput></para>
</abstract>
</bookinfo>
<dedication>
<para>For Anita, the source of all the magic</para>
</dedication>
<part label="Part 1" id="part1">
<title>Introduction</title>
<partintro>
<epigraph>
<para>Script: <emphasis>A writing; a written
document. [Obs.]</emphasis></para>
<para>--<emphasis>Webster's Dictionary</emphasis>, 1913 ed.</para>
</epigraph>
<para><anchor id="whatsascript"/></para>
<para>The shell is a command interpreter. More than just the
insulating layer between the operating system kernel and the user,
it's also a fairly powerful programming language. A shell program,
called a <firstterm>script</firstterm>, is an easy-to-use tool for
building applications by <quote>gluing together</quote> 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, lend additional power and flexibility to scripts.
Shell scripts are especially well suited for administrative
system tasks and other routine repetitive tasks not requiring the
bells and whistles of a full-blown tightly structured programming
language.</para>
</partintro>
<chapter id="why-shell">
<title>Shell Programming!</title>
<epigraph>
<para>No programming language is perfect. There is not even a single
best language; there are only languages well suited or perhaps
poorly suited for particular purposes.</para>
<para>--Herbert Mayer</para>
</epigraph>
<para>A working knowledge of shell scripting is essential to anyone
wishing to become reasonably proficient at system administration,
even if they do not anticipate ever having to actually write a
script. Consider that as a Linux machine boots up, it executes the
shell scripts in <filename class="directory">/etc/rc.d</filename>
to restore the system configuration and set up services. A detailed
understanding of these startup scripts is important for analyzing
the behavior of a system, and possibly modifying it.</para>
<para>The craft of scripting is not hard to master,
since 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 -- even austere -- similar to
that of invoking and chaining together utilities at the command
line, and there are only a few <quote>rules</quote> governing
their use. Most short scripts work right the first time, and
debugging even the longer ones is straightforward.</para>
<para>
<blockquote>
<literallayout>
In the early days of personal computing, the BASIC language enabled
anyone reasonably computer proficient to write programs on an early
generation of microcomputers. Decades later, the Bash scripting
language enables anyone with a rudimentary knowledge of Linux or
UNIX to do the same on modern machines.
We now have miniaturized single-board computers with amazing
capabilities, such as the <ulink url="http://www.raspberrypi.org/">Raspberry Pi</ulink>.
Bash scripting provides a way to explore the capabilities of these
fascinating devices.
</literallayout>
</blockquote>
</para>
<para>A shell script is a quick-and-dirty method of prototyping
a complex application. Getting even a limited subset of
the functionality to work in a script is often a useful
first stage in project development. In this way, the structure
of the application can be tested and tinkered with, and the
major pitfalls found before proceeding to the final coding
in <firstterm>C</firstterm>, <firstterm>C++</firstterm>,
<firstterm>Java</firstterm>, <link linkend="perlref">Perl</link>,
or <firstterm>Python</firstterm>.</para>
<para>Shell scripting hearkens back to the classic UNIX philosophy
of breaking complex projects into simpler subtasks, of chaining
together components and utilities. Many consider this a better,
or at least more aesthetically pleasing approach to problem solving
than using one of the new generation of high-powered all-in-one
languages, such as <firstterm>Perl</firstterm>, 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>According to <link linkend="mayerref">Herbert Mayer</link>,
<quote>a useful language needs arrays, pointers,
and a generic mechanism for building data structures.</quote>
By these criteria, shell scripting falls somewhat short of being
<quote>useful.</quote> Or, perhaps not. . . .</para>
<sidebar>
<para>When not to use shell scripts
<itemizedlist>
<listitem>
<para>Resource-intensive tasks, especially where speed is
a factor (sorting, hashing, recursion
<footnote><para>Although <link linkend="recursionref0">recursion
<emphasis>is</emphasis> possible in a shell script</link>,
it tends to be slow and its implementation is often
an <link linkend="fiboref">ugly kludge</link>.
</para></footnote>
...)</para>
</listitem> <listitem>
<para>Procedures involving heavy-duty math operations,
especially floating point arithmetic, arbitrary
precision calculations, or complex numbers (use
<firstterm>C++</firstterm> or <firstterm>FORTRAN</firstterm>
instead)</para>
</listitem> <listitem>
<para>Cross-platform portability required (use
<firstterm>C</firstterm> or <firstterm>Java</firstterm>
instead)</para>
</listitem> <listitem>
<para>Complex applications, where structured programming is
a necessity (type-checking of variables, function
prototypes, etc.)</para>
</listitem> <listitem>
<para>Mission-critical applications upon which you are betting the
future of the company</para>
</listitem> <listitem>
<para>Situations where <emphasis>security</emphasis> is
important, where you need to guarantee the integrity of
your system and protect against intrusion, cracking, and
vandalism</para>
</listitem> <listitem>
<para>Project consists of subcomponents with interlocking
dependencies</para>
</listitem> <listitem>
<para>Extensive file operations required
(<firstterm>Bash</firstterm> is limited to serial file access,
and that only in a particularly clumsy and inefficient
line-by-line fashion.)</para>
</listitem> <listitem>
<para>Need native support for multi-dimensional arrays</para>
</listitem> <listitem>
<para>Need data structures, such as linked lists or trees</para>
</listitem> <listitem>
<para>Need to generate / manipulate graphics or GUIs</para>
</listitem> <listitem>
<para>Need direct access to system hardware or
external peripherals</para>
</listitem> <listitem>
<para>Need port or <link linkend="socketref">socket</link>
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
put the source code right out in the open for all the world
to see.)</para>
</listitem>
</itemizedlist></para>
<para>If any of the above applies, consider a more powerful scripting
language -- perhaps <firstterm>Perl</firstterm>,
<firstterm>Tcl</firstterm>, <firstterm>Python</firstterm>,
<firstterm>Ruby</firstterm> -- or possibly a
compiled language such as <firstterm>C</firstterm>,
<firstterm>C++</firstterm>, or <firstterm>Java</firstterm>. Even
then, prototyping the application as a shell script might still
be a useful development step.</para>
</sidebar>
<para><anchor id="bashdef"/></para>
<para>We will be using <acronym>Bash</acronym>, an acronym
<footnote><para>An <firstterm>acronym</firstterm>
is an <emphasis>ersatz</emphasis> word formed by pasting
together the initial letters of the words into a tongue-tripping
phrase. This morally corrupt and pernicious practice
deserves appropriately severe punishment. Public
flogging suggests itself.</para></footnote>
for <quote>Bourne-Again shell</quote> and a pun on Stephen Bourne's
now classic <firstterm>Bourne</firstterm> shell. Bash has become
a <foreignphrase>de facto</foreignphrase> standard for shell
scripting on most flavors of UNIX. Most of the principles this
book covers apply equally well to scripting with other shells,
such as the <firstterm>Korn Shell</firstterm>, from which Bash
derives some of its features,
<footnote><para>Many of the features of <firstterm>ksh88</firstterm>,
and even a few from the updated <firstterm>ksh93</firstterm>
have been merged into Bash.</para></footnote>
and the <firstterm>C Shell</firstterm> and its variants. (Note that
<firstterm>C Shell</firstterm> programming is not recommended due to
certain inherent problems, as pointed out in an October, 1993 <ulink
url="http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/">Usenet
post</ulink> by Tom Christiansen.) </para>
<para>What follows is a tutorial on shell scripting. It relies
heavily on examples to illustrate various features of the shell.
The example scripts work -- they've been tested, insofar as
possible -- and some of them are even useful in real life. The
reader can play with the actual working code of the examples
in the source archive (<filename>scriptname.sh</filename> or
<filename>scriptname.bash</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 necessarily conform to this nomenclature.</para></footnote>
give them <firstterm>execute</firstterm> permission
(<userinput>chmod u+rx scriptname</userinput>),
then run them to see what happens. Should the <ulink
url="http://bash.deta.in/abs-guide-latest.tar.bz2">source
archive</ulink> not be available, then cut-and-paste from the <ulink
url="http://www.tldp.org/LDP/abs/abs-guide.html.tar.gz">HTML</ulink> or
<ulink url="http://bash.deta.in/abs-guide.pdf">pdf</ulink>
rendered versions. Be aware that some of the scripts presented here
introduce features before they are explained, and this may require
the reader to temporarily skip ahead for enlightenment.</para>
<para>Unless otherwise noted, <ulink
url="mailto:thegrendel.abs@gmail.com">the author</ulink> of this
book wrote the example scripts that follow.</para>
<epigraph>
<para>His countenance was bold and bashed not.</para>
<para>--Edmund Spenser</para>
</epigraph>
</chapter> <!-- Why Shell Programming? -->
<chapter id="sha-bang">
<title>Starting Off With a Sha-Bang</title>
<epigraph>
<para>Shell programming is a 1950s juke box . . .</para>
<para>--Larry Wall</para>
</epigraph>
<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><firstterm>cleanup</firstterm>: A script to clean up log
files in /var/log </title> <programlisting>&ex1;</programlisting>
</example>
<para>There is nothing unusual here, only a set of commands that
could just as easily have been invoked one by one from the
command-line on the console or in a terminal window.
The advantages of placing the commands in a script go far beyond
not having to retype them time and again. The script becomes a
<firstterm>program</firstterm> -- a <emphasis>tool</emphasis> --
and it can easily be modified or customized for a particular
application.</para>
<example id="ex1a">
<title><firstterm>cleanup</firstterm>: An improved clean-up
script</title> <programlisting>&ex1a;</programlisting>
</example>
<para>Now <emphasis>that's</emphasis> beginning to look like a real
script. But we can go even farther . . .</para>
<example id="ex2">
<title><firstterm>cleanup</firstterm>: An enhanced
and generalized version of above scripts.</title>
<programlisting>&ex2;</programlisting>
</example>
<para>Since you may not wish to wipe out the entire system log,
this version of the script keeps the last section of the message
log intact. You will constantly discover ways of fine-tuning
previously written scripts for increased effectiveness.</para>
<para><anchor id="shabangref"/>* * *</para>
<para><anchor id="magnumref"/>The
<firstterm><indexterm>
<primary>sha-bang</primary>
</indexterm> sha-bang</firstterm>
(<token>
<indexterm>
<primary>#!</primary>
</indexterm> #!</token>)
<footnote><para>More commonly seen in the literature as
<firstterm>she-bang</firstterm> or <firstterm>sh-bang</firstterm>.
This derives from the concatenation of the tokens
<firstterm>sharp</firstterm> (<token>#</token>) and
<firstterm>bang</firstterm> (<token>!</token>).</para></footnote>
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. The
<token>#!</token> is actually a two-byte
<footnote>
<para>Some flavors of UNIX (those based on 4.2 BSD)
allegedly take a four-byte magic number, requiring
a blank after the <token>!</token> --
<userinput>#! /bin/sh</userinput>. <ulink
url="http://www.in-ulm.de/~mascheck/various/shebang/#details">
According to Sven Mascheck</ulink> this is probably a myth.</para>
</footnote>
<indexterm>
<primary>magic number</primary>
</indexterm>
<firstterm>magic number</firstterm>, a special marker that
designates a file type, or in this case an executable shell
script (type <userinput>man magic</userinput> for more
details on this fascinating topic). Immediately following
the <firstterm>sha-bang</firstterm> is a <firstterm>path
name</firstterm>. 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 (the line
following the <firstterm>sha-bang</firstterm> line), and 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>
<para>If, in fact, the script includes an
<emphasis>extra</emphasis> <token>#!</token> line, then
<command>bash</command> will interpret it as a comment.
<programlisting>#!/bin/bash
echo "Part 1 of script."
a=1
#!/bin/bash
# This does *not* launch a new script.
echo "Part 2 of script."
echo $a # Value of $a stays at 1.</programlisting></para>
</footnote>
</para>
<para><programlisting>#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/bin/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=85
echo "This line will never print (betcha!)."
exit $WHATEVER # Doesn't matter. The script will not exit here.
# Try an echo $? after script termination.
# You'll get a 0, not a 85.</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. (A <link
linkend="heredocref">here document</link> using
<link linkend="catref">cat</link> is possibly a better alternative
-- see <xref linkend="ex71"/>).</para>
</footnote>
Using <userinput>#!/bin/sh</userinput>, the default Bourne shell
in most commercial variants of UNIX, makes the script <link
linkend="portabilityissues">portable</link> to non-Linux machines,
though you <link linkend="binsh">sacrifice Bash-specific
features</link>. The script will, however, conform to the
<acronym>POSIX</acronym>
<footnote>
<para><anchor id="posix2ref"/><emphasis role="strong">P</emphasis>ortable
<emphasis role="strong">O</emphasis>perating
<emphasis role="strong">S</emphasis>ystem <emphasis
role="bold">I</emphasis>nterface, an attempt to
standardize UNI<emphasis role="strong">X</emphasis>-like
OSes. The POSIX specifications are listed on the <ulink
url="http://www.opengroup.org/onlinepubs/007904975/toc.htm">Open
Group site</ulink>.</para>
</footnote>
<command>sh</command> standard.</para>
<para>Note that the path given at the <quote>sha-bang</quote> must
be correct, otherwise an error message -- usually <quote>Command
not found.</quote> -- will be the only result of running the
script.
<footnote><para>To avoid this possibility, a script may begin
with a <link linkend="envv2ref">#!/bin/env bash</link>
<firstterm>sha-bang</firstterm> line. This may be
useful on UNIX machines where <firstterm>bash</firstterm>
is not located in <filename
class="directory">/bin</filename></para></footnote>
</para>
<para><token>#!</token> can be omitted if the script consists only
of a set of generic system commands, using no internal
shell directives. The second example, above, requires the
initial <token>#!</token>, since the variable assignment line,
<userinput>lines=50</userinput>, uses a shell-specific construct.
<footnote><para>If <firstterm>Bash</firstterm> is your default
shell, then the <token>#!</token> isn't necessary at the
beginning of a script. However, if launching a script from
a different shell, such as <firstterm>tcsh</firstterm>,
then you <emphasis>will</emphasis> need the
<token>#!</token>.</para></footnote>
Note again that <userinput>#!/bin/sh</userinput> invokes the default
shell interpreter, which defaults to <filename>/bin/bash</filename>
on a Linux machine.</para>
<tip>
<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 will build quite an 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.</para>
<para><programlisting>E_WRONG_ARGS=85
script_parameters="-a -h -m -z"
# -a = all, -h = help, etc.
if [ $# -ne $Number_of_expected_args ]
then
echo "Usage: `basename $0` $script_parameters"
# `basename $0` is the script's filename.
exit $E_WRONG_ARGS
fi</programlisting>
</para>
<para>Many times, you will write a script that carries out one
particular task. The first script in this chapter is an
example. Later, it might occur to you to generalize
the script to do other, similar tasks. Replacing the literal
(<quote>hard-wired</quote>) constants by variables is a step in
that direction, as is replacing repetitive code blocks by <link
linkend="functionref">functions</link>.</para>
</tip>
<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 <firstterm>Bash</firstterm>
script by <userinput>sh scriptname</userinput> turns off
Bash-specific extensions, and the script may therefore fail
to execute.</para></footnote>
or alternatively <userinput>bash scriptname</userinput>. (Not
recommended is using <userinput>sh &lt;scriptname</userinput>,
since this effectively disables reading from
<link linkend="stdinoutdef"><filename>stdin</filename></link>
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 <firstterm>read</firstterm>, 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
<filename>scriptname</filename> is located, why doesn't
this work? This fails because, for security reasons, the
current directory (<filename class="directory">./</filename>)
is not by default 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
<firstterm>root</firstterm>, of course), to make the script
available to yourself and all other users as a systemwide
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="prelimexer">
<title>Preliminary Exercises</title>
<orderedlist>
<listitem>
<para>System administrators often write scripts to automate common
tasks. Give several instances where such scripts would be
useful.</para>
</listitem>
<listitem>
<para>Write a script that upon invocation shows the
<link linkend="dateref">time and date</link>, <link
linkend="whoref">lists all logged-in users</link>, and gives
the system <link linkend="uptimeref">uptime</link>. The script
then <link linkend="ioredirref">saves this information</link>
to a logfile.</para>
</listitem>
</orderedlist>
</sect1> <!-- Preliminary Exercises -->
</chapter> <!-- Starting Off With a Sha-Bang -->
</part> <!-- Part 1 (Introduction) -->
<part label="Part 2" id="part2">
<title>Basics</title>
<chapter id="special-chars">
<title>Special Characters</title>
<para>What makes a character <firstterm>special</firstterm>?
If it has a meaning beyond its
<firstterm>literal meaning</firstterm>, a <link
linkend="metameaningref">meta-meaning</link>, then we refer
to it as a <firstterm>special character</firstterm>. Along
with commands and <link linkend="keywordref">keywords</link>,
<firstterm>special characters</firstterm> are building blocks
of Bash scripts.</para>
<variablelist id="scharlist">
<title><anchor id="scharlist1"/>Special Characters Found In
Scripts and Elsewhere</title>
<varlistentry><term><anchor id="hashmarkref"/><token>#</token></term>
<listitem>
<indexterm>
<primary>#</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>#</secondary>
</indexterm>
<indexterm>
<primary>comment</primary>
</indexterm>
<formalpara><title>Comments</title>
<para>Lines beginning with a <token>#</token>
(with the exception of <link linkend="magnumref">
<token>#!</token></link>) are comments and will
<emphasis>not</emphasis> be executed.</para>
</formalpara>
<para><programlisting># This line is a comment.</programlisting></para>
<para>Comments may also occur following the end of a command.</para>
<para><programlisting>echo "A comment will follow." # Comment here.
# ^ Note whitespace before #</programlisting></para>
<para><anchor id="wsbcomm"/> 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>
<para><anchor id="comminpipe"/>Comments may even be embedded
within a <link linkend="piperef">pipe</link>.</para>
<para>
<programlisting>initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\
# Delete lines containing '#' comment character.
sed -e 's/\./\. /g' -e 's/_/_ /g'` )
# Excerpted from life.sh script</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, a <link linkend="quotingref">quoted</link>
or an <link linkend="escp">escaped</link> <token>#</token>
in an <link linkend="echoref">echo</link> statement does
<emphasis>not</emphasis> begin a comment. Likewise, a
<token>#</token> appears in <link linkend="psub2">certain
parameter-substitution constructs</link> and in <link
linkend="numconstants"> numerical constant expressions</link>.
<programlisting>echo "The # here does not begin a comment."
echo 'The # here does not begin a comment.'
echo The \# here does not begin a comment.
echo The # here begins a comment.
echo ${PATH#*:} # Parameter substitution, not a comment.
echo $(( 2#101011 )) # Base conversion, not a comment.
# Thanks, S.C.</programlisting>
The standard <link linkend="quotingref">quoting and
escape</link> characters (&quot; ' \) escape the #.
</para></note>
<para>Certain <link linkend="psorex1">pattern matching
operations</link> also use the <token>#</token>.</para>
</listitem>
</varlistentry>
<varlistentry><term><anchor id="semicolonref"/><token>;</token></term>
<listitem>
<indexterm>
<primary>;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>;</secondary>
</indexterm>
<indexterm>
<primary>separator</primary>
</indexterm>
<formalpara><title>Command separator [semicolon]</title>
<para>Permits putting two or more commands on the same
line.</para>
</formalpara>
<para>
<programlisting>echo hello; echo there
if [ -x "$filename" ]; then # Note the space after the semicolon.
#+ ^^
echo "File $filename exists."; cp $filename $filename.bak
else # ^^
echo "File $filename not found."; touch $filename
fi; echo "File test complete."</programlisting>
</para>
<para>Note that the <quote><token>;</token></quote>
<link linkend="findref0">sometimes needs to be
<firstterm>escaped</firstterm></link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>;;</token></term>
<listitem>
<indexterm>
<primary>;;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>case</secondary>
</indexterm>
<indexterm>
<primary>;;</primary>
</indexterm>
<formalpara><title>Terminator in a <link
linkend="caseesac1">case</link> option [double semicolon]</title>
<para><anchor id="doublesemicolon"/></para>
</formalpara>
<para><programlisting>case "$variable" in
abc) echo "\$variable = abc" ;;
xyz) echo "\$variable = xyz" ;;
esac</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>;;&amp;</token></term>
<term><token>;&amp;</token></term>
<listitem>
<indexterm>
<primary>;;&amp;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>;;&amp;</secondary>
</indexterm>
<indexterm>
<primary>;&amp;</primary>
<secondary>case statement</secondary>
</indexterm>
<indexterm>
<primary>;&amp;</primary>
</indexterm>
<formalpara><title><link
linkend="ncterm">Terminators</link>
in a <firstterm>case</firstterm> option (<link
linkend="bash4ref">version 4+</link> of Bash).</title>
<para></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry><term><token>.</token></term>
<listitem>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<indexterm>
<primary>dot command</primary>
</indexterm>
<indexterm>
<primary>source</primary>
</indexterm>
<para><anchor id="dotref"/></para>
<formalpara><title><quote>dot</quote> command [period]</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>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<indexterm>
<primary>filename</primary>
</indexterm>
<indexterm>
<primary>part of a filename</primary>
</indexterm>
<formalpara><title><quote>dot</quote>, as a component of a filename</title>
<para>When working with filenames, a leading dot is the prefix
of a <quote>hidden</quote> file, a file that an
<link linkend="lsref">ls</link> will not normally show.
<screen><prompt>bash$ </prompt><userinput>touch .hidden-file</userinput>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 10
-rw-r--r-- 1 bozo 4034 Jul 18 22:04 data1.addressbook
-rw-r--r-- 1 bozo 4602 May 25 13:58 data1.addressbook.bak
-rw-r--r-- 1 bozo 877 Dec 17 2000 employment.addressbook</computeroutput>
<prompt>bash$ </prompt><userinput>ls -al</userinput>
<computeroutput>total 14
drwxrwxr-x 2 bozo bozo 1024 Aug 29 20:54 ./
drwx------ 52 bozo bozo 3072 Aug 29 20:51 ../
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.addressbook
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.addressbook.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.addressbook
-rw-rw-r-- 1 bozo bozo 0 Aug 29 20:54 .hidden-file</computeroutput>
</screen>
</para>
</formalpara>
<para><anchor id="dotdirectory"/></para>
<para>When considering directory names, <firstterm>a single
dot</firstterm> represents the current working directory,
and <firstterm>two dots</firstterm> denote the parent
directory.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>pwd</userinput>
<computeroutput>/home/bozo/projects</computeroutput>
<prompt>bash$ </prompt><userinput>cd .</userinput>
<prompt>bash$ </prompt><userinput>pwd</userinput>
<computeroutput>/home/bozo/projects</computeroutput>
<prompt>bash$ </prompt><userinput>cd ..</userinput>
<prompt>bash$ </prompt><userinput>pwd</userinput>
<computeroutput>/home/bozo/</computeroutput>
</screen>
</para>
<para>The <firstterm>dot</firstterm> often appears as the
destination (directory) of a file movement command,
in this context meaning <firstterm>current
directory</firstterm>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cp /home/bozo/current_work/junk/* .</userinput>
</screen>
Copy all the <quote>junk</quote> files to
<link linkend="pwdref">$PWD</link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>.</token></term>
<listitem>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<indexterm>
<primary>character match</primary>
</indexterm>
<indexterm>
<primary>match single character</primary>
</indexterm>
<formalpara><title><quote>dot</quote> character match</title>
<para>When <link linkend="regexdot">matching
characters</link>, as part of a <link
linkend="regexref">regular expression</link>, a
<quote>dot</quote> <link linkend="regexdot">matches a
single character</link>.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>"</token></term>
<listitem><formalpara><title><link linkend="dblquo">partial
quoting</link> [double quote]</title>
<para><emphasis>"STRING"</emphasis> preserves (from
interpretation) most of the special characters within
<emphasis>STRING</emphasis>. See <xref
linkend="quoting"/>.</para>
</formalpara> </listitem>
</varlistentry>
<varlistentry>
<term><token>'</token></term>
<listitem><formalpara><title><link linkend="snglquo">full
quoting</link> [single quote]</title>
<para><emphasis>'STRING'</emphasis> preserves all special
characters within <emphasis>STRING</emphasis>. This is a
stronger form of quoting than <emphasis>"STRING"</emphasis>.
See <xref linkend="quoting"/>.</para>
</formalpara> </listitem>
</varlistentry>
<varlistentry>
<term><token>,</token></term>
<listitem><formalpara><title><link linkend="commaop">comma
operator</link></title>
<para>The <firstterm>comma operator</firstterm>
<footnote><para><anchor id="operatordef"/>An
<firstterm>operator</firstterm> is an agent that carries
out an <firstterm>operation</firstterm>. Some examples
are the common <link linkend="arops1">arithmetic
operators</link>, <command>+ - * /</command>. In
Bash, there is some overlap between the concepts
of <firstterm>operator</firstterm> and <link
linkend="keywordref">keyword</link>.</para></footnote>
links together a
series of arithmetic operations. All are evaluated,
but only the last one is returned.
<programlisting>let "t2 = ((a = 9, 15 / 3))"
# Set "a = 9" and "t2 = 15 / 3"</programlisting>
</para>
</formalpara>
<para><anchor id="commaop2"/>The <firstterm>comma</firstterm>
operator can also concatenate strings.
<programlisting>for file in /{,usr/}bin/*calc
# ^ Find all executable files ending in "calc"
#+ in /bin and /usr/bin directories.
do
if [ -x "$file" ]
then
echo $file
fi
done
# /bin/ipcalc
# /usr/bin/kcalc
# /usr/bin/oidcalc
# /usr/bin/oocalc
# Thank you, Rory Winston, for pointing this out.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>,</token></term>
<term><token>,</token></term>
<listitem>
<formalpara><title><link linkend="casemodparamsub">Lowercase
conversion</link> in <firstterm>parameter substitution</firstterm>
(added in <link
linkend="bash4ref">version 4</link> of Bash)</title>
<para></para></formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>\</token></term>
<listitem><formalpara><title><link linkend="escp">escape</link> [backslash]</title>
<para>A quoting mechanism for single characters.</para>
</formalpara>
<para><userinput>\X</userinput>
<firstterm>escapes</firstterm> 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>
<para>See <xref linkend="quoting"/> for an in-depth explanation
of escaped characters.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/</token></term>
<listitem><formalpara><title>Filename path separator [forward slash]</title>
<para>Separates the components of a filename (as in
<filename>/home/bozo/projects/Makefile</filename>).</para>
</formalpara>
<para>This is also the division <link
linkend="arops1">arithmetic operator</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="backticksref"/><token>`</token></term>
<listitem><formalpara><title><link
linkend="commandsubref">command substitution</link></title>
<para>The <command>`command`</command> construct makes
available the output of <command>command</command>
for assignment to a variable. This is also known as
<link linkend="backquotesref">backquotes</link> or
backticks.</para></formalpara> </listitem>
</varlistentry>
<varlistentry><term><anchor id="colon0ref"/><token>:</token></term>
<listitem>
<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>
<para><anchor id="nullref"/></para>
<formalpara><title>null command [colon]</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>. The
<quote><token>:</token></quote> command is itself a
<firstterm>Bash</firstterm> <link
linkend="builtinref">builtin</link>, and its <link
linkend="exitstatusref">exit status</link> is
<firstterm>true</firstterm>
(<returnvalue>0</returnvalue>).</para>
</formalpara>
<para><programlisting>:
echo $? # 0</programlisting></para>
<para>Endless loop:</para>
<para><programlisting>
while :
do
operation-1
operation-2
...
operation-n
done
# Same as:
# while true
# do
# ...
# done</programlisting>
</para>
<para>Placeholder in if/then test:</para>
<para><programlisting>
if condition
then : # Do nothing and branch ahead
else # Or 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`} Gives an error without the leading :
# unless "username" is a command or builtin...
: ${1?"Usage: $0 ARGUMENT"} # From "usage-message.sh example script.</programlisting>
</para>
<para>Provide a placeholder where a command is expected in a
<link linkend="heredocref">here document</link>. See <xref
linkend="anonheredoc"/>.</para>
<para>Evaluate string of variables using
<link linkend="paramsubref">parameter substitution</link>
(as in <xref linkend="ex6"/>).
<programlisting>: ${HOSTNAME?} ${USER?} ${MAIL?}
# Prints error message
#+ if one or more of essential environmental variables not set.</programlisting>
</para>
<para><command><link linkend="exprepl1">Variable expansion / substring
replacement</link></command>.</para>
<para>In combination with the <token>&gt;</token> <link
linkend="ioredirref">redirection operator</link>,
truncates a file to zero length, without changing its
permissions. If the file did not previously exist,
creates it.
<programlisting>: > data.xxx # File "data.xxx" now empty.
# Same effect as cat /dev/null >data.xxx
# However, this does not fork a new process, since ":" is a builtin.</programlisting>
See also <xref linkend="ex12"/>.</para>
<para>In combination with the <token>&gt;&gt;</token>
redirection operator, has no effect on a pre-existing
target file (<userinput>: &gt;&gt; target_file</userinput>).
If the file did not previously exist, creates it.</para>
<note><para><anchor id="regfileref"/>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 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>
<para>The <quote><token>:</token></quote> serves as a <link
linkend="fieldref">field</link>
separator, in <link
linkend="datafilesref1"><filename>/etc/passwd</filename></link>,
and in the <link linkend="pathref">$PATH</link> variable.
<screen><prompt>bash$ </prompt><userinput>echo $PATH</userinput>
<computeroutput>/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games</computeroutput></screen>
</para>
<para><anchor id="colonfname"/></para>
<para>A <firstterm>colon</firstterm> is <link
linkend="fstrangeref">acceptable as a function name</link>.
<programlisting>:()
{
echo "The name of this function is "$FUNCNAME" "
# Why use a colon as a function name?
# It's a way of obfuscating your code.
}
:
# The name of this function is :</programlisting>
This is not <link linkend="portabilityissues">portable</link>
behavior, and therefore not a recommended practice.
In fact, more recent releases of Bash do not permit
this usage. An underscore <command>_</command> works,
though.</para>
<para><anchor id="coloninfunction"/></para>
<para>A <firstterm>colon</firstterm> can serve
as a placeholder in an otherwise empty
function.</para>
<para><programlisting>not_empty ()
{
:
} # Contains a : (null command), and so is not empty.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry><term><anchor id="notref"/><token>!</token></term>
<listitem>
<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>
<formalpara><title>reverse (or negate) the sense of
a test or exit status [bang]</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 <firstterm>equal</firstterm>
( <link linkend="equalsignref">=</link>
) to <firstterm>not-equal</firstterm> ( != ). The
<token>!</token> operator is a Bash <link
linkend="keywordref">keyword</link>.</para>
</formalpara>
<para>In a different context, the <token>!</token>
also appears in <link linkend="ivrref">indirect variable
references</link>.</para>
<para>In yet another context, from the <firstterm>command
line</firstterm>, the <token>!</token> invokes the
Bash <firstterm>history mechanism</firstterm> (see <xref
linkend="histcommands"/>). Note that within a script,
the history mechanism is disabled.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="asteriskref"/><token>*</token></term>
<listitem>
<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>
<formalpara><title>wild card [asterisk]</title>
<para>The <token>*</token> character serves as a <quote>wild
card</quote> for filename expansion in
<link linkend="globbingref">globbing</link>. By itself,
it matches every filename in a given directory.</para>
</formalpara>
<para>
<screen><prompt>bash$ </prompt><userinput>echo *</userinput>
<computeroutput>abs-book.xml add-drive.sh agram.sh alias.sh</computeroutput>
</screen>
</para>
<para><anchor id="asteriskref2"/></para>
<para>The <token>*</token> also represents <link
linkend="asteriskreg">any number
(or zero) characters</link> in a <link
linkend="regexref">regular expression</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*</token></term>
<listitem>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>*</secondary>
</indexterm>
<indexterm>
<primary>multiplication</primary>
<secondary>exponentiation</secondary>
</indexterm>
<indexterm>
<primary>arithmetic operator</primary>
</indexterm>
<formalpara><title><link linkend="arops1">arithmetic operator</link></title>
<para>In the context of arithmetic operations, the
<token>*</token> denotes multiplication.</para>
</formalpara>
<para><token>**</token> A double asterisk can represent the
<link linkend="exponentiationref">exponentiation</link>
operator or <link linkend="globstarref">extended
file-match</link> <firstterm>globbing</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>?</token></term>
<listitem>
<indexterm>
<primary>?</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>?</secondary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>operator</secondary>
</indexterm>
<indexterm>
<primary>test token</primary>
</indexterm>
<formalpara><title>test operator</title>
<para>Within certain expressions, the <token>?</token> indicates
a test for a condition.</para>
</formalpara>
<para><anchor id="cstrinary"/></para>
<para>In a <link linkend="dblparens">double-parentheses
construct</link>, the <token>?</token>
can serve as an element of a C-style
<firstterm>trinary</firstterm> operator.
<footnote><para>This is more commonly known
as the <firstterm>ternary</firstterm>
operator. Unfortunately, <firstterm>ternary</firstterm>
is an ugly word. It doesn't roll off
the tongue, and it doesn't elucidate. It
obfuscates. <firstterm>Trinary</firstterm> is by far
the more elegant usage.</para></footnote>
</para>
<para><varname>condition</varname><command>?</command><varname>result-if-true</varname><command>:</command><varname>result-if-false</varname></para>
<para><programlisting>(( var0 = var1&lt;98?9:21 ))
# ^ ^
# if [ "$var1" -lt 98 ]
# then
# var0=9
# else
# var0=21
# fi</programlisting></para>
<para>In a <link linkend="paramsubref">parameter
substitution</link> expression, the <token>?</token>
<link linkend="qerrmsg">tests whether a variable has been
set</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="wildcardqu"/><token>?</token></term>
<listitem>
<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>
<formalpara><title>wild card</title>
<para><anchor id="quexwc"/>The <token>?</token> character
serves as a single-character <quote>wild card</quote>
for filename expansion in <link
linkend="globbingref">globbing</link>, as well as <link
linkend="quexregex">representing one character</link>
in an <link linkend="extregex">extended regular
expression</link>.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry><term><token>$</token></term>
<listitem>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<indexterm>
<primary>variable substitution</primary>
</indexterm>
<formalpara><title><link linkend="varsubn">Variable
substitution</link> (contents of a variable)</title>
<para>
<programlisting>var1=5
var2=23skidoo
echo $var1 # 5
echo $var2 # 23skidoo</programlisting>
</para>
</formalpara>
<para><anchor id="varprefixref"/></para>
<para>A <token>$</token> prefixing a variable name
indicates the <firstterm>value</firstterm> the variable
holds.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>$</token></term>
<listitem>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<indexterm>
<primary>regular expression</primary>
</indexterm>
<indexterm>
<primary>end of line</primary>
</indexterm>
<formalpara><title>end-of-line</title>
<para>In a <link linkend="regexref">regular expression</link>, a
<quote>$</quote> addresses the <link
linkend="dollarsignref">end of a line</link> of
text.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry><term><token>${}</token></term>
<listitem>
<indexterm>
<primary>${}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>${}</secondary>
</indexterm>
<indexterm>
<primary>parameter substitution</primary>
</indexterm>
<formalpara><title><link linkend="paramsubref">Parameter
substitution</link></title>
<para></para></formalpara>
</listitem>
</varlistentry>
<varlistentry><term><token>$' ... '</token></term>
<listitem>
<indexterm>
<primary>$' ... '</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<indexterm>
<primary>string expansion</primary>
</indexterm>
<indexterm>
<primary>string expansion</primary>
</indexterm>
<formalpara><title><link linkend="strq">Quoted string
expansion</link></title>
<para>This construct expands single or multiple
escaped octal or hex values into ASCII
<footnote>
<para><anchor id="asciidef"/></para>
<para><command>A</command>merican
<command>S</command>tandard
<command>C</command>ode
for
<command>I</command>nformation
<command>I</command>nterchange.
This is a system for encoding text characters
(alphabetic, numeric, and a limited set of symbols)
as 7-bit numbers that can be stored and manipulated by
computers. Many of the ASCII characters are
represented on a standard keyboard.</para></footnote>
or <link linkend="unicoderef">Unicode</link>
characters.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>$*</token></term>
<term><token>$@</token></term>
<listitem>
<indexterm>
<primary>$*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$*</secondary>
</indexterm>
<indexterm>
<primary>$@</primary>
<secondary>positional parameters</secondary>
</indexterm>
<indexterm>
<primary>$@</primary>
</indexterm>
<formalpara><title><link linkend="appref">positional
parameters</link></title>
<para></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>$?</token></term>
<listitem>
<indexterm>
<primary>$?</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>?</secondary>
</indexterm>
<indexterm>
<primary>exit status</primary>
<secondary>variable</secondary>
</indexterm>
<indexterm>
<primary>exit status</primary>
</indexterm>
<formalpara><title>exit status variable</title>
<para>The <link linkend="exsref">$? variable</link>
holds the <link linkend="exitstatusref">exit status</link>
of a command, a <link linkend="functionref">function</link>,
or of the script itself.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="processidref"/><token>$$</token></term>
<listitem>
<indexterm>
<primary>$$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$$</secondary>
</indexterm>
<indexterm>
<primary>process ID</primary>
<secondary>variable</secondary>
</indexterm>
<indexterm>
<primary>process ID</primary>
</indexterm>
<formalpara><title>process ID variable</title>
<para>The <link linkend="proccid">$$ variable</link>
holds the <firstterm>process ID</firstterm>
<footnote>
<para><anchor id="processiddef"/></para>
<para>A <firstterm>PID</firstterm>, or
<firstterm>process ID</firstterm>, is a number assigned
to a running process. The <firstterm>PID</firstterm>s
of running processes may be viewed with a <link
linkend="ppssref">ps</link> command.
</para>
<para><anchor id="processref"/></para>
<para>
<userinput>Definition:</userinput> A
<firstterm>process</firstterm> is a currently
executing command (or program), sometimes referred
to as a <firstterm>job</firstterm>. </para>
</footnote>
of the script in which it appears.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="parensref"/><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>
<anchor id="arrayinit1"/>
<programlisting>Array=(element1 element2 element3)</programlisting>
</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>{xxx,yyy,zzz,...}</token></term>
<listitem>
<indexterm>
<primary>{xxx,yyy,zzz..}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>brace expansion</primary>
</indexterm>
<formalpara><title>Brace expansion</title>
<para><anchor id="braceexpref"/>
<programlisting>echo \"{These,words,are,quoted}\" # " prefix and suffix
# "These" "words" "are" "quoted"
cat {file1,file2,file3} > combined_file
# Concatenates the files file1, file2, and file3 into combined_file.
cp file22.{txt,backup}
# Copies "file22.txt" to "file22.backup"</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 <firstterm>brace
expansion</firstterm>. 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="braceexpref33"/><token>{a..z}</token></term>
<listitem>
<indexterm>
<primary>{a..z}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>extended brace expansion</primary>
</indexterm>
<formalpara>
<title>Extended Brace expansion</title>
<para>
<programlisting>echo {a..z} # a b c d e f g h i j k l m n o p q r s t u v w x y z
# Echoes characters between a and z.
echo {0..3} # 0 1 2 3
# Echoes characters between 0 and 3.
base64_charset=( {A..Z} {a..z} {0..9} + / = )
# Initializing an array, using extended brace expansion.
# From vladz's "base64.sh" example script.</programlisting>
</para>
</formalpara>
<para>The <firstterm>{a..z}</firstterm>
<link linkend="braceexpref3">extended brace
expansion</link> construction is a feature introduced
in <link linkend="bash3ref">version 3</link> of
<firstterm>Bash</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="codeblockref"/><token>{}</token></term>
<listitem>
<indexterm>
<primary>{}</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>{}</secondary>
</indexterm>
<indexterm>
<primary>block of code</primary>
</indexterm>
<formalpara><title>Block of code [curly brackets]</title>
<para>Also referred to as an <firstterm>inline group</firstterm>,
this construct, in effect, creates an <firstterm>anonymous
function</firstterm> (a function without a
name). However, unlike in a <quote>standard</quote> <link
linkend="functionref">function</link>, the variables
inside 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><anchor id="blockio"/></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>
<para><anchor id="blockio2"/></para>
<example id="rpmcheck">
<title>Saving the output 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> run as a
<link linkend="subshellsref">subshell</link>.</para>
<para><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" # Won't work.
# Thanks, S.C.</programlisting></para>
</footnote>
</para>
<para>It is possible to <link
linkend="iterationref">iterate</link> a code block
using a <link linkend="nododone">non-standard
<firstterm>for-loop</firstterm></link>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><token>{}</token></term>
<listitem>
<formalpara><title>placeholder for text</title>
<para>Used after <link linkend="xargscurlyref">xargs
<option>-i</option></link> (<firstterm>replace
strings</firstterm> option). The <token>{}</token> double
curly brackets are a placeholder for output text.</para>
</formalpara>
<para><programlisting>ls . | xargs -i -t cp ./{} $1
# ^^ ^^
# From "ex42.sh" (copydir.sh) example.</programlisting></para>
<para><anchor id="semicolonesc"/></para>
</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>
<sidebar><para><anchor id="pathnameref"/></para>
<para>Definition: A <firstterm>pathname</firstterm>
is a <firstterm>filename</firstterm> that includes the
complete <link linkend="pathref">path</link>. As an example,
<filename>/home/bozo/Notes/Thursday/schedule.txt</filename>.
This is sometimes referred to as the <firstterm>absolute
path</firstterm>.</para></sidebar>
<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><anchor id="leftbracket"/><token>[ ]</token></term>
<listitem>
<indexterm>
<primary>[]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[ ]</secondary>
</indexterm>
<indexterm>
<primary>test</primary>
</indexterm>
<formalpara><title>test</title>
<para><anchor id="bracktest"/></para></formalpara>
<para><link linkend="ifthen">Test</link> expression between
<command>[ ]</command>. Note that <command>[</command>
is part of the shell <firstterm>builtin</firstterm> <link
linkend="ttestref">test</link> (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>
<listitem>
<indexterm>
<primary>[[]]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[[ ]]</secondary>
</indexterm>
<indexterm>
<primary>test</primary>
</indexterm>
<formalpara><title>test</title>
<para></para></formalpara>
<para>Test expression between <token>[[ ]]</token>. More
flexible than the single-bracket <token>[ ]</token> test,
this is a shell <link
linkend="keywordref">keyword</link>.</para> <para>See the
discussion on the <link linkend="dblbrackets">[[ ... ]]
construct</link>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>[ ]</token></term>
<listitem>
<indexterm>
<primary>[ ]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>array_element[ ]</secondary>
</indexterm>
<indexterm>
<primary>array element</primary>
</indexterm>
<formalpara><title>array element</title>
<para></para></formalpara>
<para>In the context of an <link linkend="arrayref">array</link>,
brackets set off the numbering of each element of that array.
<programlisting>Array[1]=slot_1
echo ${Array[1]}</programlisting></para>
</listitem>
</varlistentry>
<varlistentry><term><token>[ ]</token></term>
<listitem>
<indexterm>
<primary>[ ]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>character range</secondary>
</indexterm>
<indexterm>
<primary>regular expression</primary>
</indexterm>
<formalpara><title>range of characters</title>
<para></para></formalpara>
<para>As part of a <link linkend="regexref">regular
expression</link>, brackets delineate a <link
linkend="bracketsref">range of characters</link> to
match.</para>
</listitem>
</varlistentry>
<varlistentry><term><anchor id="bracketarith"/><token>$[ ... ]</token></term>
<listitem>
<indexterm>
<primary>$[ ]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>integer expansion</secondary>
</indexterm>
<indexterm>
<primary>integer arithmetic (obsolete)</primary>
</indexterm>
<formalpara><title>integer expansion</title>
<para></para></formalpara>
<para>Evaluate integer expression between
<token>$[ ]</token>.
<programlisting>a=3
b=7
echo $[$a+$b] # 10
echo $[$a*$b] # 21</programlisting></para>
<para>Note that this usage is <emphasis>deprecated</emphasis>,
and has been replaced by the
<link linkend="dblparens">(( ... ))</link> construct.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>(( ))</token></term>
<listitem>
<indexterm>
<primary>(( ))</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>(( ))</secondary>
</indexterm>
<indexterm>
<primary>integer comparison</primary>
</indexterm>
<formalpara><title>integer expansion</title>
<para></para></formalpara>
<para>Expand and evaluate integer expression between
<token>(( ))</token>.</para>
<para>See the discussion on the <link
linkend="dblparens">(( ... )) construct</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&gt;</token> <token>&amp;&gt;</token> <token>&gt;&amp;</token> <token>&gt;&gt;</token> <token>&lt;</token> <token>&lt;&gt;</token></term>
<listitem>
<indexterm>
<primary>></primary>
</indexterm>
<indexterm>
<primary>>&amp;</primary>
</indexterm>
<indexterm>
<primary>>></primary>
</indexterm>
<indexterm>
<primary>&lt;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>></secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>>&amp;</secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>>></secondary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&lt;</secondary>
</indexterm>
<indexterm>
<primary>redirection</primary>
</indexterm>
<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><anchor id="redirouterror"/></para>
<para><userinput>command &amp;>filename</userinput> redirects
both the <link
linkend="stdinoutdef"><filename>stdout</filename></link>
and the
<filename>stderr</filename> of <filename>command</filename>
to <filename>filename</filename>.</para>
<note>
<para>
<anchor id="devnullredirect"/>
This is useful for suppressing output when
testing for a condition. For example, let us
test whether a certain command exists.
</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>type bogus_command &amp;>/dev/null</userinput>
<computeroutput></computeroutput>
<prompt>bash$ </prompt><userinput>echo $?</userinput>
<computeroutput>1</computeroutput>
</screen>
</para>
<para>Or in a script:</para>
<para><programlisting>command_test () { type "$1" &amp;>/dev/null; }
# ^
cmd=rmdir # Legitimate command.
command_test $cmd; echo $? # 0
cmd=bogus_command # Illegitimate command
command_test $cmd; echo $? # 1</programlisting></para>
</note>
<para><anchor id="redirouterror2"/></para>
<para><userinput>command >&amp;2</userinput> redirects
<filename>stdout</filename> 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 is created.</para>
<para><anchor id="redirrw"/></para>
<para><userinput>[i]&lt;&gt;filename</userinput>
opens file <filename>filename</filename> for reading
and writing, and assigns <link linkend="fdref">file
descriptor</link> <token>i</token> to it. If
<filename>filename</filename> does not exist, it is
created.</para>
<formalpara><title><link linkend="processsubref">process substitution</link></title>
<para></para>
</formalpara>
<para><userinput>(command)></userinput></para>
<para><userinput>&lt;(command)</userinput></para>
<para><link linkend="ltref">In a different context</link>,
the <quote><token>&lt;</token></quote> and
<quote><token>&gt;</token></quote> characters act
as <link linkend="scomparison1">string comparison
operators</link>.</para>
<para><link linkend="intlt">In yet another context</link>,
the <quote><token>&lt;</token></quote> and
<quote><token>&gt;</token></quote> characters act
as <link linkend="icomparison1">integer comparison
operators</link>. See also <xref linkend="ex45"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="heredocrrref"/><token>&lt;&lt;</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="herestringref"/><token>&lt;&lt;&lt;</token></term>
<listitem><formalpara><title>redirection used in a <link
linkend="herestringsref">here string</link></title>
<para></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&lt;</token></term>
<term><token>></token></term>
<listitem>
<indexterm>
<primary>&lt;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&lt;</secondary>
</indexterm>
<indexterm>
<primary>></primary>
<secondary>ASCII comparison</secondary>
</indexterm>
<indexterm>
<primary>></primary>
</indexterm>
<formalpara><title><link linkend="ltref">ASCII
comparison</link></title>
<para><programlisting>veg1=carrots
veg2=tomatoes
if [[ "$veg1" &lt; "$veg2" ]]
then
echo "Although $veg1 precede $veg2 in the dictionary,"
echo -n "this does not necessarily imply anything "
echo "about my culinary preferences."
else
echo "What kind of dictionary are you using, anyhow?"
fi</programlisting></para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>\&lt;</token></term>
<term><token>\></token></term>
<listitem>
<indexterm>
<primary>\&lt;</primary>
</indexterm>
<indexterm>
<primary>regular expression</primary>
<secondary>\&lt;</secondary>
</indexterm>
<indexterm>
<primary>></primary>
<secondary>word boundary</secondary>
</indexterm>
<indexterm>
<primary>></primary>
</indexterm>
<formalpara><title><link linkend="anglebrac">word
boundary</link> in a <link linkend="regexref">regular
expression</link></title>
<para></para>
</formalpara>
<para><prompt>bash$ </prompt><userinput>grep '\&lt;the\&gt;' textfile</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|</token></term>
<listitem>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>|</secondary>
</indexterm>
<indexterm>
<primary>pipe</primary>
</indexterm>
<para><anchor id="piperef"/></para>
<formalpara><title>pipe</title>
<para>Passes the output (<filename>stdout</filename>)
of a previous command to the input
(<filename>stdin</filename>) of the next one, or
to the shell. This is a method of chaining commands
together.</para>
</formalpara>
<para>
<programlisting>echo ls -l | sh
# Passes the output of "echo ls -l" to the shell,
#+ with the same result as a simple "ls -l".
cat *.lst | sort | uniq
# Merges and sorts all ".lst" files, then deletes duplicate lines.</programlisting>
</para>
<sidebar>
<para>
A pipe, as a classic method of interprocess
communication, sends the <filename>stdout</filename>
of one <link linkend="processref">process</link> to the
<filename>stdin</filename> of another. In a typical case,
a command, such as <link linkend="catref">cat</link> or
<link linkend="echoref">echo</link>, pipes a stream of
data to a
<anchor id="filterdef"/>
<firstterm>filter</firstterm>, a command that
transforms its input for processing.
<footnote><para> Even as in olden times a
<firstterm>philtre</firstterm> denoted a potion alleged
to have magical transformative powers, so does a UNIX
<firstterm>filter</firstterm> transform its target in
(roughly) analogous fashion. (The coder who comes up with a
<quote>love philtre</quote> that runs on a Linux machine
will likely win accolades and honors.)</para></footnote>
</para>
<para>
<userinput>cat $filename1 $filename2 | grep $search_word</userinput>
</para>
<para>For an interesting note on the complexity of using UNIX
pipes, see <ulink
url="http://www.faqs.org/faqs/unix-faq/faq/part3/">the UNIX FAQ,
Part 3</ulink>.</para>
</sidebar>
<para><anchor id="ucref"/>The output of a command or commands
may be piped to a script.
<programlisting>#!/bin/bash
# uppercase.sh : Changes input to uppercase.
tr 'a-z' 'A-Z'
# Letter ranges must be quoted
#+ to prevent filename generation from single-letter filenames.
exit 0</programlisting>
Now, let us pipe the output of <command>ls -l</command> to this
script.
<screen><prompt>bash$ </prompt><userinput>ls -l | ./uppercase.sh</userinput>
<computeroutput>-RW-RW-R-- 1 BOZO BOZO 109 APR 7 19:49 1.TXT
-RW-RW-R-- 1 BOZO BOZO 109 APR 14 16:48 2.TXT
-RW-R--R-- 1 BOZO BOZO 725 APR 20 20:56 DATA-FILE</computeroutput>
</screen>
</para>
<note>
<para>The <filename>stdout</filename> of each process in
a pipe must be read as the <filename>stdin</filename>
of the next. If this is not the case, the data stream
will <firstterm>block</firstterm>, and the pipe will not
behave as expected.
<programlisting>cat file1 file2 | ls -l | sort
# The output from "cat file1 file2" disappears.</programlisting>
</para>
<para>A pipe runs as a <link linkend="childref">child
process</link>, and therefore cannot alter script
variables.
<programlisting>variable="initial_value"
echo "new_value" | read variable
echo "variable = $variable" # variable = initial_value</programlisting>
</para>
<para>If one of the commands in the pipe
aborts, this prematurely terminates execution of the
pipe. Called a <firstterm>broken pipe</firstterm>, this
condition sends a <replaceable>SIGPIPE</replaceable> <link
linkend="signald">signal</link>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>|</token></term>
<listitem>
<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>
<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>
<listitem>
<indexterm>
<primary>||</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>||</secondary>
</indexterm>
<indexterm>
<primary>or</primary>
</indexterm>
<indexterm>
<primary>logical operator</primary>
</indexterm>
<formalpara><title><link linkend="orref">OR logical operator</link></title>
<para>In a <link linkend="testconstructs1">test
construct</link>, the <token>||</token> operator causes
a return of <returnvalue>0</returnvalue> (success) if
<emphasis>either</emphasis> of the linked test conditions
is true.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bgjob"/><token>&amp;</token></term>
<listitem><formalpara><title>Run job in background</title>
<para>A command followed by an <token>&amp;</token>
will run in the background.</para>
</formalpara>
<para>
<screen><prompt>bash$ </prompt><userinput>sleep 10 &amp;</userinput>
<computeroutput>[1] 850</computeroutput>
<computeroutput>[1]+ Done sleep 10</computeroutput>
</screen>
</para>
<para>Within a script, commands and even <link
linkend="forloopref1">loops</link> may run in the
background.</para>
<para><anchor id="bgloop0"/></para>
<example id="bgloop">
<title>Running a loop in the background</title>
<programlisting>&bgloop;</programlisting>
</example>
<caution><para>A command run in the background within a
script may cause the script to hang, waiting
for a keystroke. Fortunately, there is a <link
linkend="waithang">remedy</link> for this.</para></caution>
</listitem>
</varlistentry>
<varlistentry><term><anchor
id="logicaland"/><token>&amp;&amp;</token></term>
<listitem>
<indexterm>
<primary>&amp;&amp;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&amp;&amp;</secondary>
</indexterm>
<indexterm>
<primary>and</primary>
</indexterm>
<indexterm>
<primary>logical operator</primary>
</indexterm>
<formalpara><title><link linkend="logops1">AND logical
operator</link></title>
<para>In a <link linkend="testconstructs1">test
construct</link>, the <token>&amp;&amp;</token> operator causes
a return of <returnvalue>0</returnvalue> (success) only if
<emphasis>both</emphasis> the linked test conditions
are true.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dashref"/><token>-</token></term>
<listitem><formalpara><title>option, prefix</title>
<para>Option flag for a command or filter. Prefix for
an operator. Prefix for a <link
linkend="defparam1">default parameter</link>
in <link linkend="paramsubref">parameter
substitution</link>.</para>
</formalpara>
<para><userinput>COMMAND -[Option1][Option2][...]</userinput></para>
<para><userinput>ls -al</userinput></para>
<para><userinput>sort -dfu $filename</userinput></para>
<para>
<programlisting>if [ $file1 -ot $file2 ]
then # ^
echo "File $file1 is older than $file2."
fi
if [ "$a" -eq "$b" ]
then # ^
echo "$a is equal to $b."
fi
if [ "$c" -eq 24 -a "$d" -eq 47 ]
then # ^ ^
echo "$c equals 24 and $d equals 47."
fi
param2=${param1:-$DEFAULTVAL}
# ^</programlisting>
</para>
<para><anchor id="doubledashref"/></para>
<para><command>--</command></para>
<para>The <firstterm>double-dash</firstterm>
<option>--</option> prefixes <firstterm>long</firstterm>
(verbatim) options to commands.</para>
<para><userinput>sort --ignore-leading-blanks</userinput></para>
<para>Used with a <link linkend="builtinref">Bash
builtin</link>, it means the <firstterm>end of
options</firstterm> to that particular command.</para>
<tip><para>This provides a handy means of removing
files whose <emphasis>names begin with a dash</emphasis>.
<screen><prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>-rw-r--r-- 1 bozo bozo 0 Nov 25 12:29 -badname</computeroutput>
<prompt>bash$ </prompt><userinput>rm -- -badname</userinput>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 0</computeroutput></screen>
</para></tip>
<para>The <firstterm>double-dash</firstterm> is also used in
conjunction with <link linkend="setref">set</link>.</para>
<para><userinput>set -- $variable</userinput> (as in <xref
linkend="setpos"/>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dashref2"/><token>-</token></term>
<listitem>
<indexterm>
<primary>-</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>-</secondary>
</indexterm>
<indexterm>
<primary>redirection</primary>
<secondary>from/to stdin/stdout</secondary>
</indexterm>
<formalpara><title>redirection from/to <filename>stdin</filename> or <filename>stdout</filename> [dash]</title>
<para><anchor id="coxex"/></para>
</formalpara>
<para>
<screen><prompt>bash$ </prompt><userinput>cat -</userinput>
<userinput>abc</userinput>
<computeroutput>abc</computeroutput>
<computeroutput>...</computeroutput>
<userinput>Ctl-D</userinput></screen>
</para>
<para>As expected, <userinput>cat -</userinput> echoes
<filename>stdin</filename>, in this case keyboarded user input,
to <filename>stdout</filename>. But, does I/O redirection using
<command>-</command> have real-world applications?</para>
<para><programlisting>(cd /source/directory &amp;&amp; tar cf - . ) | (cd /dest/directory &amp;&amp; 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) &amp;&amp;
# "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) &amp;&amp;
# "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 ../dest/directory; tar xpvf -)
#
# Also having same effect:
# cp -a /source/directory/* /dest/directory
# Or:
# cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
# If there are hidden files in /source/directory.
</programlisting></para>
<para><programlisting>bunzip2 -c linux-2.6.16.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>
<screen><prompt>bash$ </prompt><userinput>echo "whatever" | cat -</userinput>
<computeroutput>whatever</computeroutput> </screen>
</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.
<anchor id="filterdash"/>
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>
Add a <quote>-</quote> for a more useful result. This causes the
shell to await user input.
<screen>
<prompt>bash$ </prompt><userinput>file -</userinput>
<userinput>abc</userinput>
<computeroutput>standard input: ASCII text</computeroutput>
<prompt>bash$ </prompt><userinput>file -</userinput>
<userinput>#!/bin/bash</userinput>
<computeroutput>standard input: Bourne-Again shell script text executable</computeroutput>
</screen>
Now the command accepts input from <filename>stdin</filename>
and analyzes it.
</para>
<para>The <quote>-</quote> can be used to pipe
<filename>stdout</filename> to other commands. This permits
such stunts as <link linkend="prependref">prepending lines
to a file</link>.</para>
<para>Using <link linkend="diffref">diff</link> to
compare a file with a <emphasis>section</emphasis>
of another:</para>
<para><userinput>grep Linux 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
<quote>-</quote> may cause problems when coupled with the
<quote>-</quote> redirection operator. A script should
check for this and add an appropriate prefix to such
filenames, for example <filename>./-FILENAME</filename>,
<filename>$PWD/-FILENAME</filename>, or
<filename>$PATHNAME/-FILENAME</filename>.</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>A <command>cd -</command> command changes to the
previous working directory. This uses the
<link linkend="oldpwd">$OLDPWD</link> <link
linkend="envref">environmental variable</link>.</para>
</formalpara>
<caution><para>Do not confuse the <quote>-</quote> used in this
sense with the <quote>-</quote> redirection
operator just discussed. The interpretation of the
<quote>-</quote> depends on the context in which it
appears.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<listitem><formalpara><title>Minus</title>
<para>Minus sign in an <link linkend="arops1">arithmetic
operation</link>.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>=</token></term>
<listitem><formalpara><title>Equals</title>
<para><link linkend="eqref">Assignment operator</link>
<programlisting>a=28
echo $a # 28</programlisting></para>
</formalpara>
<para>In a <link linkend="equalsignref">different context</link>,
the <quote><token>=</token></quote> is a <link
linkend="scomparison1">string comparison</link>
operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>+</token></term>
<listitem><formalpara><title>Plus</title>
<para>Addition <link linkend="arops1">arithmetic
operator</link>.</para>
</formalpara>
<para>In a <link linkend="plusref">different context</link>,
the <token>+</token> is a <link linkend="regexp">Regular
Expression</link> operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>+</token></term>
<listitem><formalpara><title>Option</title>
<para>Option flag for a command or filter.</para>
</formalpara>
<para>Certain commands and <link
linkend="builtinref">builtins</link> use the
<option>+</option> to enable certain options and the
<option>-</option> to disable them. In <link
linkend="paramsubref">parameter substitution</link>,
the <option>+</option> prefixes an <link linkend="paramaltv">
alternate value</link> that a variable expands to.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="modulo00"/><token>%</token></term>
<listitem><formalpara><title><link linkend="moduloref">modulo</link></title>
<para>Modulo (remainder of a division) <link linkend="arops1">arithmetic
operation</link>.</para>
</formalpara>
<para><programlisting>let "z = 5 % 3"
echo $z # 2</programlisting></para>
<para>In a <link linkend="pctpatref">different context</link>,
the <token>%</token> is a <link linkend="psub2">pattern
matching</link> operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tilderef"/><token>~</token></term>
<listitem><formalpara><title>home directory [tilde]</title>
<para>This corresponds to the <link
linkend="homedirref">$HOME</link> internal variable.
<filename>~bozo</filename> 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><anchor id="workingdirref"/><token>~+</token></term>
<listitem><formalpara><title>current working directory</title>
<para>This corresponds to the <link
linkend="pwdref">$PWD</link> internal variable.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="prevworkingdir"/><token>~-</token></term>
<listitem><formalpara><title>previous working directory</title>
<para>This corresponds to the <link
linkend="oldpwd">$OLDPWD</link> internal variable.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>=~</token></term>
<listitem><formalpara><title><link linkend="regexmatchref">regular
expression match</link></title>
<para>This operator was introduced with <link
linkend="bash3ref">version 3</link> of Bash.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry><term><anchor id="beglineref"/><token>^</token></term>
<listitem>
<indexterm>
<primary>^</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>^</secondary>
</indexterm>
<indexterm>
<primary>regular expression</primary>
</indexterm>
<indexterm>
<primary>beginning of line</primary>
</indexterm>
<indexterm>
<primary>uppercase modification</primary>
<secondary>parameter substitution</secondary>
</indexterm>
<formalpara><title>beginning-of-line</title>
<para>In a <link linkend="regexref">regular expression</link>, a
<quote>^</quote> addresses the <link
linkend="caretref">beginning of a line</link> of text.</para>
</formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><token>^</token></term>
<term><token>^^</token></term>
<listitem>
<formalpara><title><link linkend="casemodparamsub">Uppercase
conversion</link> in <firstterm>parameter substitution</firstterm>
(added in <link
linkend="bash4ref">version 4</link> of Bash)</title>
<para></para></formalpara>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="controlcharref"/>Control Characters</term>
<listitem>
<formalpara><title> change the behavior of the
terminal or text display.</title>
<para>A control character is a <keycap>CONTROL</keycap>
+ <keycap>key</keycap> combination (pressed
simultaneously).
A control character may also
be written in <firstterm>octal</firstterm> or
<firstterm>hexadecimal</firstterm> notation,
following an <firstterm>escape</firstterm>.</para>
</formalpara>
<para>Control characters are not normally useful inside a
script.</para>
<itemizedlist id="ctlchar">
<listitem>
<para><userinput>Ctl-A</userinput></para>
<para>Moves cursor to beginning of line of text
(on the command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-B</userinput></para>
<para><userinput>Backspace</userinput>
(nondestructive).</para>
</listitem>
<listitem>
<para><anchor id="ctlcref"/></para>
<para><userinput>Ctl-C</userinput></para>
<para><userinput>Break</userinput>.
Terminate a foreground job.</para>
</listitem>
<listitem>
<para><anchor id="ctldref"/></para>
<para><userinput>Ctl-D</userinput></para>
<para><firstterm>Log out</firstterm> from a shell (similar to
<link linkend="exitcommandref">exit</link>).</para>
<para><userinput>EOF</userinput> (end-of-file). This also
terminates input from <filename>stdin</filename>.</para>
<para>When typing text on the console or in an
<firstterm>xterm</firstterm> window,
<userinput>Ctl-D</userinput> erases the character under the
cursor. When there are no characters present,
<userinput>Ctl-D</userinput> logs out of the session, as
expected. In an <firstterm>xterm</firstterm> window,
this has the effect of closing the window.</para>
</listitem>
<listitem>
<para><userinput>Ctl-E</userinput></para>
<para>Moves cursor to end of line of text
(on the command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-F</userinput></para>
<para>Moves cursor forward one character position
(on the command-line).</para>
</listitem>
<listitem>
<para><anchor id="ctlgref"/></para>
<para><userinput>Ctl-G</userinput></para>
<para><userinput>BEL</userinput>. On some
old-time teletype terminals, this would actually ring
a bell. In an <firstterm>xterm</firstterm> it might
beep.</para>
</listitem>
<listitem>
<para><anchor id="ctlhref"/></para>
<para><userinput>Ctl-H</userinput></para>
<para><userinput>Rubout</userinput> (destructive backspace).
Erases characters the cursor backs over while
backspacing.</para>
<para>
<programlisting>#!/bin/bash
# Embedding Ctl-H in a string.
a="^H^H" # Two Ctl-H's -- backspaces
# ctl-V ctl-H, using vi/vim
echo "abcdef" # abcdef
echo
echo -n "abcdef$a " # abcd f
# Space at end ^ ^ Backspaces twice.
echo
echo -n "abcdef$a" # abcdef
# No space at end ^ Doesn't backspace (why?).
# Results may not be quite as expected.
echo; echo
# Constantin Hagemeier suggests trying:
# a=$'\010\010'
# a=$'\b\b'
# a=$'\x08\x08'
# But, this does not change the results.
########################################
# Now, try this.
rubout="^H^H^H^H^H" # 5 x Ctl-H.
echo -n "12345678"
sleep 2
echo -n "$rubout"
sleep 2</programlisting>
</para>
</listitem>
<listitem>
<para><userinput>Ctl-I</userinput></para>
<para><userinput>Horizontal tab</userinput>.</para>
</listitem>
<listitem>
<para><anchor id="ctljref"/></para>
<para><userinput>Ctl-J</userinput></para>
<para><userinput>Newline</userinput> (line feed).
In a script, may also be expressed in octal notation --
'\012' or in hexadecimal -- '\x0a'.</para>
</listitem>
<listitem>
<para><userinput>Ctl-K</userinput></para>
<para><userinput>Vertical tab</userinput>.</para>
<para>When typing text on the console or in an
<firstterm>xterm</firstterm> window,
<userinput>Ctl-K</userinput> erases from the character
under the cursor to end of line. Within a script,
<userinput>Ctl-K</userinput> may behave differently,
as in Lee Lee Maschmeyer's example, below.</para>
</listitem>
<listitem>
<para><userinput>Ctl-L</userinput></para>
<para><userinput>Formfeed</userinput> (clear the terminal
screen). In a terminal, this has the same effect as the
<link linkend="clearref">clear</link> command. When sent
to a printer, a <userinput>Ctl-L</userinput> causes
an advance to end of the paper sheet.</para>
</listitem>
<listitem>
<para><anchor id="ctlmref"/></para>
<para><userinput>Ctl-M</userinput></para>
<para><userinput>Carriage return</userinput>.</para>
<para>
<programlisting>#!/bin/bash
# Thank you, Lee Maschmeyer, for this example.
read -n 1 -s -p \
$'Control-M leaves cursor at beginning of this line. Press Enter. \x0d'
# Of course, '0d' is the hex equivalent of Control-M.
echo >&amp;2 # The '-s' makes anything typed silent,
#+ so it is necessary to go to new line explicitly.
read -n 1 -s -p $'Control-J leaves cursor on next line. \x0a'
# '0a' is the hex equivalent of Control-J, linefeed.
echo >&amp;2
###
read -n 1 -s -p $'And Control-K\x0bgoes straight down.'
echo >&amp;2 # Control-K is vertical tab.
# A better example of the effect of a vertical tab is:
var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'
echo "$var"
# This works the same way as the above example. However:
echo "$var" | col
# This causes the right end of the line to be higher than the left end.
# It also explains why we started and ended with a line feed --
#+ to avoid a garbled screen.
# As Lee Maschmeyer explains:
# --------------------------
# In the [first vertical tab example] . . . the vertical tab
#+ makes the printing go straight down without a carriage return.
# This is true only on devices, such as the Linux console,
#+ that can't go "backward."
# The real purpose of VT is to go straight UP, not down.
# It can be used to print superscripts on a printer.
# The col utility can be used to emulate the proper behavior of VT.
exit 0</programlisting>
</para>
</listitem>
<listitem>
<para><userinput>Ctl-N</userinput></para>
<para>Erases a line of text recalled from
<firstterm>history buffer</firstterm>
<footnote><para>Bash stores a list of commands
previously issued from the command-line
in a <firstterm>buffer</firstterm>, or
memory space, for recall with the <link
linkend="builtinref">builtin</link>
<firstterm>history</firstterm>
commands.</para></footnote> (on the
command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-O</userinput></para>
<para>Issues a <firstterm>newline</firstterm>
(on the command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-P</userinput></para>
<para>Recalls last command from <firstterm>history
buffer</firstterm> (on the command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-Q</userinput></para>
<para>Resume (<userinput>XON</userinput>).</para>
<para>This resumes <filename>stdin</filename> in a terminal.</para>
</listitem>
<listitem>
<para><userinput>Ctl-R</userinput></para>
<para>Backwards search for text in <firstterm>history
buffer</firstterm>
(on the command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-S</userinput></para>
<para>Suspend (<userinput>XOFF</userinput>).</para>
<para>This freezes <filename>stdin</filename> in a terminal.
(Use Ctl-Q to restore input.)</para>
</listitem>
<listitem>
<para><userinput>Ctl-T</userinput></para>
<para>Reverses the position of the character the cursor
is on with the previous character (on the
command-line).</para>
</listitem>
<listitem>
<para><userinput>Ctl-U</userinput></para>
<para>Erase a line of input, from the cursor backward to
beginning of line. In some settings,
<userinput>Ctl-U</userinput> erases the entire
line of input, <emphasis>regardless of cursor
position</emphasis>.</para>
</listitem>
<listitem>
<para><userinput>Ctl-V</userinput></para>
<para>When inputting text, <userinput>Ctl-V</userinput>
permits inserting control characters. For example, the
following two are equivalent:
<programlisting>echo -e '\x0a'
echo &lt;Ctl-V&gt;&lt;Ctl-J&gt;</programlisting></para>
<para><userinput>Ctl-V</userinput> is primarily useful from
within a text editor.</para>
</listitem>
<listitem>
<para><userinput>Ctl-W</userinput></para>
<para>When typing text on the console or in an xterm window,
<userinput>Ctl-W</userinput> erases from the character
under the cursor backwards to the first instance of
<link linkend="whitespaceref">whitespace</link>. In
some settings, <userinput>Ctl-W</userinput> erases
backwards to first non-alphanumeric character.</para>
</listitem>
<listitem>
<para><userinput>Ctl-X</userinput></para>
<para>In certain word processing programs,
<firstterm>Cuts</firstterm> highlighted text
and copies to <firstterm>clipboard</firstterm>.</para>
</listitem>
<listitem>
<para><userinput>Ctl-Y</userinput></para>
<para><firstterm>Pastes</firstterm> back text previously
erased (with <userinput>Ctl-U</userinput> or
<userinput>Ctl-W</userinput>).</para>
</listitem>
<listitem>
<para><userinput>Ctl-Z</userinput></para>
<para><firstterm>Pauses</firstterm> a foreground job.</para>
<para><firstterm>Substitute</firstterm> operation in certain
word processing applications.</para>
<para><userinput>EOF</userinput> (end-of-file) character
in the MSDOS filesystem.</para>
</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whitespaceref"/>Whitespace</term>
<listitem>
<formalpara>
<title>functions as a separator between commands and/or
variables.</title>
<para>Whitespace consists of either
<firstterm>spaces</firstterm>,
<firstterm>tabs</firstterm>, <firstterm>blank
lines</firstterm>, or any combination thereof.
<footnote><para>A linefeed (<firstterm>newline</firstterm>)
is also a whitespace character. This explains
why a <firstterm>blank line</firstterm>,
consisting only of a linefeed, is considered
whitespace.</para></footnote>
In some contexts, such as <link linkend="wsbad">variable
assignment</link>, whitespace is not permitted, and
results in a syntax error.</para>
</formalpara>
<para>Blank lines have no effect on the action of a script,
and are therefore useful for visually separating functional
sections.</para>
<para><link linkend="ifsref">$IFS</link>, the special variable
separating <firstterm>fields</firstterm> of input to certain
commands. It defaults to whitespace.</para>
<sidebar><para>
<anchor id="fieldref"/><userinput>Definition:</userinput>
A <firstterm>field</firstterm> is a discrete chunk of data
expressed as a string of consecutive characters.
Separating each field from adjacent fields is either
<firstterm>whitespace</firstterm> or some other designated
character (often determined by the <token>$IFS</token>).
In some contexts, a field may be called a
<firstterm>record</firstterm>.
</para></sidebar>
<para><anchor id="quotingws"/></para>
<para>To preserve <firstterm>whitespace</firstterm>
within a string or in a variable, use <link
linkend="quotingref">quoting</link>.</para>
<para>UNIX <link linkend="filterdef">filters</link>
can target and operate on <firstterm>whitespace</firstterm>
using the <link linkend="posixref">POSIX</link> character class
<link linkend="wsposix">[:space:]</link>.</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Special characters used in shell scripts -->
<chapter id="variables">
<title>Introduction to Variables and Parameters</title>
<para><firstterm>Variables</firstterm> are how programming and
scripting languages represent data. A variable is nothing
more than a <firstterm>label</firstterm>, a name assigned to a
location or set of locations in computer memory holding an item
of data.</para>
<para>Variables appear in arithmetic operations and manipulation of
quantities, and in string parsing.</para>
<sect1 id="varsubn">
<title>Variable Substitution</title>
<para>The <firstterm>name</firstterm> of a variable is a placeholder
for its <firstterm>value</firstterm>, the data it holds.
Referencing (retrieving) its value is called
<firstterm>variable substitution</firstterm>.</para>
<variablelist id="dollarsign">
<varlistentry>
<term><token>$</token></term>
<listitem>
<indexterm>
<primary>$</primary>
</indexterm> <indexterm>
<primary>variable</primary> <secondary>$</secondary>
</indexterm> <indexterm>
<primary>variable</primary>
<secondary>substitution</secondary>
</indexterm>
<para><anchor id="varnameval"/></para>
<para>Let us carefully distinguish between the
<firstterm>name</firstterm> of a variable
and its <firstterm>value</firstterm>. If
<userinput>variable1</userinput> is the name of a
variable, then <userinput>$variable1</userinput>
is a reference to its <firstterm>value</firstterm>,
the data item it contains.
<footnote>
<para><anchor id="lvalueref"/>Technically, the
<firstterm>name</firstterm> of a variable is called an
<firstterm>lvalue</firstterm>, meaning that it appears
on the <emphasis>left</emphasis> side of an assignment
statment, as in <userinput>VARIABLE=23</userinput>.
A variable's <firstterm>value</firstterm> is
an <firstterm>rvalue</firstterm>, meaning that
it appears on the <emphasis>right</emphasis>
side of an assignment statement, as in
<userinput>VAR2=$VARIABLE</userinput>.</para>
<para><anchor id="pointerref"/>A variable's
<firstterm>name</firstterm> is, in fact,
a <firstterm>reference</firstterm>, a
<firstterm>pointer</firstterm> to the memory
location(s) where the actual data associated with
that variable is kept.</para>
</footnote>
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>variable1=23</userinput>
<prompt>bash$ </prompt><userinput>echo variable1</userinput>
<computeroutput>variable1</computeroutput>
<prompt>bash$ </prompt><userinput>echo $variable1</userinput>
<computeroutput>23</computeroutput></screen>
</para>
<para>The only times a variable appears <quote>naked</quote>
-- without the <token>$</token> prefix -- is when
declared or assigned, when <firstterm>unset</firstterm>,
when <link linkend="exportref">exported</link>,
in an arithmetic expression within <link
linkend="dblparens">double parentheses
(( ... ))</link>, or in the special case of a variable
representing a <link linkend="signald">signal</link>
(see <xref linkend="ex76"/>). Assignment may be with an
<token>=</token> (as in <parameter>var1=27</parameter>),
in a <link linkend="readref">read</link> statement,
and at the head of a loop (<parameter>for var2 in 1
2 3</parameter>).</para>
<para><anchor id="dblquo"/>Enclosing a referenced value in
<firstterm>double quotes</firstterm> (<token>" ... "</token>)
does not interfere with variable substitution. This is
called <firstterm>partial quoting</firstterm>, 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 <firstterm>full
quoting</firstterm>, sometimes referred to as 'strong
quoting.' See <xref linkend="quoting"/> for a
detailed discussion.</para>
<para>Note that <userinput>$variable</userinput> is actually a
simplified 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>
<para><anchor id="varunsetting"/></para>
<example id="ex9">
<title>Variable assignment and substitution</title>
<programlisting>&ex9;</programlisting>
</example>
<caution>
<para><anchor id="uninitvar1"/></para>
<para>An uninitialized variable has a
<quote>null</quote> value -- no assigned value at all
(<emphasis>not</emphasis> zero!).
<programlisting>if [ -z "$unassigned" ]
then
echo "\$unassigned is NULL."
fi # $unassigned is NULL.</programlisting></para>
<para>Using a variable before
assigning a value to it may cause problems.
It is nevertheless possible to perform arithmetic operations
on an uninitialized variable.
<programlisting>echo "$uninitialized" # (blank line)
let "uninitialized += 5" # Add 5 to it.
echo "$uninitialized" # 5
# Conclusion:
# An uninitialized variable has no value,
#+ however it evaluates as 0 in an arithmetic operation.</programlisting>
See also <xref linkend="selfsource"/>.</para>
</caution>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Variable Substitution -->
<sect1 id="varassignment">
<title>Variable Assignment</title>
<variablelist>
<varlistentry>
<term><anchor id="eqref"/><token>=</token></term>
<listitem>
<indexterm>
<primary>=</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>assignment</secondary>
</indexterm>
<para>the assignment operator (<emphasis>no space before
and after</emphasis>)</para>
<caution>
<para>Do not confuse this with <link
linkend="equalsignref">=</link> and
<link linkend="equalref">-eq</link>, which
<link linkend="ifthen">test</link>,
rather than assign!</para>
<para>Note that <token>=</token> can be either
an <firstterm>assignment</firstterm> or a
<firstterm>test</firstterm> operator, depending on
context.</para>
</caution>
<para><anchor id="ex15_0"/></para>
<example id="ex15">
<title>Plain Variable Assignment</title>
<programlisting>&ex15;</programlisting>
</example>
<para><anchor id="ex16_0"/></para>
<example id="ex16">
<title>Variable Assignment, plain and fancy</title>
<programlisting>&ex16;</programlisting>
</example>
<para><anchor id="commandsubref0"/></para>
<para>Variable assignment using the <firstterm>$(...)</firstterm>
mechanism (a newer method than <link
linkend="backquotesref">backquotes</link>). This is
likewise a form of <link linkend="commandsubref">command
substitution</link>.</para>
<para><programlisting># From /etc/rc.d/rc.local
R=$(cat /etc/redhat-release)
arch=$(uname -m)</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Variable Assignment -->
<sect1 id="untyped">
<title>Bash Variables Are Untyped</title>
<para><anchor id="bvuntyped"/></para>
<para>Unlike many other programming languages, Bash does not segregate
its variables by <quote>type.</quote> Essentially, <emphasis>Bash
variables are character strings</emphasis>, but, depending on
context, Bash permits arithmetic operations and comparisons on
variables. The determining factor is whether the value of a
variable contains only digits.</para>
<example id="intorstring">
<title>Integer or string?</title>
<programlisting>&intorstring;</programlisting>
</example>
<para>Untyped variables are both a blessing and a curse. They permit
more flexibility in scripting and make it easier to grind out
lines of code (and give you enough rope to hang yourself!).
However, they likewise permit subtle errors to creep in
and encourage sloppy programming habits.</para>
<para>To lighten the burden of keeping track of variable
types in a script, Bash <emphasis>does</emphasis> permit
<link linkend="declareref">declaring</link> variables.</para>
</sect1> <!-- Bash Variables Are Untyped-->
<sect1 id="othertypesv">
<title>Special Variable Types</title>
<variablelist>
<varlistentry>
<term><replaceable>Local variables</replaceable></term>
<listitem>
<indexterm>
<primary>variable</primary>
<secondary>local</secondary>
</indexterm>
<para>Variables <link
linkend="scoperef">visible</link> 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><anchor id="envref"/><replaceable>Environmental variables</replaceable></term>
<listitem>
<indexterm>
<primary>variable</primary>
<secondary>environmental</secondary>
</indexterm>
<para>Variables that affect the behavior of the shell and
user interface</para>
<note>
<para>In a more general context, each <link
linkend="processref">process</link> has an
<quote>environment</quote>, that is, a group of
variables 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 environmental variables causes the
shell to update its environment, and all the shell's
<firstterm>child processes</firstterm> (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&amp;=ZZZZZZZZZZZZZZ/'`"</userinput>
<prompt>bash$ </prompt><userinput>du</userinput>
<computeroutput>bash: /usr/bin/du: Argument list too long</computeroutput>
</screen>
</para>
<para>Note: this <quote>error</quote> has been fixed, as of
kernel version 2.6.23.</para>
<para>(Thank you, St&eacute;phane Chazelas 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
<firstterm>environment</firstterm> local to
the script. This is the function of the <link
linkend="exportref">export</link> command.</para>
<anchor id="childref"/>
<note>
<para>A script can <command>export</command> variables only
to child <link linkend="processref">processes</link>,
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.
<emphasis><link linkend="forkref">Child processes</link>
cannot export variables back to the parent processes that
spawned them.</emphasis></para>
<para><anchor id="childref2"/><userinput>Definition:</userinput>
A <firstterm>child process</firstterm> is a
subprocess launched by another process, its <link
linkend="parentref">parent</link>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="posparamref1"/><replaceable>Positional parameters</replaceable></term>
<listitem>
<indexterm>
<primary>parameter</primary>
<secondary>positional</secondary>
</indexterm>
<para>Arguments passed to the script from the command
line
<footnote><para>Note that <link
linkend="passedargs"><firstterm>functions</firstterm>
also take positional parameters</link>.</para></footnote>
: <varname>$0</varname>, <varname>$1</varname>,
<varname>$2</varname>, <varname>$3</varname> . . .</para>
<para><anchor id="scrnameparam"/><varname>$0</varname> is
the name of the script itself,
<varname>$1</varname> is the first argument,
<varname>$2</varname> the second, <varname>$3</varname>
the third, and so forth.
<footnote>
<para><anchor id="arg0"/>The process calling the
script sets the <varname>$0</varname> parameter. By
convention, this parameter is the name of the script. See
the <link linkend="manref">manpage</link> (manual page)
for <command>execv</command>.</para>
<para>From the <firstterm>command-line</firstterm>, however,
<varname>$0</varname> is the name of the shell.
<screen><prompt>bash$ </prompt><userinput>echo $0</userinput>
<computeroutput>bash</computeroutput>
<prompt>tcsh% </prompt><userinput>echo $0</userinput>
<computeroutput>tcsh</computeroutput></screen></para>
</footnote>
<anchor id="bracketnotation"/>
After <varname>$9</varname>, the arguments must be enclosed
in brackets, for example, <varname>${10}</varname>,
<varname>${11}</varname>, <varname>${12}</varname>.</para>
<para>The special variables <link linkend="appref">$* and $@</link>
denote <emphasis>all</emphasis> the positional parameters.</para>
<example id="ex17">
<title>Positional Parameters</title>
<programlisting>&ex17;</programlisting>
</example>
<para><firstterm>Bracket notation</firstterm> for positional
parameters leads to a fairly simple way of referencing
the <emphasis>last</emphasis> argument passed to a
script on the command-line. This also requires <link
linkend="varrefnew">indirect referencing</link>.</para>
<para><anchor id="lastargref"/></para>
<para><programlisting>args=$# # Number of args passed.
lastarg=${!args}
# Note: This is an *indirect reference* to $args ...
# Or: lastarg=${!#} (Thanks, Chris Monson.)
# This is an *indirect reference* to the $# variable.
# Note that lastarg=${!$#} doesn't work.
</programlisting></para>
<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.
<footnote><para>If the the script is <link
linkend="sourceref">sourced</link> or <link
linkend="symlinkref">symlinked</link>, then
this will not work. It is safer to check <link
linkend="bashsourceref">$BASH_Source</link>.</para></footnote>
There must also exist symbolic links to all the alternate
names of the script. See <xref linkend="hellol"/>.</para>
<para><anchor id="nullvar"/></para>
<tip><para>If a script expects a command-line parameter
but is invoked without one, this may cause a <firstterm>null
variable assignment</firstterm>, 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>variable1_=$1_ # Rather than variable1=$1
# This will prevent an error, even if positional parameter is absent.
critical_argument01=$variable1_
# The extra character can be stripped off later, like so.
variable1=${variable1_/_/}
# Side effects only if $variable1_ begins with an underscore.
# This uses one of the parameter substitution templates discussed later.
# (Leaving out the replacement pattern results in a deletion.)
# A more straightforward way of dealing with this is
#+ to simply test whether expected positional parameters have been passed.
if [ -z $1 ]
then
exit $E_MISSING_POS_PARAM
fi
# However, as Fabian Kreutz points out,
#+ the above method may have unexpected side-effects.
# A better method is parameter substitution:
# ${1:-$DefaultVal}
# See the "Parameter Substition" section
#+ in the "Variables Revisited" chapter.
</programlisting>
<para>---</para>
<example id="ex18">
<title><firstterm>wh</firstterm>, <firstterm>
whois</firstterm> domain name lookup</title>
<programlisting>&ex18;</programlisting>
</example>
<para>---</para>
<para><anchor id="shiftref"/></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> &lt;--- <varname>$2</varname>, <varname>$2</varname> &lt;--- <varname>$3</varname>, <varname>$3</varname> &lt;--- <varname>$4</varname>, etc.</para>
<para>The old <varname>$1</varname> disappears, but
<emphasis><varname>$0</varname> (the script name)
does not change</emphasis>. If you use a large number of
positional parameters to a script, <command>shift</command>
lets you access those past <literal>10</literal>, although
<link linkend="bracketnotation">{bracket} notation</link>
also permits this.</para>
<example id="ex19">
<title>Using <firstterm>shift</firstterm></title>
<programlisting>&ex19;</programlisting>
</example>
<para>The <command>shift</command> command can take a numerical
parameter indicating how many positions to shift.</para>
<para><programlisting>#!/bin/bash
# shift-past.sh
shift 3 # Shift 3 positions.
# n=3; shift $n
# Has the same effect.
echo "$1"
exit 0
# ======================== #
$ sh shift-past.sh 1 2 3 4 5
4
# However, as Eleni Fragkiadaki, points out,
#+ attempting a 'shift' past the number of
#+ positional parameters ($#) returns an exit status of 1,
#+ and the positional parameters themselves do not change.
# This means possibly getting stuck in an endless loop. . . .
# For example:
# until [ -z "$1" ]
# do
# echo -n "$1 "
# shift 20 # If less than 20 pos params,
# done #+ then loop never ends!
#
# When in doubt, add a sanity check. . . .
# shift 20 || break
# ^^^^^^^^</programlisting></para>
<note><para>The <command>shift</command> command works in a similar
fashion on parameters passed to a <link
linkend="functionref">function</link>. See <xref
linkend="multiplication"/>.</para></note>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Special Variable Types -->
</chapter> <!-- Variables -->
<chapter id="quoting">
<title>Quoting</title>
<para><anchor id="quotingref"/></para>
<indexterm>
<primary>"</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>"</secondary>
</indexterm>
<indexterm>
<primary>'</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>'</secondary>
</indexterm>
<indexterm>
<primary>quote</primary>
</indexterm>
<indexterm>
<primary>\</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>\</secondary>
</indexterm>
<indexterm>
<primary>escape</primary>
</indexterm>
<para>Quoting means just that, bracketing a string in quotes. This
has the effect of protecting <link linkend="scharlist1">special
characters</link> 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. For example, the <link
linkend="asteriskref">asterisk *</link> represents
a <firstterm>wild card</firstterm> character in
<link linkend="globbingref">globbing</link> and <link
linkend="regexref">Regular Expressions</link>).</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>
<para><anchor id="quotingdef"/></para>
<sidebar><para>In everyday speech or writing, when we
<quote>quote</quote> a phrase, we set it apart and give it special
meaning. In a Bash script, when we <firstterm>quote</firstterm> a
string, we set it apart and protect its <firstterm>literal</firstterm>
meaning.</para></sidebar>
<para>Certain programs and utilities reinterpret or expand
special characters in a quoted string. An important use of
quoting is 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>Note that the unquoted <userinput>grep [Ff]irst *.txt</userinput>
works under the Bash shell.
<footnote><para>Unless there is a file named
<filename>first</filename> in the current working directory. Yet
another reason to <firstterm>quote</firstterm>. (Thank you, Harald
Koenig, for pointing this out.</para></footnote>
</para>
<para>Quoting can also suppress <link linkend="echoref">echo's</link>
<quote>appetite</quote> for newlines.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $(ls -l)</userinput>
<computeroutput>total 8 -rw-rw-r-- 1 bo bo 13 Aug 21 12:57 t.sh -rw-rw-r-- 1 bo bo 78 Aug 21 12:57 u.sh</computeroutput>
<prompt>bash$ </prompt><userinput>echo "$(ls -l)"</userinput>
<computeroutput>total 8
-rw-rw-r-- 1 bo bo 13 Aug 21 12:57 t.sh
-rw-rw-r-- 1 bo bo 78 Aug 21 12:57 u.sh</computeroutput></screen>
</para>
<sect1 id="quotingvar">
<title>Quoting Variables</title>
<para>When referencing a variable, it is generally advisable to
enclose its name in double quotes.
This prevents reinterpretation of all special characters within
the quoted string -- except <token>$</token>, <token>`</token>
(backquote), and <token>\</token> (escape).
<footnote>
<para><anchor id="quotingbsl"/></para>
<para>Encapsulating <quote>!</quote> within double
quotes gives an error when used <emphasis>from the command
line</emphasis>. This is interpreted as a <link
linkend="histcommands">history command</link>. Within a script,
though, this problem does not occur, since the Bash history
mechanism is disabled then.</para>
<para>Of more concern is the <emphasis>apparently</emphasis>
inconsistent behavior of <replaceable>\</replaceable>
within double quotes, and especially following an
<command>echo -e</command> command.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo hello\!</userinput>
<computeroutput>hello!</computeroutput>
<prompt>bash$ </prompt><userinput>echo "hello\!"</userinput>
<computeroutput>hello\!</computeroutput>
<prompt>bash$ </prompt><userinput>echo \</userinput>
<computeroutput>></computeroutput>
<prompt>bash$ </prompt><userinput>echo "\"</userinput>
<computeroutput>></computeroutput>
<prompt>bash$ </prompt><userinput>echo \a</userinput>
<computeroutput>a</computeroutput>
<prompt>bash$ </prompt><userinput>echo "\a"</userinput>
<computeroutput>\a</computeroutput>
<prompt>bash$ </prompt><userinput>echo x\ty</userinput>
<computeroutput>xty</computeroutput>
<prompt>bash$ </prompt><userinput>echo "x\ty"</userinput>
<computeroutput>x\ty</computeroutput>
<prompt>bash$ </prompt><userinput>echo -e x\ty</userinput>
<computeroutput>xty</computeroutput>
<prompt>bash$ </prompt><userinput>echo -e "x\ty"</userinput>
<computeroutput>x y</computeroutput>
</screen>
</para>
<para>Double quotes following an <firstterm>echo</firstterm>
<emphasis>sometimes</emphasis> escape
<replaceable>\</replaceable>. Moreover, the
<option>-e</option> option to <firstterm>echo</firstterm>
causes the <quote>\t</quote> to be interpreted as a
<firstterm>tab</firstterm>.</para>
<para>(Thank you, Wayne Pollock, for pointing this out, and Geoff
Lee and Daniel Barclay for explaining it.) </para>
</footnote>
Keeping <token>$</token> as a special character within
double quotes permits referencing a quoted variable
(<replaceable>"$variable"</replaceable>), that is, replacing the
variable with its value (see <xref linkend="ex9"/>, above).</para>
<para><anchor id="wsquo"/></para>
<para>Use double quotes to prevent word splitting.
<footnote><para><anchor id="wsplitref"/><quote>Word
splitting,</quote> in this context, means dividing
a character string into 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.</para>
<para><anchor id="varsplitting"/></para>
<para><programlisting>List="one two three"
for a in $List # Splits the variable in parts at whitespace.
do
echo "$a"
done
# one
# two
# three
echo "---"
for a in "$List" # Preserves whitespace in a single variable.
do # ^ ^
echo "$a"
done
# one two three</programlisting></para>
<para>A more elaborate example:</para>
<para><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, St&eacute;phane Chazelas.
</programlisting></para>
<tip><para>Enclosing the arguments to an <command>echo</command>
statement in double quotes is necessary only when word splitting
or preservation of <link linkend="whitespaceref">whitespace</link>
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 St&eacute;phane Chazelas.</programlisting>
</para></note>
</sect1> <!-- Quoting Variables -->
<sect1 id="escapingsection">
<title>Escaping</title>
<para><anchor id="escp"/><firstterm>Escaping</firstterm> is a method
of quoting single characters. The <token>escape</token>
(<token>\</token>) preceding a character tells the shell to
interpret that character literally.</para>
<caution><para>With certain commands and utilities, such as <link
linkend="echoref">echo</link> and <link
linkend="sedref">sed</link>, escaping a character may have the
opposite effect - it can toggle on a special meaning for that
character.</para></caution>
<variablelist id="specialmeanings">
<title><anchor id="spm"/>Special meanings of certain
escaped characters</title>
<varlistentry>
<term>used with <command>echo</command> and
<command>sed</command></term>
<listitem><para></para></listitem>
</varlistentry>
<varlistentry><term><token>\n</token></term>
<listitem>
<indexterm>
<primary>\n</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\n</secondary>
</indexterm>
<indexterm>
<primary>newline</primary>
</indexterm>
<para>means newline</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\r</token></term>
<listitem>
<indexterm>
<primary>\r</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\r</secondary>
</indexterm>
<indexterm>
<primary>carriage return</primary>
</indexterm>
<para>means return</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\t</token></term>
<listitem>
<indexterm>
<primary>\t</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\t</secondary>
</indexterm>
<indexterm>
<primary>tabulation</primary>
</indexterm>
<para>means tab</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\v</token></term>
<listitem>
<indexterm>
<primary>\v</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\v</secondary>
</indexterm>
<indexterm>
<primary>vertical tabulation</primary>
</indexterm>
<para> means vertical tab</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\b</token></term>
<listitem>
<indexterm>
<primary>\b</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\b</secondary>
</indexterm>
<indexterm>
<primary>backspace</primary>
</indexterm>
<para>means backspace</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\a</token></term>
<listitem>
<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>
<para>means <firstterm>alert</firstterm> (beep or flash)</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\0xx</token></term>
<listitem>
<indexterm>
<primary>\0xx</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\0nn</secondary>
</indexterm>
<indexterm>
<primary>octal ASCII</primary>
</indexterm>
<para><anchor id="octalref"/>translates to the
octal <link linkend="asciidef">ASCII</link>
equivalent of <replaceable>0nn</replaceable>, where
<replaceable>nn</replaceable> is a string of digits</para>
<important>
<para><anchor id="strq"/></para>
<para>The <userinput>$' ... '</userinput>
<link linkend="quotingref">quoted</link> string-expansion
construct is a mechanism that uses escaped octal or hex values
to assign ASCII characters to variables, e.g.,
<command>quote=$'\042'</command>.</para>
</important>
<example id="escaped">
<title>Escaped Characters</title>
<programlisting>&escaped;</programlisting>
</example>
<para>A more elaborate example:</para>
<example id="bashek">
<title>Detecting key-presses</title>
<programlisting>&bashek;</programlisting>
</example>
<para>See also <xref linkend="ex77"/>.</para>
</listitem>
</varlistentry>
<varlistentry><term><token>\"</token></term>
<listitem>
<indexterm>
<primary>\"</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\"</secondary>
</indexterm>
<indexterm>
<primary>quote</primary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>\$</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\$</secondary>
</indexterm>
<indexterm>
<primary>dollar</primary>
</indexterm>
<para>gives the dollar sign its literal meaning
(variable name following <token>\$</token> will not be
referenced)</para>
<para><programlisting>echo "\$variable01" # $variable01
echo "The book cost \$7.98." # The book cost $7.98.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry><term><token>\\</token></term>
<listitem>
<indexterm>
<primary>\\</primary>
</indexterm>
<indexterm>
<primary>escaped character</primary>
<secondary>\\</secondary>
</indexterm>
<indexterm>
<primary>double backslash</primary>
</indexterm>
<para>gives the backslash its literal meaning</para>
<para><programlisting>echo "\\" # Results in \
# Whereas . . .
echo "\" # Invokes secondary prompt from the command-line.
# In a script, gives an error message.
# However . . .
echo '\' # Results in \</programlisting></para>
</listitem>
</varlistentry>
</variablelist>
<note>
<para>The behavior of <token>\</token> depends on whether
it is escaped, <link linkend="snglquo">strong-quoted</link>,
<link linkend="dblquo">weak-quoted</link>, or appearing within
<link linkend="commandsubref">command substitution</link> or a
<link linkend="heredocref">here document</link>.
<programlisting> # Simple escaping and quoting
echo \z # z
echo \\z # \z
echo '\z' # \z
echo '\\z' # \\z
echo "\z" # \z
echo "\\z" # \z
# Command substitution
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
# Here document
cat &lt;&lt;EOF
\z
EOF # \z
cat &lt;&lt;EOF
\\z
EOF # \z
# These examples supplied by St&eacute;phane Chazelas.</programlisting>
</para>
<para>Elements of a string assigned to a variable may be escaped, but
the escape character alone may not be assigned to a variable.
<programlisting>variable=\
echo "$variable"
# Will not work - gives an error message:
# test.sh: : command not found
# A "naked" escape cannot safely be assigned to a variable.
#
# What actually happens here is that the "\" escapes the newline and
#+ the effect is variable=echo "$variable"
#+ invalid variable assignment
variable=\
23skidoo
echo "$variable" # 23skidoo
# This works, since the second line
#+ is a valid variable assignment.
variable=\
# \^ escape followed by space
echo "$variable" # space
variable=\\
echo "$variable" # \
variable=\\\
echo "$variable"
# Will not work - gives an error message:
# test.sh: \: command not found
#
# First escape escapes second one, but the third one is left "naked",
#+ with same result as first instance, above.
variable=\\\\
echo "$variable" # \\
# Second and fourth escapes escaped.
# This is o.k.</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><anchor id="escnewline"/></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 &amp;&amp; tar cf - . ) | \
(cd /dest/directory &amp;&amp; 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, St&eacute;phane 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.
#foo\
#bar
# Examples suggested by St&eacute;phane Chazelas.</programlisting></para>
</sect1> <!-- Escaping -->
</chapter> <!-- Quoting -->
<chapter id="exit-status">
<title>Exit and Exit Status</title>
<epigraph>
<para>... there are dark corners in the Bourne shell, and people use all
of them.</para>
<para>--Chet Ramey</para>
</epigraph>
<para><anchor id="exitcommandref"/>The
<command>
<indexterm>
<primary>exit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exit</secondary>
</indexterm>
exit
</command>
command terminates a script, just as in a <command>C</command>
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> or <firstterm>exit code</firstterm>).
<anchor id="exitsuccess"/>
A successful command returns a <returnvalue>0</returnvalue>, while
an unsuccessful one returns a <returnvalue>non-zero</returnvalue>
value that usually can be interpreted as an <firstterm>error
code</firstterm>. Well-behaved UNIX commands, programs, and
utilities return a <returnvalue>0</returnvalue> exit code upon
successful completion, though there are some exceptions.</para>
<para><anchor id="functxstr"/></para>
<para>Likewise, <link linkend="functionref">functions</link>
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>nnn</replaceable></userinput>
command may be used to deliver an
<returnvalue><replaceable>nnn</replaceable></returnvalue>
exit status to the shell
(<returnvalue><replaceable>nnn</replaceable></returnvalue>
must be an integer 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 (previous to the
<command>exit</command>).</para>
<para><programlisting>#!/bin/bash
COMMAND_1
. . .
COMMAND_LAST
# Will exit with status of last command.
exit</programlisting></para>
<para>The equivalent of a bare <command>exit</command> is
<command>exit $?</command> or even just omitting the
<command>exit</command>.</para>
<para><programlisting>#!/bin/bash
COMMAND_1
. . .
COMMAND_LAST
# Will exit with status of last command.
exit $?</programlisting></para>
<para><programlisting>#!/bin/bash
COMMAND1
. . .
COMMAND_LAST
# Will exit with status of last command.</programlisting></para>
</note>
<para><anchor id="exsref"/></para>
<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>
<footnote><para>In those instances when there is no <link
linkend="returnref">return</link>
terminating the function.</para></footnote>
</para>
<para><anchor id="pipeex"/>Following the execution of a <link
linkend="piperef">pipe</link>, a <varname>$?</varname>
gives the exit status of the last command executed.</para>
<para>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 <returnvalue>1 - 255</returnvalue> 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 <firstterm>logical
not</firstterm> 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 between it and the command.
# !true leads to a "command not found" error
#
# The '!' operator prefixing a command invokes the Bash history mechanism.
true
!true
# No error this time, but no negation either.
# It just repeats the previous command (true).
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
# Thanks, St&eacute;phane Chazelas and Kristopher Newsome.</programlisting>
</example>
</para>
</note>
<caution><para>Certain exit status codes have <link
linkend="exitcodesref">reserved meanings</link> and should not
be user-specified in a script. </para></caution>
</chapter> <!-- Exit and Exit status -->
<chapter id="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</primary>
</indexterm>
<indexterm>
<primary>else if</primary>
</indexterm>
<indexterm>
<primary>elif</primary>
</indexterm>
<para>Every reasonably complete programming language can test
for a condition, then act according to the result of the
test. Bash has the <link linkend="ttestref">test</link>
command, various <link linkend="dblbrackets">bracket</link>
and <link linkend="dblparenstst">parenthesis</link> operators,
and the <command>if/then</command> construct.</para>
<sect1 id="testconstructs">
<title>Test Constructs</title>
<para><anchor id="testconstructs1"/></para>
<itemizedlist id="testingref">
<listitem>
<para>An <command>if/then</command> construct tests whether the
<link linkend="exitstatusref">exit status</link> of a list
of commands is <returnvalue>0</returnvalue> (since 0 means
<quote>success</quote> by UNIX convention), and if so, executes
one or more commands.</para>
</listitem>
<listitem>
<para>There exists a dedicated command called <command>
[</command> (<link linkend="leftbracket">left bracket</link>
special character). It is a synonym for <command>test</command>,
and a <link linkend="builtinref">builtin</link> for efficiency
reasons. This command considers its arguments as comparison
expressions or file tests and returns an exit status corresponding
to the result of the comparison (0 for true, 1 for false).</para>
</listitem>
<listitem>
<para>With version 2.02, Bash introduced the <link
linkend="dblbrackets">[[ ... ]]</link> <firstterm>extended
test command</firstterm>, 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>
</listitem>
<listitem>
<para><anchor id="dblparenstst"/></para>
<para>The <link linkend="dblparens">(( ... ))</link> and <link
linkend="letref">let ...</link> constructs return an
<link linkend="exitstatusref">exit status</link>,
<emphasis>according to whether the arithmetic expressions they
evaluate expand to a non-zero value</emphasis>. These
<link linkend="arithexpref">arithmetic-expansion</link>
constructs may therefore be used to perform <link
linkend="icomparison1">arithmetic comparisons</link>.</para>
<para>
<programlisting>(( 0 &amp;&amp; 1 )) # Logical AND
echo $? # 1 ***
# And so ...
let "num = (( 0 &amp;&amp; 1 ))"
echo $num # 0
# But ...
let "num = (( 0 &amp;&amp; 1 ))"
echo $? # 1 ***
(( 200 || 11 )) # Logical OR
echo $? # 0 ***
# ...
let "num = (( 200 || 11 ))"
echo $num # 1
let "num = (( 200 || 11 ))"
echo $? # 0 ***
(( 200 | 11 )) # Bitwise OR
echo $? # 0 ***
# ...
let "num = (( 200 | 11 ))"
echo $num # 203
let "num = (( 200 | 11 ))"
echo $? # 0 ***
# The "let" construct returns the same exit status
#+ as the double-parentheses arithmetic expansion.</programlisting>
</para>
<caution><para><anchor id="arxs"/>Again, note that the
<firstterm>exit status</firstterm> of an arithmetic expression
is <emphasis>not</emphasis> an error value.
<programlisting>var=-2 &amp;&amp; (( var+=2 ))
echo $? # 1
var=-2 &amp;&amp; (( var+=2 )) &amp;&amp; echo $var
# Will not echo $var!</programlisting>
</para></caution>
</listitem>
<listitem>
<para><anchor id="ifgrepref"/></para>
<para>An <command>if</command> can test any command, not just
conditions enclosed within brackets.</para>
<para><programlisting>if cmp a b &amp;&gt; /dev/null # Suppress output.
then echo "Files a and b are identical."
else echo "Files a and b differ."
fi
# The very useful "if-grep" construct:
# -----------------------------------
if grep -q Bash file
then echo "File contains at least one occurrence of Bash."
fi
word=Linux
letter_sequence=inu
if echo "$word" | grep -q "$letter_sequence"
# The "-q" option to grep suppresses output.
then
echo "$letter_sequence found in $word"
else
echo "$letter_sequence not found in $word"
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><emphasis>These last two examples
courtesy of St&eacute;phane 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><anchor id="elseref"/><programlisting>if [ condition-true ]
then
command 1
command 2
...
else # Or else ...
# Adds default code block executing if original condition tests false.
command 3
command 4
...
fi</programlisting>
</para>
<note>
<para>When <firstterm>if</firstterm> and <firstterm>then</firstterm>
are on same line in a condition test, a semicolon must
terminate the <firstterm>if</firstterm> statement. Both
<firstterm>if</firstterm> and <firstterm>then</firstterm>
are <link linkend="keywordref">keywords</link>. Keywords (or
commands) begin statements, and before a new statement on the
same line begins, the old one must terminate.</para>
<para><programlisting>if [ -x "$filename" ]; then</programlisting></para>
</note>
<variablelist id="elifref">
<title><anchor id="elifref1"/>Else if and elif</title>
<varlistentry>
<term><token>elif</token></term>
<listitem>
<para><userinput>elif</userinput> is a contraction
for <firstterm>else if</firstterm>. The effect is to nest an
inner <token>if/then</token> construct within an outer
one.</para>
<para><programlisting>if [ condition1 ]
then
command1
command2
command3
elif [ condition2 ]
# Same as else if
then
command4
command5
else
default-command
fi</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
<indexterm>
<primary>test</primary>
</indexterm>
<indexterm>
<primary>test</primary>
<secondary>test</secondary>
</indexterm>
<indexterm>
<primary>[</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>[</secondary>
</indexterm>
<indexterm>
<primary>]</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>]</secondary>
</indexterm>
<anchor id="ifref2"/>
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
<firstterm>token</firstterm>
<footnote><para><anchor id="tokenref"/>A
<firstterm>token</firstterm> is a symbol or short
string with a special meaning attached to it (a <link
linkend="metameaningref">meta-meaning</link>). In Bash,
certain tokens, such as <command>[</command> and <link
linkend="dotref">. (dot-command)</link>, may expand to
<firstterm>keywords</firstterm> and commands.</para></footnote>
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>
<para><anchor id="ttestref"/></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 <firstterm>sh-utils</firstterm>
package. Likewise, <command>[</command> does not call
<filename>/usr/bin/[</filename>, which is linked to
<filename>/usr/bin/test</filename>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>type test</userinput>
<computeroutput>test is a shell builtin</computeroutput>
<prompt>bash$ </prompt><userinput>type '['</userinput>
<computeroutput>[ is a shell builtin</computeroutput>
<prompt>bash$ </prompt><userinput>type '[['</userinput>
<computeroutput>[[ is a shell keyword</computeroutput>
<prompt>bash$ </prompt><userinput>type ']]'</userinput>
<computeroutput>]] is a shell keyword</computeroutput>
<prompt>bash$ </prompt><userinput>type ']'</userinput>
<computeroutput>bash: type: ]: not found</computeroutput>
</screen>
</para>
<para><anchor id="usrbintest"/></para>
<para>If, for some reason, you wish to use
<filename>/usr/bin/test</filename> in a Bash script,
then specify it by full pathname.</para>
</note>
<example id="ex11">
<title>Equivalence of <firstterm>test</firstterm>,
<filename>/usr/bin/test</filename>, <token>[ ]</token>,
and <filename>/usr/bin/[</filename></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>
<sidebar>
<para><anchor id="dblbrackets"/>The <token>[[ ]]</token> construct
is the more versatile Bash version of <token>[ ]</token>. This
is the <firstterm>extended test command</firstterm>, adopted from
<firstterm>ksh88</firstterm>.</para>
<para>* * *</para>
<para>No filename expansion or word splitting takes 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>
<para>Using the <command>[[ ... ]]</command> test construct,
rather than <command>[ ... ]</command> can prevent many
logic errors in scripts. For example, the <token>&amp;&amp;</token>,
<token>||</token>, <token>&lt;</token>, and <token>&gt;</token>
operators work within a <token>[[ ]]</token> test, despite
giving an error within a <token>[ ]</token> construct.</para>
<para><anchor id="dblbraev"/></para>
<para><firstterm>Arithmetic evaluation</firstterm> of octal /
hexadecimal constants takes place automatically within a
<token>[[ ... ]]</token> construct.
<programlisting># [[ Octal and hexadecimal evaluation ]]
# Thank you, Moritz Gronbach, for pointing this out.
decimal=15
octal=017 # = 15 (decimal)
hex=0x0f # = 15 (decimal)
if [ "$decimal" -eq "$octal" ]
then
echo "$decimal equals $octal"
else
echo "$decimal is not equal to $octal" # 15 is not equal to 017
fi # Doesn't evaluate within [ single brackets ]!
if [[ "$decimal" -eq "$octal" ]]
then
echo "$decimal equals $octal" # 15 equals 017
else
echo "$decimal is not equal to $octal"
fi # Evaluates within [[ double brackets ]]!
if [[ "$decimal" -eq "$hex" ]]
then
echo "$decimal equals $hex" # 15 equals 0x0f
else
echo "$decimal is not equal to $hex"
fi # [[ $hexadecimal ]] also evaluates!</programlisting>
</para>
</sidebar>
<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" ] &amp;&amp; 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><anchor id="dblprx"/>The <link linkend="dblparens">(( ))
construct</link> expands and evaluates an arithmetic
expression. If the expression evaluates as zero, it returns
an <link linkend="exitstatusref">exit status</link> of
<returnvalue>1</returnvalue>, or <quote>false</quote>. A non-zero
expression returns an exit status of <returnvalue>0</returnvalue>,
or <quote>true</quote>. This is in marked contrast to using
the <command>test</command> and <token>[ ]</token> constructs
previously discussed.</para>
<example id="arithtests">
<title>Arithmetic Tests using <token>(( ))</token></title>
<programlisting>&arithtests;</programlisting>
</example>
</sect1> <!-- Test Constructs -->
<sect1 id="fto">
<title>File test operators</title>
<variablelist>
<title><anchor id="rtif"/>Returns true if...</title>
<varlistentry>
<term><token>-e</token></term>
<listitem><para>file exists</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-a</token></term>
<listitem><para>file exists</para>
<para>This is identical in effect to <token>-e</token>.
It has been <quote>deprecated,</quote>
<footnote><para>
Per the 1913 edition of <emphasis>Webster's
Dictionary</emphasis>:
<programlisting>Deprecate
...
To pray against, as an evil;
to seek to avert by prayer;
to desire the removal of;
to seek deliverance from;
to express deep regret for;
to disapprove of strongly.</programlisting>
</para></footnote>
and its use is
discouraged.</para></listitem>
</varlistentry>
<varlistentry>
<term><anchor id="regularfile"/><token>-f</token></term>
<listitem><para>file is a <replaceable>regular</replaceable>
file (not a directory or <link linkend="devfileref">device
file</link>)</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 <link linkend="blockdevref">block
device</link></para>
<para><anchor id="blockdevtest"/></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-c</token></term>
<listitem><para><anchor id="chardevtest"/>file is a <link
linkend="chardevref">character device</link></para>
<para><programlisting>device0="/dev/sda2" # / (root directory)
if [ -b "$device0" ]
then
echo "$device0 is a block device."
fi
# /dev/sda2 is a block device.
device1="/dev/ttyS1" # PCMCIA modem card.
if [ -c "$device1" ]
then
echo "$device1 is a character device."
fi
# /dev/ttyS1 is a character device.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-p</token></term>
<listitem>
<para>file is a <link linkend="piperef">pipe</link></para>
<para><programlisting>function show_input_type()
{
[ -p /dev/fd/0 ] &amp;&amp; echo PIPE || echo STDIN
}
show_input_type "Input" # STDIN
echo "Input" | show_input_type # PIPE
# This example courtesy of Carl Anderson.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-h</token></term>
<listitem><para>file is a <link linkend="symlinkref">symbolic
link</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 <link linkend="socketref">socket</link></para></listitem>
</varlistentry>
<varlistentry>
<term><token>-t</token></term>
<listitem>
<para><anchor id="termtest"/>file (<link
linkend="fdref">descriptor</link>) is
associated with a terminal device</para>
<para>This test option <link linkend="ii2test"> may be used
to check</link> 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><anchor id="suidref"/></para>
<para>set-user-id (suid) flag set on file</para>
<para>A binary owned by <firstterm>root</firstterm>
with <replaceable>set-user-id</replaceable> flag set
runs with <firstterm>root</firstterm> privileges, even
when an ordinary user invokes it.
<footnote><para>Be aware that <firstterm>suid</firstterm>
binaries may open security holes. The
<firstterm>suid</firstterm> flag has no effect on
shell scripts.</para></footnote>
This is useful for executables (such as
<command>pppd</command> and <command>cdrecord</command>)
that need to access system hardware. Lacking the
<firstterm>suid</firstterm> flag, these binaries could not
be invoked by a <firstterm>non-root</firstterm> user.</para>
<para>
<screen>
<computeroutput>-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd</computeroutput>
</screen>
</para>
<para>A file with the <replaceable>suid</replaceable>
flag set shows an <firstterm>s</firstterm> 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 <firstterm>sticky bit,</firstterm>
the <firstterm>save-text-mode</firstterm> 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 Linux 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 <firstterm>t</firstterm>
to the permissions on the file or directory listing.
This restricts altering or deleting specific files
in that directory to the owner of those files.</para>
<para>
<screen>
<computeroutput>drwxrwxrwt 7 root 1024 May 19 21:26 tmp/</computeroutput>
</screen>
</para>
<para>If a user does not own a directory that has the sticky
bit set, but has write permission in that directory, she
can only delete those files that she owns in it. This
keeps users from inadvertently overwriting or deleting
each other's files in a publicly accessible directory,
such as <filename class="directory">/tmp</filename>.
(The <firstterm>owner</firstterm> of the directory or
<firstterm>root</firstterm> can, of course, delete or
rename files there.)</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>
<example id="brokenlink">
<title>Testing for broken links</title>
<programlisting>&brokenlink;</programlisting>
</example>
<para><xref linkend="cookies"/>, <xref linkend="bingrep"/>,
<xref linkend="fileinfo"/>, <xref linkend="ramdisk"/>, and <xref
linkend="mailformat"/> also illustrate uses of the file test
operators.</para>
</sect1> <!-- File test operators -->
<sect1 id="comparison-ops">
<title>Other Comparison Operators</title>
<para>A <firstterm>binary</firstterm> comparison operator
compares two variables or quantities. <emphasis>Note
that integer and string comparison use a different set of
operators.</emphasis></para>
<variablelist id="icomparison">
<title><anchor id="icomparison1"/>integer comparison</title>
<varlistentry>
<term><anchor id="equalref"/><token>-eq</token></term>
<listitem>
<para>is equal to</para>
<para><userinput>if [ "$a" -eq "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="nequalref"/><token>-ne</token></term>
<listitem>
<para>is not equal to</para>
<para><userinput>if [ "$a" -ne "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gt0ref"/><token>-gt</token></term>
<listitem>
<para>is greater than</para>
<para><userinput>if [ "$a" -gt "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ge0ref"/><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><anchor id="lt0ref"/><token>-lt</token></term>
<listitem>
<para>is less than</para>
<para><userinput>if [ "$a" -lt "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="le0ref"/><token>-le</token></term>
<listitem>
<para>is less than or equal to</para>
<para><userinput>if [ "$a" -le "$b" ]</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="intlt"/><token>&lt;</token></term>
<listitem>
<para>is less than (within <link linkend="dblparens">double
parentheses</link>)</para>
<para><userinput>(("$a" &lt; "$b"))</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lteq"/><token>&lt;=</token></term>
<listitem>
<para>is less than or equal to (within double parentheses)</para>
<para><userinput>(("$a" &lt;= "$b"))</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="intgt"/><token>&gt;</token></term>
<listitem>
<para>is greater than (within double parentheses)</para>
<para><userinput>(("$a" &gt; "$b"))</userinput></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gteq"/><token>&gt;=</token></term>
<listitem>
<para>is greater than or equal to (within double parentheses)</para>
<para><userinput>(("$a" &gt;= "$b"))</userinput></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="scomparison">
<title><anchor id="scomparison1"/>string comparison</title>
<varlistentry>
<term><token>=</token></term>
<listitem>
<para><anchor id="equalsignref"/></para>
<para>is equal to</para>
<para><userinput>if [ "$a" = "$b" ]</userinput></para>
<caution><para>Note the <link
linkend="whitespaceref">whitespace</link>
framing the <command>=</command>.</para>
<para><userinput>if [ "$a"="$b" ]</userinput> is
<emphasis>not</emphasis> equivalent to the
above.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="scomparison2"/><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>
<note><para>
The <token>==</token> comparison operator behaves differently
within a <link linkend="dblbrackets">double-brackets</link>
test than within single brackets.
<programlisting>[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
[ $a == z* ] # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
# Thanks, St&eacute;phane Chazelas</programlisting>
</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="notequal"/><token>!=</token></term>
<listitem>
<para>is not equal to</para>
<para><userinput>if [ "$a" != "$b" ]</userinput></para>
<para>This operator uses pattern matching within a <link
linkend="dblbrackets">[[ ... ]]</link> construct.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ltref"/><token>&lt;</token></term>
<listitem>
<para>is less than, in <link
linkend="asciidef">ASCII</link> alphabetical
order</para>
<para><userinput>if [[ "$a" &lt; "$b" ]]</userinput></para>
<para><userinput>if [ "$a" \&lt; "$b" ]</userinput></para>
<para>Note that the <quote>&lt;</quote> needs to be
<link linkend="escp">escaped</link> within a
<userinput>[ ]</userinput> construct.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gtref"/><token>&gt;</token></term>
<listitem>
<para>is greater than, in ASCII alphabetical order</para>
<para><userinput>if [[ "$a" &gt; "$b" ]]</userinput></para>
<para><userinput>if [ "$a" \&gt; "$b" ]</userinput></para>
<para>Note that the <quote>&gt;</quote> needs to be
escaped within a <userinput>[ ]</userinput> construct.</para>
<para>See <xref linkend="bubble"/> for an application of this
comparison operator.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="stringnull"/><token>-z</token></term>
<listitem>
<para>string is <firstterm>null</firstterm>,
that is, has zero length</para>
<para><programlisting> String='' # Zero-length ("null") string variable.
if [ -z "$String" ]
then
echo "\$String is null."
else
echo "\$String is NOT null."
fi # $String is null.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="stringnotnull"/><token>-n</token></term>
<listitem>
<para>string is not <firstterm>null.</firstterm></para>
<caution><para>The <userinput>-n</userinput> test
requires that the string be quoted within the
test brackets. Using an unquoted string with
<firstterm>! -z</firstterm>, 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 <firstterm>null</firstterm></title>
<programlisting>&strtest;</programlisting>
</example>
<example id="ex14">
<title><firstterm>zmore</firstterm></title>
<programlisting>&ex14;</programlisting>
</example>
<variablelist id="ccomparison">
<title><anchor id="ccomparison1"/>compound comparison</title>
<varlistentry>
<term><anchor id="compoundand"/><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><anchor id="compoundor"/><token>-o</token></term>
<listitem>
<para>logical or </para>
<para><replaceable>exp1 -o exp2</replaceable> returns
true if either exp1 <emphasis>or</emphasis> exp2 is
true.</para>
</listitem>
</varlistentry>
</variablelist>
<para>
These are similar to the Bash comparison operators
<command>&amp;&amp;</command> and <command>||</command>, used
within <link linkend="dblbrackets">double brackets</link>.
<programlisting>[[ condition1 &amp;&amp; condition2 ]]</programlisting>
</para>
<para>
The <command>-o</command> and <command>-a</command> operators
work with the <link linkend="ttestref">test</link> command or
occur within single test brackets.
<programlisting>if [ "$expr1" -a "$expr2" ]
then
echo "Both expr1 and expr2 are true."
else
echo "Either expr1 or expr2 is false."
fi</programlisting>
</para>
<caution>
<para>But, as <emphasis>rihad</emphasis> points out:
<programlisting>[ 1 -eq 1 ] &amp;&amp; [ -n "`echo true 1>&amp;2`" ] # true
[ 1 -eq 2 ] &amp;&amp; [ -n "`echo true 1>&amp;2`" ] # (no output)
# ^^^^^^^ False condition. So far, everything as expected.
# However ...
[ 1 -eq 2 -a -n "`echo true 1>&amp;2`" ] # true
# ^^^^^^^ False condition. So, why "true" output?
# Is it because both condition clauses within brackets evaluate?
[[ 1 -eq 2 &amp;&amp; -n "`echo true 1>&amp;2`" ]] # (no output)
# No, that's not it.
# Apparently &amp;&amp; and || "short-circuit" while -a and -o do not.</programlisting>
</para>
</caution>
<para>Refer to <xref linkend="andor"/>, <xref linkend="twodim"/>,
and <xref linkend="whx"/> to see compound comparison operators
in action.</para>
</sect1> <!-- Comparison operators (binary) -->
<sect1 id="nestedifthen">
<title>Nested <replaceable>if/then</replaceable> Condition Tests</title>
<para>Condition tests using the <replaceable>if/then</replaceable>
construct may be nested. The net result is equivalent to using the
<link linkend="logops1"><firstterm>&amp;&amp;</firstterm></link> compound
comparison operator.</para>
<para><programlisting>a=3
if [ "$a" -gt 0 ]
then
if [ "$a" -lt 5 ]
then
echo "The value of \"a\" lies somewhere between 0 and 5."
fi
fi
# Same result as:
if [ "$a" -gt 0 ] &amp;&amp; [ "$a" -lt 5 ]
then
echo "The value of \"a\" lies somewhere between 0 and 5."
fi</programlisting></para>
<para><xref linkend="cards"/> and <xref linkend="backlight"/>
demonstrate nested <replaceable>if/then</replaceable> condition
tests.</para>
</sect1> <!-- Nested if/then Tests -->
<sect1 id="testtest">
<title>Testing Your Knowledge of Tests</title>
<para>The systemwide <filename>xinitrc</filename> file can be used
to launch the X server. This file contains quite a number
of <firstterm>if/then</firstterm> tests. The following
is excerpted from an <quote>ancient</quote> version of
<filename>xinitrc</filename> (<firstterm>Red Hat 7.1</firstterm>,
or thereabouts).</para>
<para><programlisting>if [ -f $HOME/.Xclients ]; then
exec $HOME/.Xclients
elif [ -f /etc/X11/xinit/Xclients ]; then
exec /etc/X11/xinit/Xclients
else
# failsafe settings. Although we should never get here
# (we provide fallbacks in Xclients as well) it can't hurt.
xclock -geometry 100x100-5+5 &amp;
xterm -geometry 80x50-50+150 &amp;
if [ -f /usr/bin/netscape -a -f /usr/share/doc/HTML/index.html ]; then
netscape /usr/share/doc/HTML/index.html &amp;
fi
fi</programlisting></para>
<para>Explain the <firstterm>test</firstterm> constructs in the
above snippet, then examine an updated version of the
file, <filename>/etc/X11/xinit/xinitrc</filename>, and
analyze the <firstterm>if/then</firstterm> test constructs
there. You may need to refer ahead to the discussions of <link
linkend="grepref">grep</link>, <link linkend="sedref">sed</link>,
and <link linkend="regexref">regular expressions</link>.</para>
</sect1> <!-- Testing Your Knowledge of Tests -->
</chapter> <!-- Tests -->
<chapter id="operations">
<title>Operations and Related Topics</title>
<sect1 id="ops">
<title>Operators</title>
<variablelist id="asnop">
<title><anchor id="asnop1"/>assignment</title>
<varlistentry>
<term><replaceable>variable assignment</replaceable></term>
<listitem><para>Initializing or changing the value of a variable</para>
</listitem>
</varlistentry>
<varlistentry>
<term>=</term>
<listitem>
<indexterm>
<primary>=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>=</secondary>
</indexterm>
<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">= test
operator</link>.</para>
<para>
<programlisting># = as a test operator
if [ "$string1" = "$string2" ]
then
command
fi
# if [ "X$string1" = "X$string2" ] is safer,
#+ to prevent an error message should one of the variables be empty.
# (The prepended "X" characters cancel out.)</programlisting>
</para>
</caution>
</listitem>
</varlistentry>
</variablelist>
<indexterm>
<primary>expr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>expr</secondary>
</indexterm>
<indexterm>
<primary>let</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>let</secondary>
</indexterm>
<variablelist id="arops">
<title><anchor id="arops1"/>arithmetic operators</title>
<varlistentry>
<term><token>+</token></term>
<listitem>
<indexterm>
<primary>+</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>+</secondary>
</indexterm>
<indexterm>
<primary>addition</primary>
</indexterm>
<indexterm>
<primary>plus</primary>
</indexterm>
<para>plus</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-</token></term>
<listitem>
<indexterm>
<primary>-</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>-</secondary>
</indexterm>
<indexterm>
<primary>subtraction</primary>
</indexterm>
<indexterm>
<primary>minus</primary>
</indexterm>
<para>minus</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*</token></term>
<listitem>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>*</secondary>
</indexterm>
<indexterm>
<primary>multiplication</primary>
</indexterm>
<para>multiplication</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/</token></term>
<listitem>
<indexterm>
<primary>/</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>/</secondary>
</indexterm>
<indexterm>
<primary>division</primary>
</indexterm>
<para>division</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exponentiationref"/><token>**</token></term>
<listitem>
<indexterm>
<primary>**</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>**</secondary>
</indexterm>
<indexterm>
<primary>exponentiation</primary>
</indexterm>
<para>exponentiation</para>
<para>
<programlisting># Bash, version 2.02, introduced the "**" exponentiation operator.
let "z=5**3" # 5 * 5 * 5
echo "z = $z" # z = 125</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="moduloref"/><token>%</token></term>
<listitem>
<indexterm>
<primary>%</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>%</secondary>
</indexterm>
<indexterm>
<primary>modulo</primary>
</indexterm>
<para>modulo, or mod (returns the
<firstterm>remainder</firstterm> of an integer division
operation)</para>
<para>
<screen><prompt>bash$ </prompt><userinput>expr 5 % 3</userinput>
<computeroutput>2</computeroutput>
</screen>
<emphasis>5/3 = 1, with remainder 2</emphasis>
</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"/> and
<xref linkend="collatz"/>). It can even be used to generate
prime numbers, (see <xref linkend="primes"/>). Modulo turns
up surprisingly often in numerical recipes.</para>
<example id="gcd">
<title>Greatest common divisor</title>
<programlisting>&gcd;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="arithopscomb"/><token>+=</token></term>
<listitem>
<indexterm>
<primary>+=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>+=</secondary>
</indexterm>
<indexterm>
<primary>plus-equal</primary>
</indexterm>
<para><firstterm>plus-equal</firstterm> (increment variable
by a constant)
<footnote><para>In a different context, <command>+=</command> can
serve as a <firstterm>string concatenation</firstterm>
operator. This can be useful for <link
linkend="pathappend">modifying <firstterm>environmental
variables</firstterm></link>.</para></footnote>
</para>
<para><userinput>let "var += 5"</userinput> results in
<parameter>var</parameter> being incremented by
<literal>5</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-=</token></term>
<listitem>
<indexterm>
<primary>-=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>-=</secondary>
</indexterm>
<indexterm>
<primary>minus-equal</primary>
</indexterm>
<para><firstterm>minus-equal</firstterm> (decrement
variable by a constant)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>*=</token></term>
<listitem>
<indexterm>
<primary>*=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>*=</secondary>
</indexterm>
<indexterm>
<primary>times-equal</primary>
</indexterm>
<para><firstterm>times-equal</firstterm> (multiply
variable by a constant)</para>
<para><userinput>let "var *= 4"</userinput> results in <parameter>var</parameter>
being multiplied by <literal>4</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>/=</token></term>
<listitem>
<indexterm>
<primary>/=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>/=</secondary>
</indexterm>
<indexterm>
<primary>slash-equal</primary>
</indexterm>
<para><firstterm>slash-equal</firstterm> (divide
variable by a constant)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>%=</token></term>
<listitem>
<indexterm>
<primary>%=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>%=</secondary>
</indexterm>
<indexterm>
<primary>mod-equal</primary>
</indexterm>
<para><firstterm>mod-equal</firstterm>
(<firstterm>remainder</firstterm>
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>
<para><anchor id="intvarref"/></para>
<note>
<para>Integer variables in older versions of Bash were signed
<firstterm>long</firstterm> (32-bit) integers, in the range of
-2147483648 to 2147483647. An operation that took a variable
outside these limits gave an erroneous result.</para>
<para>
<programlisting>echo $BASH_VERSION # 1.14
a=2147483646
echo "a = $a" # a = 2147483646
let "a+=1" # Increment "a".
echo "a = $a" # a = 2147483647
let "a+=1" # increment "a" again, past the limit.
echo "a = $a" # a = -2147483648
# ERROR: out of range,
# + and the leftmost bit, the sign bit,
# + has been set, making the result negative.</programlisting>
</para>
<para>As of version &gt;= 2.05b, Bash supports 64-bit integers.</para>
</note>
<caution>
<para><anchor id="nofloatingpoint"/></para>
<para>Bash does not understand floating point arithmetic. It
treats numbers containing a decimal point as strings.</para>
<para>
<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>
</para>
<para>Use <link linkend="bcref">bc</link> in scripts 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 <link linkend="socketref">sockets</link>. <quote>Bit
flipping</quote> is more relevant to compiled languages, such
as C and C++, which provide direct access to system
hardware. However, see <emphasis>vladz's</emphasis>
ingenious use of bitwise operators in his
<firstterm>base64.sh</firstterm> (<xref linkend="base64"/>)
script. </para></formalpara>
<variablelist id="bitwsops">
<title><anchor id="bitwsops1"/>bitwise operators</title>
<varlistentry>
<term><token>&lt;&lt;</token></term>
<listitem>
<indexterm>
<primary>&lt;&lt;</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&lt;&lt;</secondary>
</indexterm>
<indexterm>
<primary>left shift</primary>
</indexterm>
<para>bitwise left shift (multiplies by <literal>2</literal>
for each shift position)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&lt;&lt;=</token></term>
<listitem>
<indexterm>
<primary>&lt;&lt;=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&lt;&lt;=</secondary>
</indexterm>
<indexterm>
<primary>left-shift-equal</primary>
</indexterm>
<para><firstterm>left-shift-equal</firstterm></para>
<para><userinput>let "var &lt;&lt;= 2"</userinput> results in <parameter>var</parameter>
left-shifted <literal>2</literal> bits (multiplied by <literal>4</literal>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>></token></term>
<listitem>
<indexterm>
<primary>>></primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>>></secondary>
</indexterm>
<indexterm>
<primary>right shift</primary>
</indexterm>
<para>bitwise right shift (divides by <literal>2</literal>
for each shift position)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>>>=</token></term>
<listitem>
<indexterm>
<primary>>>=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>>>=</secondary>
</indexterm>
<indexterm>
<primary>right-shift-equal</primary>
</indexterm>
<para><firstterm>right-shift-equal</firstterm>
(inverse of <token>&lt;&lt;=</token>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&amp;</token></term>
<listitem>
<indexterm>
<primary>&amp;</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&amp;</secondary>
</indexterm>
<indexterm>
<primary>AND</primary>
<secondary>bitwise</secondary>
</indexterm>
<para>bitwise AND</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&amp;=</token></term>
<listitem>
<indexterm>
<primary>&amp;=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>&amp;=</secondary>
</indexterm>
<indexterm>
<primary>and-equal</primary>
</indexterm>
<para>bitwise <firstterm>AND-equal</firstterm></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|</token></term>
<listitem>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>|</secondary>
</indexterm>
<indexterm>
<primary>OR</primary>
<secondary>bitwise</secondary>
</indexterm>
<para>bitwise OR</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>|=</token></term>
<listitem>
<indexterm>
<primary>|=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>|=</secondary>
</indexterm>
<indexterm>
<primary>OR-equal</primary>
</indexterm>
<para>bitwise <firstterm>OR-equal</firstterm></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>~</token></term>
<listitem>
<indexterm>
<primary>~</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>~</secondary>
</indexterm>
<indexterm>
<primary>negate</primary>
</indexterm>
<para>bitwise NOT</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>^</token></term>
<listitem>
<indexterm>
<primary>^</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>^</secondary>
</indexterm>
<indexterm>
<primary>XOR</primary>
</indexterm>
<para>bitwise XOR</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>^=</token></term>
<listitem>
<indexterm>
<primary>^=</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>^=</secondary>
</indexterm>
<indexterm>
<primary>XOR-equal</primary>
</indexterm>
<para>bitwise <firstterm>XOR-equal</firstterm></para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="logops">
<title><anchor id="logops1"/>logical (boolean) operators</title>
<varlistentry>
<term><token>!</token></term>
<listitem>
<indexterm>
<primary>!</primary>
</indexterm>
<indexterm>
<primary>operator</primary>
<secondary>!</secondary>
</indexterm>
<indexterm>
<primary>NOT</primary>
</indexterm>
<para>NOT</para>
<para><programlisting>if [ ! -f $FILENAME ]
then
...</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>&amp;&amp;</token></term>
<listitem>
<indexterm>
<primary>&amp;&amp;</primary>
</indexterm>
<indexterm>
<primary>operator</primary>
<secondary>&amp;&amp;</secondary>
</indexterm>
<indexterm>
<primary>AND</primary>
<secondary>logical</secondary>
</indexterm>
<para>AND</para>
<para><programlisting>if [ $condition1 ] &amp;&amp; [ $condition2 ]
# Same as: if [ $condition1 -a $condition2 ]
# Returns true if both condition1 and condition2 hold true...
if [[ $condition1 &amp;&amp; $condition2 ]] # Also works.
# Note that &amp;&amp; operator not permitted <emphasis>inside brackets</emphasis>
#+ of [ ... ] construct.</programlisting></para>
<note><para><token>&amp;&amp;</token> may also be used, depending on context,
in an <link linkend="listconsref">and list</link>
to concatenate commands.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="orref"/><token>||</token></term>
<listitem>
<indexterm>
<primary>||</primary>
</indexterm>
<indexterm>
<primary>operator</primary>
<secondary>||</secondary>
</indexterm>
<indexterm>
<primary>OR</primary>
<secondary>logical</secondary>
</indexterm>
<para>OR</para>
<para><programlisting>if [ $condition1 ] || [ $condition2 ]
# Same as: if [ $condition1 -o $condition2 ]
# Returns true if either condition1 or condition2 holds true...
if [[ $condition1 || $condition2 ]] # Also works.
# Note that || operator not permitted <emphasis>inside brackets</emphasis>
#+ of a [ ... ] construct.</programlisting></para>
<note><para>Bash tests the <link linkend="exitstatusref">exit
status</link> of each statement linked with a logical
operator.</para></note>
<example id="andor">
<title>Compound Condition Tests Using &amp;&amp; and ||</title>
<programlisting>&andor;</programlisting>
</example>
<para>The <token>&amp;&amp;</token> and <token>||</token> operators also
find use in an arithmetic context.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $(( 1 &amp;&amp; 2 )) $((3 &amp;&amp; 0)) $((4 || 0)) $((0 || 0))</userinput>
<computeroutput>1 0 1 0</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="miscop">
<title><anchor id="miscop1"/>miscellaneous operators</title>
<varlistentry>
<term><anchor id="commaop"/><token>,</token></term>
<listitem>
<indexterm>
<primary>,</primary>
</indexterm>
<indexterm>
<primary>operation</primary>
<secondary>,</secondary>
</indexterm>
<indexterm>
<primary>linking</primary>
</indexterm>
<para>Comma operator</para>
<para>The <command>comma operator</command> chains together
two or more arithmetic operations. All the operations are
evaluated (with possible <firstterm>side
effects</firstterm>.
<footnote><para><firstterm>Side effects</firstterm>
are, of course, unintended -- and usually undesirable --
consequences.</para></footnote>
</para>
<para>
<programlisting>let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
echo "t1 = $t1" ^^^^^^ # t1 = 11
# Here t1 is set to the result of the last operation. Why?
let "t2 = ((a = 9, 15 / 3))" # Set "a" and calculate "t2".
echo "t2 = $t2 a = $a" # t2 = 5 a = 9</programlisting>
</para>
<para>The comma operator finds use mainly in <link
linkend="forloopref1">for loops</link>. See <xref
linkend="forloopc"/>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Operators -->
<sect1 id="Numerical-Constants">
<title>Numerical Constants</title>
<para><anchor id="numconstants"/>A shell script interprets a number
as decimal (base 10), unless that number has a
special prefix or notation. A number preceded by a
<replaceable>0</replaceable> is <replaceable>octal</replaceable>
(base 8). A number preceded by <replaceable>0x</replaceable>
is <replaceable>hexadecimal</replaceable> (base 16). A number
with an embedded <replaceable>#</replaceable> evaluates as
<replaceable>BASE#NUMBER</replaceable> (with range and notational
restrictions).</para>
<example id="numbers">
<title>Representation of numerical constants</title>
<programlisting>&numbers;</programlisting>
</example>
</sect1> <!-- Numerical-Constants -->
<sect1 id="dblparens">
<title>The Double-Parentheses Construct</title>
<para><anchor id="dblparensref"/></para>
<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
<userinput>a</userinput> to <userinput>5 + 3</userinput>, or
<userinput>8</userinput>. However, this double-parentheses
construct is also a mechanism for allowing C-style
manipulation of variables in Bash, for example,
<varname>(( var++ ))</varname>.</para>
<para><anchor id="plusplusref"/></para>
<example id="cvars">
<title>C-style manipulation of variables</title>
<programlisting>&cvars;</programlisting>
</example>
<para>See also <xref linkend="forloopc"/> and <xref
linkend="numbers"/>.</para>
</sect1> <!-- The Double-Parentheses Construct -->
<sect1 id="opprecedence">
<title>Operator Precedence</title>
<para><anchor id="opprecedence1"/></para>
<para>
In a script, operations execute in order of
<firstterm>precedence</firstterm>: the higher precedence operations
execute <emphasis>before</emphasis> the lower precedence ones.
<footnote><para><firstterm>Precedence</firstterm>, in this context,
has approximately the same meaning as
<firstterm>priority</firstterm></para></footnote>
</para>
&opprectable;
<para>In practice, all you really need to remember is the
following:</para>
<itemizedlist>
<listitem>
<para>The <quote>My Dear Aunt Sally</quote> mantra (<emphasis>multiply,
divide, add, subtract</emphasis>) for the familiar <link
linkend="arops1">arithmetic operations</link>.</para>
</listitem>
<listitem>
<para>The <firstterm>compound</firstterm> logical operators,
<command>&amp;&amp;</command>, <command>||</command>, <command>-a</command>,
and <command>-o</command> have low precedence.</para>
</listitem>
<listitem>
<para>The order of evaluation of equal-precedence operators is
usually <firstterm>left-to-right</firstterm>.</para>
</listitem>
</itemizedlist>
<para>Now, let's utilize our knowledge of operator precedence to
analyze a couple of lines from the
<filename>/etc/init.d/functions file</filename>, as found in
the <firstterm>Fedora Core</firstterm> Linux distro.</para>
<para><programlisting>while [ -n "$remaining" -a "$retry" -gt 0 ]; do
# This looks rather daunting at first glance.
# Separate the conditions:
while [ -n "$remaining" -a "$retry" -gt 0 ]; do
# --condition 1-- ^^ --condition 2-
# If variable "$remaining" is not zero length
#+ AND (-a)
#+ variable "$retry" is greater-than zero
#+ then
#+ the [ expresion-within-condition-brackets ] returns success (0)
#+ and the while-loop executes an iteration.
# ==============================================================
# Evaluate "condition 1" and "condition 2" ***before***
#+ ANDing them. Why? Because the AND (-a) has a lower precedence
#+ than the -n and -gt operators,
#+ and therefore gets evaluated *last*.
#################################################################
if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
# Again, separate the conditions:
if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
# --condition 1--------- ^^ --condition 2-----
# If file "/etc/sysconfig/i18n" exists
#+ AND (-a)
#+ variable $NOLOCALE is zero length
#+ then
#+ the [ test-expresion-within-condition-brackets ] returns success (0)
#+ and the commands following execute.
#
# As before, the AND (-a) gets evaluated *last*
#+ because it has the lowest precedence of the operators within
#+ the test brackets.
# ==============================================================
# Note:
# ${NOLOCALE:-} is a parameter expansion that seems redundant.
# But, if $NOLOCALE has not been declared, it gets set to *null*,
#+ in effect declaring it.
# This makes a difference in some contexts.</programlisting></para>
<tip>
<para>To avoid confusion or error in a complex sequence of test
operators, break up the sequence into bracketed sections.
<programlisting>if [ "$v1" -gt "$v2" -o "$v1" -lt "$v2" -a -e "$filename" ]
# Unclear what's going on here...
if [[ "$v1" -gt "$v2" ]] || [[ "$v1" -lt "$v2" ]] &amp;&amp; [[ -e "$filename" ]]
# Much better -- the condition tests are grouped in logical sections.</programlisting>
</para>
</tip>
</sect1> <!-- Operator Precedence -->
</chapter> <!-- Operations -->
</part> <!-- Part 2 (Basics) -->
<part label="Part 3" id="part3">
<title>Beyond the Basics</title>
<chapter id="variables2">
<title>Another Look at Variables</title>
<para>Used properly, variables can add power and flexibility
to scripts. This requires learning their subtleties and
nuances.</para>
<sect1 id="internalvariables">
<title>Internal Variables</title>
<variablelist id="internalvariables1">
<varlistentry>
<term><replaceable><link
linkend="builtinref">Builtin</link> variables:</replaceable></term>
<listitem><para>variables affecting bash script behavior</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bashvarref"/><varname>$BASH</varname></term>
<listitem>
<indexterm>
<primary>$BASH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH</secondary>
</indexterm>
<indexterm>
<primary>path to bash</primary>
</indexterm>
<para>The path to the <firstterm>Bash</firstterm>
binary itself
<screen><prompt>bash$ </prompt><userinput>echo $BASH</userinput>
<computeroutput>/bin/bash</computeroutput></screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="bashenvref"/><varname>$BASH_ENV</varname></term>
<listitem>
<indexterm>
<primary>$BASH_ENV</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH_ENV</secondary>
</indexterm>
<para>An <link linkend="envref">environmental
variable</link> pointing to a Bash startup file to be read
when a script is invoked</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bashsubshellref"/><varname>$BASH_SUBSHELL</varname></term>
<listitem>
<indexterm>
<primary>$BASH_SUBSHELL</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>subshell</secondary>
</indexterm>
<para>A variable indicating the <link
linkend="subshellsref">subshell</link> level. This is a
new addition to Bash, <link linkend="bash3ref">version 3</link>.</para>
<para>See <xref linkend="subshell"/> for usage.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="bashpidref"/><varname>$BASHPID</varname></term>
<listitem>
<indexterm>
<primary>$BASHPID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>process ID</secondary>
</indexterm>
<para><firstterm>Process ID</firstterm>
of the current instance of Bash. This is not the same as the
<link linkend="proccid">$$</link> variable, but it often
gives the same result.</para>
<para>
<screen>
<prompt>bash4$ </prompt><userinput>echo $$</userinput>
<computeroutput>11015</computeroutput>
<prompt>bash4$ </prompt><userinput>echo $BASHPID</userinput>
<computeroutput>11015</computeroutput>
<prompt>bash4$ </prompt><userinput>ps ax | grep bash4</userinput>
<computeroutput>11015 pts/2 R 0:00 bash4</computeroutput>
</screen>
</para>
<para><anchor id="bashpidref2"/>But ...</para>
<para><programlisting>
#!/bin/bash4
echo "\$\$ outside of subshell = $$" # 9602
echo "\$BASH_SUBSHELL outside of subshell = $BASH_SUBSHELL" # 0
echo "\$BASHPID outside of subshell = $BASHPID" # 9602
echo
( echo "\$\$ inside of subshell = $$" # 9602
echo "\$BASH_SUBSHELL inside of subshell = $BASH_SUBSHELL" # 1
echo "\$BASHPID inside of subshell = $BASHPID" ) # 9603
# Note that $$ returns PID of parent process.
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$BASH_VERSINFO[n]</varname></term>
<listitem>
<indexterm>
<primary>$BASH_VERSINFO</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>version information</secondary>
</indexterm>
<para>A 6-element <link linkend="arrayref">array</link>
containing version information about the installed release
of Bash. This is similar to <varname>$BASH_VERSION</varname>,
below, but a bit more detailed.</para>
<para>
<programlisting># Bash version info:
for n in 0 1 2 3 4 5
do
echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
done
# BASH_VERSINFO[0] = 3 # Major version no.
# BASH_VERSINFO[1] = 00 # Minor version no.
# BASH_VERSINFO[2] = 14 # Patch level.
# BASH_VERSINFO[3] = 1 # Build version.
# BASH_VERSINFO[4] = release # Release status.
# BASH_VERSINFO[5] = i386-redhat-linux-gnu # Architecture
# (same as $MACHTYPE).</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$BASH_VERSION</varname></term>
<listitem>
<indexterm>
<primary>$BASH_VERSION</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$BASH_VERSION</secondary>
</indexterm>
<para>The version of Bash installed on the system</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo $BASH_VERSION</userinput>
<computeroutput>3.2.25(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. <link linkend="shellvarref">$SHELL</link>
does not necessarily give the correct answer.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cdpathref"/><varname>$CDPATH</varname></term>
<listitem>
<indexterm>
<primary>$CDPATH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$CDPATH</secondary>
</indexterm>
<indexterm>
<primary>cd path</primary>
</indexterm>
<indexterm>
<primary>cd</primary>
<secondary>path</secondary>
</indexterm>
<para>A colon-separated list of search paths
available to the <link linkend="cdref">cd</link>
command, similar in function to the <link
linkend="pathref">$PATH</link> variable for binaries.
The <varname>$CDPATH</varname> variable may be set in the
local <link
linkend="bashrc"><filename>~/.bashrc</filename></link>
file.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cd bash-doc</userinput>
<computeroutput>bash: cd: bash-doc: No such file or directory</computeroutput>
<prompt>bash$ </prompt><userinput>CDPATH=/usr/share/doc</userinput>
<prompt>bash$ </prompt><userinput>cd bash-doc</userinput>
<computeroutput>/usr/share/doc/bash-doc</computeroutput>
<prompt>bash$ </prompt><userinput>echo $PWD</userinput>
<computeroutput>/usr/share/doc/bash-doc</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dirstackref"/><varname>$DIRSTACK</varname></term>
<listitem>
<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>
<para>The top value in the directory stack
<footnote><para><anchor id="stackdefref"/>
A <firstterm>stack register</firstterm>
is a set of consecutive memory locations, such that
the values stored (<firstterm>pushed</firstterm>)
are retrieved (<firstterm>popped</firstterm>)
in <emphasis>reverse</emphasis> order. The last
value stored is the first retrieved. This is
sometimes called a <replaceable>LIFO</replaceable>
(<firstterm>last-in-first-out</firstterm>) or
<firstterm>pushdown</firstterm> stack.</para></footnote>
(affected by <link linkend="pushdref">pushd</link> and <link
linkend="popdref">popd</link>)</para> <para>This builtin
variable corresponds to the <link linkend="dirsd">dirs</link>
command, however <command>dirs</command> shows the entire
contents of the directory stack.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$EDITOR</varname></term>
<listitem>
<indexterm>
<primary>$EDITOR</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$EDITOR</secondary>
</indexterm>
<indexterm>
<primary>editor</primary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>$EUID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$EUID</secondary>
</indexterm>
<indexterm>
<primary>effective user ID</primary>
</indexterm>
<para><quote>effective</quote> user ID number</para>
<para>Identification number of whatever identity the
current user has assumed, perhaps by means of <link
linkend="suref">su</link>.</para>
<caution><para>The <varname>$EUID</varname> is not necessarily
the same as the <link
linkend="uidref">$UID</link>.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$FUNCNAME</varname></term>
<listitem>
<indexterm>
<primary>$FUNCNAME</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>function</secondary>
</indexterm>
<indexterm>
<primary>name</primary>
</indexterm>
<para>Name of the current function</para>
<para><programlisting>xyz23 ()
{
echo "$FUNCNAME now executing." # xyz23 now executing.
}
xyz23
echo "FUNCNAME = $FUNCNAME" # FUNCNAME =
# Null value outside a function.</programlisting>
</para>
<para>See also <xref linkend="usegetopt"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$GLOBIGNORE</varname></term>
<listitem>
<indexterm>
<primary>$GLOBIGNORE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>globbing</secondary>
</indexterm>
<indexterm>
<primary>ignore</primary>
</indexterm>
<para>A list of filename patterns to be excluded from
matching in <link linkend="globbingref">globbing</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="groupsref"/><varname>$GROUPS</varname></term>
<listitem>
<indexterm>
<primary>$GROUPS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$GROUPS</secondary>
</indexterm>
<indexterm>
<primary>groups</primary>
</indexterm>
<para>Groups current user belongs to</para>
<para>This is a listing (array) of the group id numbers for
current user, as recorded in
<link
linkend="datafilesref1"><filename>/etc/passwd</filename></link>
and <filename>/etc/group</filename>.
</para>
<para>
<screen>
<prompt>root# </prompt><userinput>echo $GROUPS</userinput>
<computeroutput>0</computeroutput>
<prompt>root# </prompt><userinput>echo ${GROUPS[1]}</userinput>
<computeroutput>1</computeroutput>
<prompt>root# </prompt><userinput>echo ${GROUPS[5]}</userinput>
<computeroutput>6</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="homedirref"/><varname>$HOME</varname></term>
<listitem>
<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>
<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>
<listitem>
<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>
<para>The <link linkend="hnameref">hostname</link> command
assigns the system host 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>
<listitem>
<indexterm>
<primary>$HOSTTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$HOSTTYPE</secondary>
</indexterm>
<indexterm>
<primary>host type</primary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>$IFS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$IFS</secondary>
</indexterm>
<indexterm>
<primary>internal field separator</primary>
</indexterm>
<para>internal field separator</para>
<para>This variable determines how Bash recognizes <link
linkend="fieldref">fields</link>, or word boundaries,
when it interprets character strings.</para>
<para><anchor id="ifsws"/></para>
<para>$IFS 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"</userinput>
<computeroutput>
</computeroutput>
<computeroutput>(With $IFS set to default, a blank line displays.)</computeroutput>
<prompt>bash$ </prompt><userinput>echo "$IFS" | cat -vte</userinput>
<computeroutput> ^I$
$</computeroutput>
<computeroutput>(Show whitespace: here a single space, ^I [horizontal tab],
and newline, and display "$" at end-of-line.)</computeroutput>
<prompt>bash$ </prompt><userinput>bash -c 'set w x y z; IFS=":-;"; echo "$*"'</userinput>
<computeroutput>w:x:y:z</computeroutput>
<computeroutput>(Read commands from string and assign any arguments to pos params.)</computeroutput>
</screen>
</para>
<para>Set <varname>$IFS</varname> to eliminate whitespace
in <link linkend="pathnameref">pathnames</link>.
<programlisting>IFS="$(printf '\n\t')" # Per David Wheeler.</programlisting>
</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>(Many thanks, St&eacute;phane Chazelas, for clarification
and above examples.)</para>
<para>See also <xref linkend="isspammer"/>, <xref
linkend="bingrep"/>, and <xref linkend="mailboxgrep"/>
for instructive examples of using
<varname>$IFS</varname>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$IGNOREEOF</varname></term>
<listitem>
<indexterm>
<primary>$IGNOREEOF</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$IGNOREEOF</secondary>
</indexterm>
<indexterm>
<primary>Ignore EOF</primary>
</indexterm>
<para>Ignore EOF: how many end-of-files (control-D)
the shell will ignore before logging out.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$LC_COLLATE</varname></term>
<listitem>
<indexterm>
<primary>$LC_COLLATE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$LC_COLLATE</secondary>
</indexterm>
<indexterm>
<primary>lowercase collate</primary>
</indexterm>
<para>Often set in the <link
linkend="sample-bashrc"><filename>.bashrc</filename></link>
or <filename>/etc/profile</filename> files, this
variable controls collation order in filename
expansion and pattern matching. If mishandled,
<varname>LC_COLLATE</varname> can cause unexpected results in
<link linkend="globbingref">filename globbing</link>.</para>
<note><para>As of version 2.05 of Bash,
filename globbing no longer distinguishes between lowercase
and uppercase letters in a character range between
brackets. For example, <command>ls [A-M]*</command>
would match both <filename>File1.txt</filename>
and <filename>file1.txt</filename>. To revert to
the customary behavior of bracket matching, set
<varname>LC_COLLATE</varname> to <option>C</option>
by an <userinput>export LC_COLLATE=C</userinput>
in <filename>/etc/profile</filename> and/or
<filename>~/.bashrc</filename>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$LC_CTYPE</varname></term>
<listitem>
<indexterm>
<primary>$LC_CTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$LC_CTYPE</secondary>
</indexterm>
<indexterm>
<primary>lowercase character type</primary>
</indexterm>
<para>This internal variable controls character interpretation
in <link linkend="globbingref">globbing</link> and pattern
matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="linenoref"/><varname>$LINENO</varname></term>
<listitem>
<indexterm>
<primary>$LINENO</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$LINENO</secondary>
</indexterm>
<indexterm>
<primary>line number</primary>
</indexterm>
<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># *** BEGIN DEBUG BLOCK ***
last_cmd_arg=$_ # Save it.
echo "At line number $LINENO, variable \"v1\" = $v1"
echo "Last command argument processed = $last_cmd_arg"
# *** END DEBUG BLOCK ***</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="machtyperef"/><varname>$MACHTYPE</varname></term>
<listitem>
<indexterm>
<primary>$MACHTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$MACHTYPE</secondary>
</indexterm>
<indexterm>
<primary>machine type</primary>
</indexterm>
<para>machine type</para>
<para>Identifies the system hardware.</para>
<screen><prompt>bash$ </prompt><userinput>echo $MACHTYPE</userinput>
<computeroutput>i686</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="oldpwd"/><varname>$OLDPWD</varname></term>
<listitem>
<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>
<para>Old working directory
(<quote>OLD-Print-Working-Directory</quote>,
previous directory you were in).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$OSTYPE</varname></term>
<listitem>
<indexterm>
<primary>$OSTYPE</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$OSTYPE</secondary>
</indexterm>
<indexterm>
<primary>os type</primary>
</indexterm>
<para>operating system type</para>
<screen><prompt>bash$ </prompt><userinput>echo $OSTYPE</userinput>
<computeroutput>linux</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pathref"/><varname>$PATH</varname></term>
<listitem>
<indexterm>
<primary>$PATH</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PATH</secondary>
</indexterm>
<indexterm>
<primary>path to binaries</primary>
</indexterm>
<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
<firstterm>path</firstterm> for the executable. The path
is stored in the <link linkend="envref">environmental
variable</link>, <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 <link
linkend="sample-bashrc"><filename>~/.bashrc</filename></link>
(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>
<para><anchor id="currentwdref"/></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="pipestatusref"/><varname>$PIPESTATUS</varname></term>
<listitem>
<indexterm>
<primary>$PIPESTATUS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>pipe</secondary>
</indexterm>
<para><link linkend="arrayref">Array</link> variable holding
<link linkend="exitstatusref">exit status</link>(es) of
last executed <firstterm>foreground</firstterm> <link
linkend="piperef">pipe</link>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo $PIPESTATUS</userinput>
<computeroutput>0</computeroutput>
<prompt>bash$ </prompt><userinput>ls -al | bogus_command</userinput>
<computeroutput>bash: bogus_command: command not found</computeroutput>
<prompt>bash$ </prompt><userinput>echo ${PIPESTATUS[1]}</userinput>
<computeroutput>127</computeroutput>
<prompt>bash$ </prompt><userinput>ls -al | bogus_command</userinput>
<computeroutput>bash: bogus_command: command not found</computeroutput>
<prompt>bash$ </prompt><userinput>echo $?</userinput>
<computeroutput>127</computeroutput>
</screen>
</para>
<para>The members of the <varname>$PIPESTATUS</varname>
array hold the exit status of each respective command
executed in a pipe. <varname>$PIPESTATUS[0]</varname>
holds the exit status of the first command in the pipe,
<varname>$PIPESTATUS[1]</varname> the exit status of
the second command, and so on.</para>
<caution>
<para>
The <varname>$PIPESTATUS</varname> variable
may contain an erroneous <errorcode>0</errorcode> value
in a login shell (in releases prior to 3.0 of Bash).
</para>
<para>
<screen>
<prompt>tcsh% </prompt><userinput>bash</userinput>
<prompt>bash$ </prompt><userinput>who | grep nobody | sort</userinput>
<prompt>bash$ </prompt><userinput>echo ${PIPESTATUS[*]}</userinput>
<computeroutput>0</computeroutput>
</screen>
</para>
<para>
The above lines contained in a script would produce the expected
<computeroutput>0 1 0</computeroutput> output.
</para>
<para>
Thank you, Wayne Pollock for pointing this out and supplying the
above example.
</para>
</caution>
<note>
<para>The <varname>$PIPESTATUS</varname> variable gives
unexpected results in some contexts.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo $BASH_VERSION</userinput>
<computeroutput>3.00.14(1)-release</computeroutput>
<prompt>bash$ </prompt><userinput>$ ls | bogus_command | wc</userinput>
<computeroutput>bash: bogus_command: command not found
0 0 0</computeroutput>
<prompt>bash$ </prompt><userinput>echo ${PIPESTATUS[@]}</userinput>
<computeroutput>141 127 0</computeroutput>
</screen>
</para>
<para>Chet Ramey attributes the above output to the behavior of
<link linkend="lsref">ls</link>. If <firstterm>ls</firstterm>
writes to a <firstterm>pipe</firstterm> whose output is not
read, then <replaceable>SIGPIPE</replaceable> kills it,
and its <link linkend="exitstatusref">exit status</link>
is <returnvalue>141</returnvalue>. Otherwise
its exit status is <returnvalue>0</returnvalue>,
as expected. This likewise is the case for <link
linkend="trref">tr</link>.</para>
</note>
<note>
<para><varname>$PIPESTATUS</varname> is a
<quote>volatile</quote> variable. It needs to be
captured immediately after the pipe in question, before
any other command intervenes.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>$ ls | bogus_command | wc</userinput>
<computeroutput>bash: bogus_command: command not found
0 0 0</computeroutput>
<prompt>bash$ </prompt><userinput>echo ${PIPESTATUS[@]}</userinput>
<computeroutput>0 127 0</computeroutput>
<prompt>bash$ </prompt><userinput>echo ${PIPESTATUS[@]}</userinput>
<computeroutput>0</computeroutput>
</screen>
</para>
</note>
<note>
<para>The <link linkend="pipefailref">pipefail option</link>
may be useful in cases where
<varname>$PIPESTATUS</varname> does not give the desired
information.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ppidref"/><varname>$PPID</varname></term>
<listitem>
<indexterm>
<primary>$PPID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PPID</secondary>
</indexterm>
<indexterm>
<primary>process ID</primary>
</indexterm>
<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><varname>$PROMPT_COMMAND</varname></term>
<listitem>
<indexterm>
<primary>$PROMPT_COMMAND</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>prompt</secondary>
</indexterm>
<para>A variable holding a command to be executed
just before the primary prompt, <varname>$PS1</varname>
is to be displayed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ps1ref"/><varname>$PS1</varname></term>
<listitem>
<indexterm>
<primary>$PS1</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS1</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
</indexterm>
<para>This is the main prompt, seen at the command-line.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="secpromptref"/><varname>$PS2</varname></term>
<listitem>
<indexterm>
<primary>$PS2</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS2</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>secondary</secondary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>$PS3</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS3</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>tertiary</secondary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>$PS4</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$PS4</secondary>
</indexterm>
<indexterm>
<primary>prompt</primary>
<secondary>quartenary</secondary>
</indexterm>
<para>The quartenary prompt, shown at the beginning of
each line of output when invoking a script with the
<token>-x</token> <emphasis>[verbose trace]</emphasis>
<link linkend="optionsref">option</link>. It displays as
<quote>+</quote>.</para>
<para>As a debugging aid, it may be useful to embed diagnostic
information in <varname>$PS4</varname>.
<programlisting>P4='$(read time junk &lt; /proc/$$/schedstat; echo "@@@ $time @@@ " )'
# Per suggestion by Erik Brandsberg.
set -x
# Various commands follow ...</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pwdref"/><varname>$PWD</varname></term>
<listitem>
<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>
<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><anchor id="replyref"/><varname>$REPLY</varname></term>
<listitem>
<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>
<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>
<listitem>
<indexterm>
<primary>$SECONDS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SECONDS</secondary>
</indexterm>
<indexterm>
<primary>seconds execution time</primary>
</indexterm>
<indexterm>
<primary>runtime</primary>
<secondary>seconds</secondary>
</indexterm>
<para>The number of seconds the script has been running.</para>
<para><programlisting>&seconds;</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELLOPTS</varname></term>
<listitem>
<indexterm>
<primary>$SHELLOPTS</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SHELLOPTS</secondary>
</indexterm>
<indexterm>
<primary>shell options</primary>
</indexterm>
<para>The list of enabled shell <link
linkend="optionsref">options</link>, a readonly variable.
<screen><prompt>bash$ </prompt><userinput>echo $SHELLOPTS</userinput>
<computeroutput>braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="shlvlref"/><varname>$SHLVL</varname></term>
<listitem>
<indexterm>
<primary>$SHLVL</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$SHLVL</secondary>
</indexterm>
<indexterm>
<primary>shell level</primary>
</indexterm>
<para>Shell level, how deeply Bash is nested.
<footnote><para>
Somewhat analogous to <link
linkend="recursionref">recursion</link>, in this context
<firstterm>nesting</firstterm> refers to a pattern
embedded within a larger pattern. One of the definitions
of <firstterm>nest</firstterm>, according to the 1913
edition of <emphasis>Webster's Dictionary</emphasis>,
illustrates this beautifully: <quote><emphasis>A collection of
boxes, cases, or the like, of graduated size, each put
within the one next larger.</emphasis></quote>
</para></footnote>
If, at the command-line, $SHLVL is 1, then in a script it
will increment to 2.</para>
<note><para>This variable is <link linkend = "subshnlevref">
<emphasis>not</emphasis> affected by
subshells</link>. Use <link
linkend="bashsubshellref">$BASH_SUBSHELL</link> when you
need an indication of subshell nesting.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tmoutref"/><varname>$TMOUT</varname></term>
<listitem>
<indexterm>
<primary>$TMOUT</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$TMOUT</secondary>
</indexterm>
<indexterm>
<primary>timeout interval</primary>
</indexterm>
<para>If the <replaceable>$TMOUT</replaceable>
environmental variable is set to a non-zero value
<varname>time</varname>, then the shell prompt will time out
after <varname>$time</varname> seconds. This will cause a
logout.</para>
<para>As of version 2.05b of Bash, it is now possible to use
<replaceable>$TMOUT</replaceable> in a script in combination
with <link linkend="readref">read</link>.</para>
<para>
<programlisting># Works in scripts for Bash, versions 2.05b and later.
TMOUT=3 # Prompt times out at three seconds.
echo "What is your favorite song?"
echo "Quickly now, you only have $TMOUT seconds to answer!"
read song
if [ -z "$song" ]
then
song="(no answer)"
# Default response.
fi
echo "Your favorite song is $song."</programlisting>
</para>
<para><anchor id="timingloop"/></para>
<para>There are other, more complex, ways of implementing
timed input in a script. One alternative is to set up
a timing loop to signal the script when it times out.
This also requires a signal handling routine to <link
linkend="trapref1">trap</link> (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><anchor id="sttyto"/></para>
<para>An alternative is using <link
linkend="sttyref">stty</link>.</para>
<example id="timeout">
<title>Once more, timed input</title>
<programlisting>&timeout;</programlisting>
</example>
<para>Perhaps the simplest method is using the
<option>-t</option> option to <link
linkend="readref">read</link>.</para>
<example id="tout">
<title>Timed <firstterm>read</firstterm></title>
<programlisting>&tout;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uidref"/><varname>$UID</varname></term>
<listitem>
<indexterm>
<primary>$UID</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$UID</secondary>
</indexterm>
<indexterm>
<primary>user ID</primary>
</indexterm>
<para>User ID number</para>
<para>Current user's user identification number, as
recorded in <link
linkend="datafilesref1"><filename>/etc/passwd</filename></link></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>$ENV</varname>,
<varname>$LOGNAME</varname>, <varname>$MAIL</varname>,
<varname>$TERM</varname>, <varname>$USER</varname>, and
<varname>$USERNAME</varname> are <emphasis>not</emphasis>
Bash <link linkend="builtinref">builtins</link>. These are,
however, often set as <link
linkend="envref">environmental variables</link> in
one of the <link linkend="filesref1">Bash</link> or
<firstterm>login</firstterm> startup files. <anchor
id="shellvarref"/><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>
<para>
<screen>
<prompt>tcsh% </prompt><userinput>echo $LOGNAME</userinput>
<computeroutput>bozo</computeroutput>
<prompt>tcsh% </prompt><userinput>echo $SHELL</userinput>
<computeroutput>/bin/tcsh</computeroutput>
<prompt>tcsh% </prompt><userinput>echo $TERM</userinput>
<computeroutput>rxvt</computeroutput>
<prompt>bash$ </prompt><userinput>echo $LOGNAME</userinput>
<computeroutput>bozo</computeroutput>
<prompt>bash$ </prompt><userinput>echo $SHELL</userinput>
<computeroutput>/bin/tcsh</computeroutput>
<prompt>bash$ </prompt><userinput>echo $TERM</userinput>
<computeroutput>rxvt</computeroutput>
</screen>
</para>
</note>
<!-- Nest note after $USER -->
</listitem>
</varlistentry>
</variablelist>
<!-- Last entry of intrinsic BASH variables -->
<variablelist id="posparmslist">
<title>Positional Parameters</title>
<varlistentry>
<term><anchor id="posparamref"/><varname>$0</varname>, <varname>$1</varname>,
<varname>$2</varname>, etc.</term>
<listitem>
<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>
<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><anchor id="clacountref"/><varname>$#</varname></term>
<listitem>
<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>
<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: <emphasis>a variable passed
to a script or function.</emphasis></para></footnote>
or positional parameters (see <xref linkend="ex4"/>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="appref"/><varname>$*</varname></term>
<listitem>
<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>
<para>All of the positional parameters, seen as a single word</para>
<note><para><quote><varname>$*</varname></quote> must be
quoted.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="appref2"/><varname>$@</varname></term>
<listitem>
<indexterm>
<primary>$@</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$*</secondary>
</indexterm>
<indexterm>
<primary>positional parameter</primary>
<secondary>all</secondary>
</indexterm>
<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>
<note><para>Of course, <quote><varname>$@</varname></quote>
should be quoted.</para></note>
<example id="arglist">
<title><firstterm>arglist</firstterm>: Listing arguments
with $* and $@</title>
<programlisting>&arglist;</programlisting>
</example>
<para>Following a <command>shift</command>, the
<varname>$@</varname> holds the remaining command-line
parameters, lacking the previous <varname>$1</varname>,
which was lost.
<programlisting>#!/bin/bash
# Invoke with ./scriptname 1 2 3 4 5
echo "$@" # 1 2 3 4 5
shift
echo "$@" # 2 3 4 5
shift
echo "$@" # 3 4 5
# Each "shift" loses parameter $1.
# "$@" then contains the remaining parameters.</programlisting>
</para>
<para>The <varname>$@</varname> special parameter finds
use as a tool for filtering input into shell scripts. The
<command>cat "$@"</command> construction accepts input
to a script either from <filename>stdin</filename> or
from files given as parameters to the script. See <xref
linkend="rot13"/> and <xref linkend="cryptoquote"/>.</para>
<caution><para>The <varname>$*</varname> and <varname>$@</varname>
parameters sometimes display inconsistent and
puzzling behavior, depending on the setting of <link
linkend="ifsref">$IFS</link>.</para></caution>
<example id="incompat">
<title>Inconsistent <varname>$*</varname> and <varname>$@</varname> behavior</title>
<programlisting>&incompat;</programlisting>
</example>
<note><para>The <command>$@</command> and <command>$*</command>
parameters differ only when between double quotes.</para></note>
<example id="ifsempty">
<title><varname>$*</varname> and <varname>$@</varname> when
<varname>$IFS</varname> is empty</title>
<programlisting>&ifsempty;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="otherspecparams">
<title>Other Special Parameters</title>
<varlistentry>
<term><anchor id="flpref"/><varname>$-</varname></term>
<listitem>
<indexterm>
<primary>$-</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$-</secondary>
</indexterm>
<indexterm>
<primary>flags</primary>
</indexterm>
<para>Flags passed to script (using <link
linkend="setref">set</link>). See <xref linkend="ex34"/>.</para>
<caution><para>This was originally a <firstterm>ksh</firstterm>
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><anchor id="pidvarref"/><varname>$!</varname></term>
<listitem>
<indexterm>
<primary>$!</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$!</secondary>
</indexterm>
<indexterm>
<primary>PID</primary>
<secondary>last job background</secondary>
</indexterm>
<para><link linkend="processiddef">PID</link> (process ID) of last
job run in background</para>
<para>
<programlisting>LOG=$0.log
COMMAND1="sleep 100"
echo "Logging PIDs background commands for script: $0" >> "$LOG"
# So they can be monitored, and killed as necessary.
echo >> "$LOG"
# Logging commands.
echo -n "PID of \"$COMMAND1\": " >> "$LOG"
${COMMAND1} &amp;
echo $! >> "$LOG"
# PID of "sleep 100": 1506
# Thank you, Jacques Lederer, for suggesting this.</programlisting>
</para>
<para>Using <varname>$!</varname> for job control:</para>
<para>
<programlisting>possibly_hanging_job &amp; { sleep ${TIMEOUT}; eval 'kill -9 $!' &amp;> /dev/null; }
# Forces completion of an ill-behaved program.
# Useful, for example, in init scripts.
# Thank you, Sylvain Fourmanoit, for this creative use of the "!" variable.</programlisting>
</para>
<para>Or, alternately:</para>
<para>
<programlisting># This example by Matthew Sage.
# Used with permission.
TIMEOUT=30 # Timeout value in seconds
count=0
possibly_hanging_job &amp; {
while ((count &lt; TIMEOUT )); do
eval '[ ! -d "/proc/$!" ] &amp;&amp; ((count = TIMEOUT))'
# /proc is where information about running processes is found.
# "-d" tests whether it exists (whether directory exists).
# So, we're waiting for the job in question to show up.
((count++))
sleep 1
done
eval '[ -d "/proc/$!" ] &amp;&amp; kill -15 $!'
# If the hanging job is running, kill it.
}
# -------------------------------------------------------------- #
# However, this may not work as specified if another process
#+ begins to run after the "hanging_job" . . .
# In such a case, the wrong job may be killed.
# Ariel Meragelman suggests the following fix.
TIMEOUT=30
count=0
# Timeout value in seconds
possibly_hanging_job &amp; {
while ((count &lt; TIMEOUT )); do
eval '[ ! -d "/proc/$lastjob" ] &amp;&amp; ((count = TIMEOUT))'
lastjob=$!
((count++))
sleep 1
done
eval '[ -d "/proc/$lastjob" ] &amp;&amp; kill -15 $lastjob'
}
exit</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="underscoreref"/><varname>$_</varname></term>
<listitem>
<indexterm>
<primary>$_</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$_</secondary>
</indexterm>
<indexterm>
<primary>underscore</primary>
<secondary>last argument</secondary>
</indexterm>
<para>Special variable set to final argument of previous command
executed.</para>
<example id="uscref">
<title>Underscore variable</title>
<programlisting>#!/bin/bash
echo $_ # /bin/bash
# Just called /bin/bash to run the script.
# Note that this will vary according to
#+ how the script is invoked.
du >/dev/null # So no output from command.
echo $_ # du
ls -al >/dev/null # So no output from command.
echo $_ # -al (last argument)
:
echo $_ # :</programlisting></example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xstatvarref"/><varname>$?</varname></term>
<listitem>
<indexterm>
<primary>$?</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$?</secondary>
</indexterm>
<indexterm>
<primary>exit status</primary>
</indexterm>
<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="proccid"/><varname>$$</varname></term>
<listitem>
<indexterm>
<primary>$$</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$$</secondary>
</indexterm>
<indexterm>
<primary>PID</primary>
<secondary>of script</secondary>
</indexterm>
<para>Process ID (<firstterm>PID</firstterm>) of
the script itself.
<footnote><para>Within a script, inside a subshell,
<varname>$$</varname> <link linkend="bashpidref">returns
the PID of the script</link>, not the
subshell.</para></footnote>
The <varname>$$</varname> variable often
finds use in scripts to construct <quote>unique</quote>
temp file names (see <xref linkend="online"/>, <xref
linkend="derpm"/>, and <xref linkend="selfdestruct"/>).
This is usually simpler than invoking <link
linkend="mktempref">mktemp</link>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Internal Variables -->
<sect1 id="declareref">
<title>Typing variables: <command>declare</command> or
<command>typeset</command></title>
<indexterm>
<primary>declare</primary>
</indexterm>
<indexterm>
<primary>typeset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>declare</secondary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>typeset</secondary>
</indexterm>
<para><anchor id="declare1ref"/></para>
<para>The <firstterm>declare</firstterm> or
<firstterm>typeset</firstterm> <link
linkend="builtinref">builtins</link>, which are exact synonyms,
permit modifying the properties of variables. This is
a very weak form of the <firstterm>typing</firstterm>
<footnote>
<para><anchor id="typingref"/>In this context,
<firstterm>typing</firstterm>
a variable means to classify it and restrict its properties.
For example, a variable <firstterm>declared</firstterm>
or <firstterm>typed</firstterm> as an integer
is no longer available for <link linkend="stringopstab">string
operations</link>.</para>
<para><programlisting>declare -i intvar
intvar=23
echo "$intvar" # 23
intvar=stringval
echo "$intvar" # 0</programlisting></para>
</footnote>
available in certain programming languages. The
<firstterm>declare</firstterm> command is specific to version
2 or later of Bash. The <firstterm>typeset</firstterm> command
also works in ksh scripts.</para>
<variablelist id="declareopsref">
<title><anchor id="declareopsref1"/>declare/typeset options</title>
<varlistentry>
<term><token>-r</token>
<replaceable>readonly</replaceable></term>
<listitem>
<para>(<userinput>declare -r var1</userinput> works the same as
<userinput>readonly var1</userinput>)</para>
<para>This is the rough equivalent of the <command>C</command>
<firstterm>const</firstterm> type qualifier. An attempt
to change the value of a <firstterm>readonly</firstterm>
variable fails with an error message.</para>
<para><programlisting>declare -r var1=1
echo "var1 = $var1" # var1 = 1
(( var1++ )) # x.sh: line 4: var1: readonly variable</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-i</token> <replaceable>integer</replaceable></term>
<listitem>
<para><programlisting>declare -i number
# The script will treat subsequent occurrences of "number" as an integer.
number=3
echo "Number = $number" # Number = 3
number=three
echo "Number = $number" # Number = 0
# Tries to evaluate the string "three" as an integer.</programlisting></para>
<para>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>
<para><programlisting>n=6/3
echo "n = $n" # n = 6/3
declare -i n
n=6/3
echo "n = $n" # n = 2</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="arraydeclare"/><token>-a</token>
<replaceable>array</replaceable></term>
<listitem><para><programlisting>declare -a indices</programlisting></para>
<para>The variable <parameter>indices</parameter> will be treated as
an <link linkend="arrayref">array</link>.</para></listitem>
</varlistentry>
<varlistentry>
<term><token>-f</token> <replaceable>function(s)</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
<link linkend="functionref">functions</link> previously
defined in that script.</para>
<para><programlisting>declare -f function_name</programlisting></para>
<para>A <userinput>declare -f function_name</userinput>
in a script lists just the function named.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>-x</token> <link linkend="exportref">export</link></term>
<listitem><para><programlisting>declare -x var3</programlisting></para>
<para>This declares a variable as available for exporting outside the
environment of the script itself.</para></listitem>
</varlistentry>
<varlistentry>
<term>-x var=$value</term>
<listitem>
<para><programlisting>declare -x var3=373</programlisting></para>
<para>The <command>declare</command> command permits
assigning a value to a variable in the same statement
as setting its properties.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex20">
<title>Using <firstterm>declare</firstterm> to type variables</title>
<programlisting>&ex20;</programlisting>
</example>
<caution>
<para>Using the <firstterm>declare</firstterm> builtin
restricts the <link linkend="scoperef">scope</link>
of a variable.
<programlisting>foo ()
{
FOO="bar"
}
bar ()
{
foo
echo $FOO
}
bar # Prints bar.</programlisting></para>
<para>However . . .
<programlisting>foo (){
declare FOO="bar"
}
bar ()
{
foo
echo $FOO
}
bar # Prints nothing.
# Thank you, Michael Iatrou, for pointing this out.</programlisting></para>
</caution>
<sect2 id="declare2x">
<title>Another use for <firstterm>declare</firstterm></title>
<para>The <firstterm>declare</firstterm> command can be
helpful in identifying variables, <link
linkend="envref">environmental</link> or otherwise.
This can be especially useful with <link
linkend="arrayref">arrays</link>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>declare | grep HOME</userinput>
<computeroutput>HOME=/home/bozo</computeroutput>
<prompt>bash$ </prompt><userinput>zzy=68</userinput>
<prompt>bash$ </prompt><userinput>declare | grep zzy</userinput>
<computeroutput>zzy=68</computeroutput>
<prompt>bash$ </prompt><userinput>Colors=([0]="purple" [1]="reddish-orange" [2]="light green")</userinput>
<prompt>bash$ </prompt><userinput>echo ${Colors[@]}</userinput>
<computeroutput>purple reddish-orange light green</computeroutput>
<prompt>bash$ </prompt><userinput>declare | grep Colors</userinput>
<computeroutput>Colors=([0]="purple" [1]="reddish-orange" [2]="light green")</computeroutput>
</screen>
</para>
</sect2> <!-- Another use for declare -->
</sect1> <!-- Typing variables: declare or typeset -->
<sect1 id="randomvar">
<title>$RANDOM: generate random integer</title>
<indexterm>
<primary>$RANDOM</primary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>$RANDOM</secondary>
</indexterm>
<epigraph>
<para>Anyone who attempts to generate random numbers by
deterministic means is, of course, living in a state of
sin.</para>
<para>--John von Neumann</para>
</epigraph>
<para><anchor id="randomvar01"/></para>
<para><varname>$RANDOM</varname> is an internal Bash <link
linkend="functionref">function</link> (not a constant) that
returns a <firstterm>pseudorandom</firstterm>
<footnote><para>True <quote>randomness,</quote> insofar as
it exists at all, can only be found in certain incompletely
understood natural phenomena, such as radioactive
decay. Computers only <firstterm>simulate</firstterm>
randomness, and computer-generated sequences of
<quote>random</quote> numbers are therefore referred to as
<firstterm>pseudorandom</firstterm>.</para></footnote>
integer in the range 0 - 32767. It should
<replaceable>not</replaceable> be used to generate an encryption
key.</para>
<example id="ex21">
<title>Generating random numbers</title>
<programlisting>&ex21;</programlisting>
</example>
<example id="pickcard">
<title>Picking a random card from a deck</title>
<programlisting>&pickcard;</programlisting>
</example>
<para><anchor id="brownianref"/></para>
<example id="brownian">
<title>Brownian Motion Simulation</title>
<programlisting>&brownian;</programlisting>
</example>
<para>
<emphasis>Jipe</emphasis> points out a set of techniques for
generating random numbers within a range.
<programlisting># Generate random number between 6 and 30.
rnumber=$((RANDOM%25+6))
# Generate random number in the same 6 - 30 range,
#+ but the number must be evenly divisible by 3.
rnumber=$(((RANDOM%30/3+1)*3))
# Note that this will not work all the time.
# It fails if $RANDOM%30 returns 0.
# Frank Wang suggests the following alternative:
rnumber=$(( RANDOM%27/3*3+6 ))</programlisting>
</para>
<para>
<emphasis>Bill Gradwohl</emphasis> came up with an improved
formula that works for positive numbers.
<programlisting>rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))</programlisting>
</para>
<para>Here Bill presents a versatile function that returns
a random number between two specified values.</para>
<example id="randombetween">
<title>Random between values</title>
<programlisting>&randombetween;</programlisting>
</example>
<para>Just how random is <varname>$RANDOM</varname>? The best
way to test this is to write a script that tracks
the distribution of <quote>random</quote> numbers
generated by <varname>$RANDOM</varname>. Let's roll a
<varname>$RANDOM</varname> die a few times . . .</para>
<example id="randomtest">
<title>Rolling a single die with RANDOM</title>
<programlisting>&randomtest;</programlisting>
</example>
<para>As we have seen in the last example, it is best to
<firstterm>reseed</firstterm> the <parameter>RANDOM</parameter>
generator each time it is invoked. Using the same seed
for <parameter>RANDOM</parameter> repeats the same series
of numbers.
<footnote>
<para>The <firstterm>seed</firstterm> of a
computer-generated pseudorandom number series
can be considered an identification label. For
example, think of the pseudorandom series with a
seed of <emphasis>23</emphasis> as <replaceable>Series
#23</replaceable>.</para>
<para>A property of a pseurandom number series is the length of
the cycle before it starts repeating itself. A good pseurandom
generator will produce series with very long cycles.</para>
</footnote>
(This mirrors the behavior of the
<replaceable>random()</replaceable> function in
<firstterm>C</firstterm>.)</para>
<example id="seedingrandom">
<title>Reseeding RANDOM</title>
<programlisting>&seedingrandom;</programlisting>
</example>
<para><anchor id="urandomref"/></para>
<note>
<para>The <filename>/dev/urandom</filename> pseudo-device file
provides a method 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, <xref linkend="rnd"/>, and
<xref linkend="insertionsort"/>), or even piping to
<link linkend="md5sumref">md5sum</link> (see <xref
linkend="horserace"/>).</para>
<para><anchor id="awkrandomref"/></para>
<para>There are also other ways to generate pseudorandom
numbers in a script. <command>Awk</command> provides a
convenient means of doing this.</para>
<example id="random2">
<title>Pseudorandom numbers, using <link
linkend="awkref">awk</link></title>
<programlisting>&random2;</programlisting>
</example>
<para>The <link linkend="dateref">date</link> command also lends
itself to <link linkend="daterandref">generating pseudorandom
integer sequences</link>.</para>
</note>
</sect1> <!-- RANDOM: generate random integer -->
</chapter> <!-- Variables Revisited -->
<chapter id="manipulatingvars">
<title>Manipulating Variables</title>
<sect1 id="String-Manipulation">
<title>Manipulating Strings</title>
<para><anchor id="stringmanip"/></para>
<para>Bash supports a surprising number of string manipulation
operations. Unfortunately, these tools lack
a unified focus. Some are a subset of <link
linkend="paramsubref">parameter substitution</link>, and
others fall under the functionality of the UNIX <link
linkend="exprref">expr</link> command. This results in
inconsistent command syntax and overlap of functionality,
not to mention confusion.</para>
<variablelist id="stringlength">
<title>String Length</title>
<varlistentry>
<term>${#string}</term>
<listitem>
<indexterm>
<primary>string length</primary>
<secondary>parameter substitution</secondary>
</indexterm>
<para></para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr length $string</term>
<listitem>
<indexterm>
<primary>string length</primary>
<secondary>expr</secondary>
</indexterm>
<para><anchor id="strlen"/>These are the equivalent of
<firstterm>strlen()</firstterm> in
<firstterm>C</firstterm>.</para></listitem>
</varlistentry>
<varlistentry>
<term>expr "$string" : '.*'</term>
<listitem>
<indexterm>
<primary>string length</primary>
<secondary>expr</secondary>
</indexterm>
<para>
<programlisting>stringZ=abcABC123ABCabc
echo ${#stringZ} # 15
echo `expr length $stringZ` # 15
echo `expr "$stringZ" : '.*'` # 15</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<example id="paragraphspace">
<title>Inserting a blank line between paragraphs in a text file</title>
<programlisting>&paragraphspace;</programlisting>
</example>
<variablelist id="lengthsubstring">
<title>Length of Matching Substring at Beginning of String</title>
<varlistentry>
<term><anchor id="exprmatch"/>expr match "$string"
'$substring'</term>
<listitem>
<indexterm>
<primary>substring length</primary>
<secondary>expr</secondary>
</indexterm>
<para><replaceable>$substring</replaceable> is a <link
linkend="regexref">regular expression</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr "$string" : '$substring'</term>
<listitem>
<indexterm>
<primary>substring length</primary>
<secondary>expr</secondary>
</indexterm>
<para><replaceable>$substring</replaceable> is a regular
expression.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# |------|
# 12345678
echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="substringindex">
<title>Index</title>
<varlistentry>
<term><anchor id="substringindex2"/>expr index $string
$substring</term>
<listitem>
<indexterm>
<primary>substring index</primary>
<secondary>expr</secondary>
</indexterm>
<para>Numerical position in $string of first character in
$substring that matches.</para>
<para><programlisting>stringZ=abcABC123ABCabc
# 123456 ...
echo `expr index "$stringZ" C12` # 6
# C position.
echo `expr index "$stringZ" 1c` # 3
# 'c' (in #3 position) matches before '1'.</programlisting></para>
<para>This is the near equivalent of
<firstterm>strchr()</firstterm> in
<firstterm>C</firstterm>.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="substringextraction">
<title>Substring Extraction</title>
<varlistentry>
<term><anchor id="substrextr01"/>${string:position}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>extraction</secondary>
</indexterm>
<para>Extracts substring from <replaceable>$string</replaceable> at
<replaceable>$position</replaceable>.</para>
<para>If the <varname>$string</varname> parameter is
<quote><token>*</token></quote>
or <quote><token>@</token></quote>, then this extracts the
<link linkend="posparamref">positional parameters</link>,
<footnote><para>This applies to either command-line
arguments or parameters passed to a <link
linkend="functionref">function</link>.</para></footnote>
starting at <varname>$position</varname>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="substrextr02"/>${string:position:length}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>extraction</secondary>
</indexterm>
<para>Extracts <replaceable>$length</replaceable> characters
of substring from <replaceable>$string</replaceable> at
<replaceable>$position</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# 0123456789.....
# 0-based indexing.
echo ${stringZ:0} # abcABC123ABCabc
echo ${stringZ:1} # bcABC123ABCabc
echo ${stringZ:7} # 23ABCabc
echo ${stringZ:7:3} # 23A
# Three characters of substring.
# Is it possible to index from the right end of the string?
echo ${stringZ:-4} # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . .
echo ${stringZ:(-4)} # Cabc
echo ${stringZ: -4} # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.
# Thank you, Dan Jacobson, for pointing this out.</programlisting>
</para>
<para>The <firstterm>position</firstterm> and
<firstterm>length</firstterm> arguments can be
<quote>parameterized,</quote> that is, represented as a
variable, rather than as a numerical constant.</para>
<para><anchor id="randstring0"/></para>
<example id="randstring">
<title>Generating an 8-character <quote>random</quote>
string</title>
<programlisting>&randstring;</programlisting>
</example>
<para><anchor id="substrextrp"/></para>
<para>If the <varname>$string</varname> parameter is
<quote><token>*</token></quote> or
<quote><token>@</token></quote>, then this extracts a maximum
of <varname>$length</varname> positional parameters, starting
at <varname>$position</varname>.</para>
<para>
<programlisting>echo ${*:2} # Echoes second and following positional parameters.
echo ${@:2} # Same as above.
echo ${*:2:3} # Echoes three positional parameters, starting at second.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr substr $string $position $length</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>extraction expr</secondary>
</indexterm>
<para>Extracts <replaceable>$length</replaceable> characters
from <replaceable>$string</replaceable> starting at
<replaceable>$position</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# 123456789......
# 1-based indexing.
echo `expr substr $stringZ 1 2` # ab
echo `expr substr $stringZ 4 3` # ABC</programlisting>
</para>
<para><anchor id="exprparen"/></para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr match "$string" '\($substring\)'</term>
<listitem>
<indexterm>
<primary>substring extraction</primary>
<secondary>expr</secondary>
</indexterm>
<para>Extracts <replaceable>$substring</replaceable>
at beginning of <replaceable>$string</replaceable>,
where <replaceable>$substring</replaceable> is a <link
linkend="regexref">regular expression</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr "$string" : '\($substring\)'</term>
<listitem>
<indexterm>
<primary>substring extraction</primary>
<secondary>expr</secondary>
</indexterm>
<para>Extracts <replaceable>$substring</replaceable>
at beginning of <replaceable>$string</replaceable>,
where <replaceable>$substring</replaceable> is a regular
expression.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# =======
echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
echo `expr "$stringZ" : '\(.......\)'` # abcABC1
# All of the above forms give an identical result.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr match "$string" '.*\($substring\)'</term>
<listitem>
<indexterm>
<primary>substring extraction</primary>
<secondary>expr</secondary>
</indexterm>
<para>Extracts <replaceable>$substring</replaceable>
at <emphasis>end</emphasis> of
<replaceable>$string</replaceable>, where
<replaceable>$substring</replaceable> is a regular
expression.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>expr "$string" : '.*\($substring\)'</term>
<listitem>
<indexterm>
<primary>substring extraction</primary>
<secondary>expr</secondary>
</indexterm>
<para>Extracts <replaceable>$substring</replaceable>
at <emphasis>end</emphasis> of <replaceable>$string</replaceable>,
where <replaceable>$substring</replaceable> is a regular
expression.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# ======
echo `expr match "$stringZ" '.*\([A-C][A-C][A-C][a-c]*\)'` # ABCabc
echo `expr "$stringZ" : '.*\(......\)'` # ABCabc</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="substringremoval">
<title>Substring Removal</title>
<varlistentry>
<term>${string#substring}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>removal</secondary>
</indexterm>
<para>Deletes shortest match of
<replaceable>$substring</replaceable> from
<emphasis>front</emphasis> of
<replaceable>$string</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>${string##substring}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>removal</secondary>
</indexterm>
<para>Deletes longest match of
<replaceable>$substring</replaceable> from
<emphasis>front</emphasis> of
<replaceable>$string</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# |----| shortest
# |----------| longest
echo ${stringZ#a*C} # 123ABCabc
# Strip out shortest match between 'a' and 'C'.
echo ${stringZ##a*C} # abc
# Strip out longest match between 'a' and 'C'.
# You can parameterize the substrings.
X='a*C'
echo ${stringZ#$X} # 123ABCabc
echo ${stringZ##$X} # abc
# As above.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>${string%substring}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>removal</secondary>
</indexterm>
<para>Deletes shortest match of
<replaceable>$substring</replaceable> from
<emphasis>back</emphasis> of
<replaceable>$string</replaceable>.</para>
<para>For example:
<programlisting># Rename all filenames in $PWD with "TXT" suffix to a "txt" suffix.
# For example, "file1.TXT" becomes "file1.txt" . . .
SUFF=TXT
suff=txt
for i in $(ls *.$SUFF)
do
mv -f $i ${i%.$SUFF}.$suff
# Leave unchanged everything *except* the shortest pattern match
#+ starting from the right-hand-side of the variable $i . . .
done ### This could be condensed into a "one-liner" if desired.
# Thank you, Rory Winston.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>${string%%substring}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>removal</secondary>
</indexterm>
<para>Deletes longest match of
<replaceable>$substring</replaceable> from
<emphasis>back</emphasis> of
<replaceable>$string</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
# || shortest
# |------------| longest
echo ${stringZ%b*c} # abcABC123ABCa
# Strip out shortest match between 'b' and 'c', from back of $stringZ.
echo ${stringZ%%b*c} # a
# Strip out longest match between 'b' and 'c', from back of $stringZ.</programlisting>
</para>
<para>This operator is useful for generating filenames.</para>
<example id="cvt">
<title>Converting graphic file formats, with filename change</title>
<programlisting>&cvt;</programlisting>
</example>
<example id="ra2ogg">
<title>Converting streaming audio files to
<firstterm>ogg</firstterm></title>
<programlisting>&ra2ogg;</programlisting>
</example>
<para><anchor id="getoptsimple1"/></para>
<para>A simple emulation of <link linkend="getopty">getopt</link>
using substring-extraction constructs.</para>
<example id="getoptsimple">
<title>Emulating <firstterm>getopt</firstterm></title>
<programlisting>&getoptsimple;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="substringreplacement">
<title>Substring Replacement</title>
<varlistentry>
<term><anchor
id="substrrepl00"/>${string/substring/replacement}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>replacement</secondary>
</indexterm>
<para>
Replace first <firstterm>match</firstterm> of
<replaceable>$substring</replaceable> with
<replaceable>$replacement</replaceable>.
<footnote><para>Note that
<replaceable>$substring</replaceable> and
<replaceable>$replacement</replaceable> may refer to
either <firstterm>literal strings</firstterm> or
<firstterm>variables</firstterm>, depending on
context. See the first usage example.</para></footnote>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="substrrepl01"/>${string//substring/replacement}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>replacement</secondary>
</indexterm>
<para>Replace all matches of
<replaceable>$substring</replaceable> with
<replaceable>$replacement</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
echo ${stringZ/abc/xyz} # xyzABC123ABCabc
# Replaces first match of 'abc' with 'xyz'.
echo ${stringZ//abc/xyz} # xyzABC123ABCxyz
# Replaces all matches of 'abc' with # 'xyz'.
echo ---------------
echo "$stringZ" # abcABC123ABCabc
echo ---------------
# The string itself is not altered!
# Can the match and replacement strings be parameterized?
match=abc
repl=000
echo ${stringZ/$match/$repl} # 000ABC123ABCabc
# ^ ^ ^^^
echo ${stringZ//$match/$repl} # 000ABC123ABC000
# Yes! ^ ^ ^^^ ^^^
echo
# What happens if no $replacement string is supplied?
echo ${stringZ/abc} # ABC123ABCabc
echo ${stringZ//abc} # ABC123ABC
# A simple deletion takes place.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="substrrepl02"/>${string/#substring/replacement}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>replacement</secondary>
</indexterm>
<para>If <replaceable>$substring</replaceable> matches
<emphasis>front</emphasis> end of
<replaceable>$string</replaceable>, substitute
<replaceable>$replacement</replaceable> for
<replaceable>$substring</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="substrrepl03"/>${string/%substring/replacement}</term>
<listitem>
<indexterm>
<primary>substring</primary>
<secondary>replacement</secondary>
</indexterm>
<para>If <replaceable>$substring</replaceable> matches
<emphasis>back</emphasis> end of
<replaceable>$string</replaceable>, substitute
<replaceable>$replacement</replaceable> for
<replaceable>$substring</replaceable>.</para>
<para>
<programlisting>stringZ=abcABC123ABCabc
echo ${stringZ/#abc/XYZ} # XYZABC123ABCabc
# Replaces front-end match of 'abc' with 'XYZ'.
echo ${stringZ/%abc/XYZ} # abcABC123ABCXYZ
# Replaces back-end match of 'abc' with 'XYZ'.</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<sect2 id="awkstringmanip">
<title>Manipulating strings using awk</title>
<para><anchor id="awkstringmanip2"/></para>
<para>A Bash script may invoke the string manipulation facilities of
<link linkend="awkref">awk</link> as an alternative to using its
built-in operations.</para>
<example id="substringex">
<title>Alternate ways of extracting and locating substrings</title>
<programlisting>&substringex;</programlisting>
</example>
</sect2> <!-- Manipulating strings using awk -->
<sect2 id="strfdisc">
<title>Further Reference</title>
<para>For more on string manipulation in scripts, refer to <xref
linkend="Parameter-Substitution"/> and the
<link linkend="expextrsub">relevant section</link> of the <link
linkend="exprref">expr</link> command listing.</para>
<para>Script examples:
<orderedlist>
<listitem><para><xref linkend="ex45"/></para></listitem>
<listitem><para><xref linkend="length"/></para></listitem>
<listitem><para><xref linkend="pattmatching"/></para></listitem>
<listitem><para><xref linkend="rfe"/></para></listitem>
<listitem><para><xref linkend="varmatch"/></para></listitem>
<listitem><para><xref linkend="insertionsort"/></para></listitem>
<listitem><para><xref linkend="qky"/></para></listitem>
</orderedlist>
</para>
</sect2> <!-- Further Reference-->
</sect1> <!-- Manipulating Strings -->
<sect1 id="Parameter-Substitution">
<title>Parameter Substitution</title>
<para><anchor id="paramsubref"/></para>
<variablelist id="pssub">
<title><anchor id="pssub1"/>Manipulating and/or expanding variables</title>
<varlistentry>
<term>
<userinput>${parameter}</userinput></term>
<listitem>
<para>Same as <replaceable>$parameter</replaceable>, i.e.,
value of the variable
<replaceable>parameter</replaceable>.
In certain contexts, only the less ambiguous
<replaceable>${parameter}</replaceable> form
works.</para>
<para>May be used for concatenating variables with strings.</para>
<para><programlisting>
your_id=${USER}-on-${HOSTNAME}
echo "$your_id"
#
echo "Old \$PATH = $PATH"
PATH=${PATH}:/opt/bin # Add /opt/bin to $PATH for duration of script.
echo "New \$PATH = $PATH"
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="defparam1"/><userinput>${parameter-default}</userinput></term>
<term><userinput>${parameter:-default}</userinput></term>
<listitem>
<para>If parameter not set, use default.</para>
<para><programlisting>var1=1
var2=2
# var3 is unset.
echo ${var1-$var2} # 1
echo ${var3-$var2} # 2
# ^ Note the $ prefix.
echo ${username-`whoami`}
# Echoes the result of `whoami`, if variable $username is still unset.</programlisting></para>
<note><para><anchor
id="unddr"/><replaceable>${parameter-default}</replaceable>
and <replaceable>${parameter:-default}</replaceable>
are almost equivalent. The extra <token>:</token> makes
a difference only when <parameter>parameter</parameter>
has been declared, but is null. </para></note>
<para><programlisting>&paramsub;</programlisting></para>
<para>The <firstterm>default parameter</firstterm> construct
finds use in providing <quote>missing</quote> command-line
arguments in scripts.</para>
<para>
<programlisting>DEFAULT_FILENAME=generic.data
filename=${1:-$DEFAULT_FILENAME}
# If not otherwise specified, the following command block operates
#+ on the file "generic.data".
# Begin-Command-Block
# ...
# ...
# ...
# End-Command-Block
# From "hanoi2.bash" example:
DISKS=${1:-E_NOPARAM} # Must specify how many disks.
# Set $DISKS to $1 command-line-parameter,
#+ or to $E_NOPARAM if that is unset.</programlisting>
</para>
<para>See also <xref linkend="ex58"/>, <xref
linkend="ex73"/>, and <xref linkend="collatz"/>.</para>
<para>Compare this method with <link
linkend="anddefault">using an <firstterm>and
list</firstterm> to supply a default command-line
argument</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${parameter=default}</userinput></term>
<term><userinput>${parameter:=default}</userinput></term>
<listitem>
<para><anchor id="defparam"/></para>
<para>If parameter not set, set it to
<firstterm>default</firstterm>.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when <varname>$parameter</varname>
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 for
<quote>command not found</quote>).</para>
</footnote>
as above.
</para>
<para><programlisting>echo ${var=abc} # abc
echo ${var=xyz} # abc
# $var had already been set to abc, so it did not change.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="paramaltv"/><userinput>${parameter+alt_value}</userinput></term>
<term><userinput>${parameter:+alt_value}</userinput></term>
<listitem>
<para>If parameter set, use
<userinput>alt_value</userinput>, else use null
string.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when
<parameter>parameter</parameter>
has been declared and is null, see below.</para>
<para><programlisting>echo "###### \${parameter+alt_value} ########"
echo
a=${param1+xyz}
echo "a = $a" # a =
param2=
a=${param2+xyz}
echo "a = $a" # a = xyz
param3=123
a=${param3+xyz}
echo "a = $a" # a = xyz
echo
echo "###### \${parameter:+alt_value} ########"
echo
a=${param4:+xyz}
echo "a = $a" # a =
param5=
a=${param5:+xyz}
echo "a = $a" # a =
# Different result from a=${param5+xyz}
param6=123
a=${param6:+xyz}
echo "a = $a" # a = xyz</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="qerrmsg"/><userinput>${parameter?err_msg}</userinput></term>
<term><userinput>${parameter:?err_msg}</userinput></term>
<listitem><para>If parameter set, use it, else print
<firstterm>err_msg</firstterm> and <emphasis>abort
the script</emphasis> with an <link
linkend="exitstatusref">exit status</link> of
<errorcode>1</errorcode>.</para>
<para>Both forms nearly equivalent. The <token>:</token>
makes a difference only when <parameter>parameter</parameter>
has been declared and is null, as above.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex6">
<title>Using parameter substitution and error messages</title>
<programlisting>&ex6;</programlisting>
</example>
<example id="usagemessage">
<title>Parameter substitution and <quote>usage</quote> messages</title>
<programlisting>&usagemessage;</programlisting>
</example>
<formalpara><title>Parameter substitution and/or expansion</title>
<para><anchor id="psub2"/>The following expressions are
the complement to the <command>match</command>
<replaceable>in</replaceable> <command>expr</command>
string operations (see <xref linkend="ex45"/>).
These particular ones are used mostly in parsing file
path names.</para></formalpara>
<variablelist id="psorex">
<title><anchor id="psorex1"/>Variable length / Substring removal</title>
<varlistentry>
<term><userinput>${#var}</userinput></term>
<listitem>
<para><userinput>String length</userinput> (number
of characters in <varname>$var</varname>). For
an <link linkend="arrayref">array</link>,
<command>${#array}</command> is the length of the
first element in the array.</para>
<note><para>
Exceptions:
<itemizedlist>
<listitem>
<para><anchor id="numposparam"/></para>
<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><anchor
id="psorex2"/><userinput>${var#Pattern}</userinput></term>
<term><userinput>${var##Pattern}</userinput></term>
<listitem>
<para><anchor id="psorexsh"/></para>
<para><command>${var#Pattern} </command>
Remove from <varname>$var</varname>
the <emphasis>shortest</emphasis> part of
<varname>$Pattern</varname> that matches
the <replaceable>front end</replaceable> of
<varname>$var</varname>.
</para>
<para><anchor id="psorexlo"/></para>
<para><command>${var##Pattern} </command>
Remove from <varname>$var</varname>
the <emphasis>longest</emphasis> part of
<varname>$Pattern</varname> that matches
the <replaceable>front end</replaceable> of
<varname>$var</varname>.
</para>
<para>A usage illustration from <xref
linkend="daysbetween"/>:
<programlisting># Function from "days-between.sh" example.
# Strips leading zero(s) from argument passed.
strip_leading_zero () # Strip possible leading zero(s)
{ #+ from argument passed.
return=${1#0} # The "1" refers to "$1" -- passed arg.
} # The "0" is what to remove from "$1" -- strips zeros.</programlisting>
</para>
<para>Manfred Schwarb's more elaborate variation of the
above:</para>
<para>
<programlisting>strip_leading_zero2 () # Strip possible leading zero(s), since otherwise
{ # Bash will interpret such numbers as octal values.
shopt -s extglob # Turn on extended globbing.
local val=${1##+(0)} # Use local variable, longest matching series of 0's.
shopt -u extglob # Turn off extended globbing.
_strip_leading_zero2=${val:-0}
# If input was 0, return 0 instead of "".
}</programlisting>
</para>
<para>Another usage illustration:</para>
<para>
<programlisting>echo `basename $PWD` # Basename of current working directory.
echo "${PWD##*/}" # Basename of current working directory.
echo
echo `basename $0` # Name of script.
echo $0 # Name of script.
echo "${0##*/}" # Name of script.
echo
filename=test.data
echo "${filename##*.}" # data
# Extension of filename.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pctpatref"/><userinput>${var%Pattern}</userinput></term>
<term><userinput>${var%%Pattern}</userinput></term>
<listitem>
<para><anchor id="pctrep1"/></para>
<para><command>${var%Pattern}</command>
Remove from <varname>$var</varname>
the <emphasis>shortest</emphasis> part of
<varname>$Pattern</varname> that matches
the <replaceable>back end</replaceable> of
<varname>$var</varname>. </para>
<para><anchor id="pctrep2"/></para>
<para><command>${var%%Pattern}</command>
Remove from <varname>$var</varname>
the <emphasis>longest</emphasis> part of
<varname>$Pattern</varname> that matches
the <replaceable>back end</replaceable> of
<varname>$var</varname>. </para>
</listitem>
</varlistentry>
</variablelist>
<para><link linkend="bash2ref">Version 2</link> of Bash added
additional options.</para>
<example id="pattmatching">
<title>Pattern matching in parameter substitution</title>
<programlisting>&pattmatching;</programlisting>
</example>
<example id="rfe">
<title>Renaming file extensions<token>:</token></title>
<programlisting>&rfe;</programlisting>
</example>
<variablelist id="exprepl">
<title><anchor id="exprepl1"/>Variable expansion / Substring
replacement</title>
<varlistentry>
<term></term>
<listitem>
<para>These constructs have been adopted from
<firstterm>ksh</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var:pos}</userinput></term>
<listitem>
<para>Variable <replaceable>var</replaceable> expanded,
starting from offset <replaceable>pos</replaceable>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var:pos:len}</userinput></term>
<listitem>
<para>Expansion to a max of <replaceable>len</replaceable>
characters of variable <replaceable>var</replaceable>, from offset
<replaceable>pos</replaceable>. See <xref linkend="pw"/>
for an example of the creative use of this operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/Pattern/Replacement}</userinput></term>
<listitem>
<para>First match of <replaceable>Pattern</replaceable>,
within <replaceable>var</replaceable> replaced with
<replaceable>Replacement</replaceable>.</para>
<para>If <replaceable>Replacement</replaceable> is
omitted, then the first match of
<replaceable>Pattern</replaceable> is replaced by
<emphasis>nothing</emphasis>, that is, deleted.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var//Pattern/Replacement}</userinput></term>
<listitem>
<formalpara><title>Global replacement</title>
<para><anchor id="psglob"/>
All matches of <replaceable>Pattern</replaceable>,
within <replaceable>var</replaceable> replaced with
<replaceable>Replacement</replaceable>.</para>
</formalpara>
<para>As above, if <replaceable>Replacement</replaceable>
is omitted, then all occurrences of
<replaceable>Pattern</replaceable> are replaced by
<emphasis>nothing</emphasis>, that is, deleted.</para>
<example id="ex7">
<title>Using pattern matching to parse arbitrary strings</title>
<programlisting>&ex7;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/#Pattern/Replacement}</userinput></term>
<listitem>
<para>If <firstterm>prefix</firstterm> of
<replaceable>var</replaceable> matches
<replaceable>Pattern</replaceable>, then substitute
<replaceable>Replacement</replaceable> for
<replaceable>Pattern</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>${var/%Pattern/Replacement}</userinput></term>
<listitem>
<para>If <firstterm>suffix</firstterm> of
<replaceable>var</replaceable> matches
<replaceable>Pattern</replaceable>, then substitute
<replaceable>Replacement</replaceable> for
<replaceable>Pattern</replaceable>.</para>
<example id="varmatch">
<title>Matching patterns at prefix or suffix of string</title>
<programlisting>&varmatch;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="varprefixm"/><userinput>${!varprefix*}</userinput></term>
<term><userinput>${!varprefix@}</userinput></term>
<listitem>
<para>Matches <emphasis>names</emphasis> of all
previously declared variables beginning
with <parameter>varprefix</parameter>.
<programlisting># This is a variation on indirect reference, but with a * or @.
# Bash, version 2.04, adds this feature.
xyz23=whatever
xyz24=
a=${!xyz*} # Expands to *names* of declared variables
# ^ ^ ^ + beginning with "xyz".
echo "a = $a" # a = xyz23 xyz24
a=${!xyz@} # Same as above.
echo "a = $a" # a = xyz23 xyz24
echo "---"
abc23=something_else
b=${!abc*}
echo "b = $b" # b = abc23
c=${!b} # Now, the more familiar type of indirect reference.
echo $c # something_else</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Parameter Substitution -->
</chapter> <!-- Manipulating Variables -->
<chapter id="loops">
<title>Loops and Branches</title>
<epigraph>
<para>What needs this iteration, woman?</para>
<para>--Shakespeare, <replaceable>Othello</replaceable></para>
</epigraph>
<para><anchor id="loopref00"/></para>
<para>Operations on code blocks are the key to structured and organized
shell scripts. Looping and branching constructs provide the tools for
accomplishing this.</para>
<sect1 id="loops1">
<title>Loops</title>
<para>A <firstterm>loop</firstterm> is a block of code that
<firstterm>iterates</firstterm>
<footnote><para><anchor
id="iterationref"/><firstterm>Iteration</firstterm>:
Repeated execution of a command or group of commands, usually --
but not always, <firstterm>while</firstterm> a given condition
holds, or <firstterm>until</firstterm> a given condition is
met.</para></footnote>
a list of commands
as long as the <firstterm>loop control condition</firstterm>
is true.</para>
<variablelist id="forloopref">
<title><anchor id="forloopref1"/>for loops</title>
<varlistentry>
<term><command>for <parameter>arg</parameter> in
<replaceable>[list]</replaceable></command></term>
<listitem>
<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>
<para>This is the basic looping construct. It differs significantly
from its <firstterm>C</firstterm> counterpart.</para>
<para><anchor id="doinref"/></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(s)</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 successive 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 <link linkend="asteriskref">wild cards</link>.</para>
<para><anchor id="needsemicolon"/></para>
<para>If <firstterm>do</firstterm> is on same line as
<firstterm>for</firstterm>, 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 <firstterm>for</firstterm> loops</title>
<programlisting>&ex22;</programlisting>
</example>
<para><anchor id="multparaml"/></para>
<para>Each <userinput>[list]</userinput> element
may contain multiple parameters. This is useful when
processing parameters in groups. In such cases,
use the <link linkend="setref">set</link> command
(see <xref linkend="ex34"/>) to force parsing of each
<userinput>[list]</userinput> element and assignment of
each component to the positional parameters.</para>
<example id="ex22a">
<title><firstterm>for</firstterm> loop with two parameters in each
[list] element</title>
<programlisting>&ex22a;</programlisting>
</example>
<para><anchor id="paramli"/></para>
<para>A variable may supply the <userinput>[list]</userinput> in a
<firstterm>for loop</firstterm>.</para>
<example id="fileinfo">
<title><emphasis>Fileinfo:</emphasis> operating on a file list
contained in a variable</title>
<programlisting>&fileinfo;</programlisting>
</example>
<para><anchor id="paramli2"/></para>
<para>The <userinput>[list]</userinput> in a
<firstterm>for loop</firstterm> may be parameterized.</para>
<example id="fileinfo01">
<title>Operating on a parameterized file list</title>
<programlisting>&fileinfo01;</programlisting>
</example>
<para><anchor id="liglob"/></para>
<para>If the <userinput>[list]</userinput> in a
<firstterm>for loop</firstterm> contains wild cards
(<token>*</token> and <token>?</token>) used in filename
expansion, then <link linkend="globbingref">globbing</link>
takes place.</para>
<example id="listglob">
<title>Operating on files with a <firstterm>for</firstterm> loop</title>
<programlisting>&listglob;</programlisting>
</example>
<para><anchor id="omitlist"/></para>
<para>Omitting the <userinput>in [list]</userinput> part of a
<firstterm>for loop</firstterm> causes the loop to operate
on <token>$@</token> -- the <link linkend="posparamref">
positional parameters</link>. A particularly clever
illustration of this is <xref linkend="primes"/>. See also <xref
linkend="revposparams"/>.</para>
<example id="ex23">
<title>Missing <userinput>in [list]</userinput> in a
<firstterm>for</firstterm> loop</title>
<programlisting>&ex23;</programlisting>
</example>
<para><anchor id="loopcs"/></para>
<para>It is possible to use <link
linkend="commandsubref">command substitution</link>
to generate the <userinput>[list]</userinput> in a
<firstterm>for loop</firstterm>. See also <xref linkend="ex53"/>,
<xref linkend="symlinks"/> and <xref linkend="base"/>.</para>
<example id="forloopcmd">
<title>Generating the <userinput>[list]</userinput> in
a <firstterm>for</firstterm> loop with command substitution</title>
<programlisting>&forloopcmd;</programlisting>
</example>
<para>Here is a somewhat more complex example of using command
substitution to create the <userinput>[list]</userinput>.</para>
<example id="bingrep">
<title>A <firstterm>grep</firstterm> replacement
for binary files</title>
<programlisting>&bingrep;</programlisting>
</example>
<para>More of the same.</para>
<example id="userlist">
<title>Listing all users on the system</title>
<programlisting>&userlist;</programlisting>
</example>
<para>Yet another example of the <userinput>[list]</userinput>
resulting from command substitution.</para>
<example id="findstring">
<title>Checking all the binaries in a directory for
authorship</title>
<programlisting>&findstring;</programlisting>
</example>
<para>A final example of <userinput>[list]</userinput>
/ command substitution, but this time
the <quote>command</quote> is a <link
linkend="functionref">function</link>.</para>
<para><programlisting>generate_list ()
{
echo "one two three"
}
for word in $(generate_list) # Let "word" grab output of function.
do
echo "$word"
done
# one
# two
# three</programlisting></para>
<para><anchor id="loopredir"/></para>
<para>The output of a <firstterm>for loop</firstterm> may
be piped to a command or commands.</para>
<example id="symlinks">
<title>Listing the <firstterm>symbolic
links</firstterm> in a directory</title>
<programlisting>&symlinks;</programlisting>
</example>
<para>The <filename>stdout</filename> of a loop may be <link
linkend="ioredirref">redirected</link> to a file, as this slight
modification to the previous example shows.</para>
<example id="symlinks2">
<title>Symbolic links in a directory, saved to a file</title>
<programlisting>&symlinks2;</programlisting>
</example>
<para><anchor id="loopcstyle"/></para>
<para>There is an alternative syntax to a <firstterm>for
loop</firstterm> that will look very familiar to C
programmers. This requires <link
linkend="dblparensref">double parentheses</link>.</para>
<example id="forloopc">
<title>A C-style <firstterm>for</firstterm> loop</title>
<programlisting>&forloopc;</programlisting>
</example>
<para>See also <xref linkend="qfunction"/>, <xref
linkend="twodim"/>, and <xref linkend="collatz"/>.</para>
<para>---</para>
<para>Now, a <firstterm>for loop</firstterm> used in a
<quote>real-life</quote> context.</para>
<example id="ex24">
<title>Using <firstterm>efax</firstterm> in batch mode</title>
<programlisting>&ex24;</programlisting>
</example>
<note><para><anchor id="nododone"/>The
<link linkend="keywordref">keywords</link>
<command>do</command> and <command>done</command> delineate
the <firstterm>for-loop</firstterm> command block. However,
these may, in certain contexts, be omitted by framing the
command block within <link linkend="codeblockref">curly
brackets</link>
<programlisting>for((n=1; n&lt;=10; n++))
# No do!
{
echo -n "* $n *"
}
# No done!
# Outputs:
# * 1 ** 2 ** 3 ** 4 ** 5 ** 6 ** 7 ** 8 ** 9 ** 10 *
# And, echo $? returns 0, so Bash does not register an error.
echo
# But, note that in a classic for-loop: for n in [list] ...
#+ a terminal semicolon is required.
for n in 1 2 3
{ echo -n "$n "; }
# ^
# Thank you, YongYe, for pointing this out.</programlisting>
</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whileloopref"/><command>while</command></term>
<listitem>
<indexterm>
<primary>while</primary>
</indexterm>
<indexterm>
<primary>do</primary>
</indexterm>
<indexterm>
<primary>done</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>while</secondary>
</indexterm>
<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>). In contrast
to a <link linkend="forloopref1">for loop</link>, a
<firstterm>while loop</firstterm> finds use in situations
where the number of loop repetitions is not known
beforehand.</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(s)</replaceable></arg><sbr/>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<para>The bracket construct in a <firstterm>while
loop</firstterm> is nothing more than our old friend,
the <link linkend="testconstructs1">test brackets</link>
used in an <firstterm>if/then</firstterm> test. In fact,
a <firstterm>while loop</firstterm> can legally use the
more versatile <link linkend="dblbrackets">double-brackets
construct</link> (while [[ condition ]]).</para>
<para><anchor id="whileneedsemi"/></para>
<para><link linkend="needsemicolon">As is the case with
<firstterm>for loops</firstterm></link>, placing the
<firstterm>do</firstterm> 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 the <firstterm>test brackets</firstterm>
<link linkend="whilenobrackets">are <emphasis>not</emphasis>
mandatory</link> in a <firstterm>while</firstterm> loop.
See, for example, the <link linkend="getoptsx">getopts
construct</link>.</para>
<example id="ex25">
<title>Simple <firstterm>while</firstterm> loop</title>
<programlisting>&ex25;</programlisting>
</example>
<example id="ex26">
<title>Another <firstterm>while</firstterm> loop</title>
<programlisting>&ex26;</programlisting>
</example>
<para><anchor id="whmultcond"/></para>
<para>A <firstterm>while loop</firstterm> may have multiple
conditions. Only the final condition determines when the loop
terminates. This necessitates a slightly different loop syntax,
however.</para>
<example id="ex26a">
<title><firstterm>while</firstterm> loop with multiple conditions</title>
<programlisting>&ex26a;</programlisting>
</example>
<para><anchor id="wloopcstyle"/></para>
<para>As with a <firstterm>for loop</firstterm>, a
<firstterm>while loop</firstterm> may employ C-style syntax
by using the double-parentheses construct (see also <xref
linkend="cvars"/>).</para>
<example id="whloopc">
<title>C-style syntax in a <firstterm>while</firstterm> loop</title>
<programlisting>&whloopc;</programlisting>
</example>
<para><anchor id="whilefunc"/></para>
<para>
Inside its test brackets, a <firstterm>while loop</firstterm>
can call a <link linkend="functionref">function</link>.
<programlisting>t=0
condition ()
{
((t++))
if [ $t -lt 5 ]
then
return 0 # true
else
return 1 # false
fi
}
while condition
# ^^^^^^^^^
# Function call -- four loop iterations.
do
echo "Still going: t = $t"
done
# Still going: t = 1
# Still going: t = 2
# Still going: t = 3
# Still going: t = 4</programlisting>
</para>
<sidebar><para><anchor id="whilenobrackets"/></para>
<para>Similar to the <link linkend="ifgrepref">if-test</link>
construct, a <firstterm>while</firstterm> loop can omit the test
brackets.
<programlisting>while condition
do
command(s) ...
done</programlisting></para></sidebar>
<para><anchor id="whilereadref2"/></para>
<para>By coupling the power of the <link
linkend="readref">read</link> command with a
<firstterm>while loop</firstterm>, we get the handy <link
linkend="whilereadref">while read</link> construct, useful
for reading and parsing files.</para>
<para><programlisting>cat $filename | # Supply input from a file.
while read line # As long as there is another line to read ...
do
...
done
# =========== Snippet from "sd.sh" example script ========== #
while read value # Read one data point at a time.
do
rt=$(echo "scale=$SC; $rt + $value" | bc)
(( ct++ ))
done
am=$(echo "scale=$SC; $rt / $ct" | bc)
echo $am; return $ct # This function "returns" TWO values!
# Caution: This little trick will not work if $ct > 255!
# To handle a larger number of data points,
#+ simply comment out the "return $ct" above.
} &lt;"$datafile" # Feed in data file.</programlisting></para>
<para><anchor id="whredir"/></para>
<note>
<para>A <firstterm>while loop</firstterm> may have its
<filename>stdin</filename> <link
linkend="redirref">redirected to a file</link> by a
<token>&lt;</token> at its end.</para>
<para>A <firstterm>while loop</firstterm> may have its
<filename>stdin</filename> <link linkend="readpiperef">
supplied by a pipe</link>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="untilloopref"/><command>until</command></term>
<listitem>
<indexterm>
<primary>until</primary>
</indexterm>
<indexterm>
<primary>do</primary>
</indexterm>
<indexterm>
<primary>done</primary>
</indexterm>
<indexterm>
<primary>loop</primary>
<secondary>until</secondary>
</indexterm>
<para>This construct tests for a condition at the top of a loop, and keeps
looping as long as that condition is
<emphasis>false</emphasis> (opposite of <firstterm>while
loop</firstterm>).</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(s)</replaceable></arg><sbr/>
<arg choice="plain">done</arg>
</cmdsynopsis></para>
<para>Note that an <firstterm>until loop</firstterm> tests for the
terminating condition at the <emphasis>top</emphasis>
of the loop, differing from a similar construct in some
programming languages.</para>
<para>As is the case with <firstterm>for loops</firstterm>,
placing the <firstterm>do</firstterm> 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><firstterm>until</firstterm> loop</title>
<programlisting>&ex27;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<para><anchor id="chooseloop"/></para>
<para>How to choose between a <firstterm>for</firstterm> loop or a
<firstterm>while</firstterm> loop or
<firstterm>until</firstterm> loop? In <command>C</command>,
you would typically use a <firstterm>for</firstterm> loop
when the number of loop iterations is known beforehand. With
<firstterm>Bash</firstterm>, however, the situation is
fuzzier. The Bash <firstterm>for</firstterm> loop is more
loosely structured and more flexible than its equivalent in
other languages. Therefore, feel free to use whatever type
of loop gets the job done in the simplest way.</para>
</sect1> <!-- Loops -->
<sect1 id="nestedloops">
<title>Nested Loops</title>
<para>A <firstterm>nested loop</firstterm> is a loop within a
loop, an inner loop within the body of an outer one. How
this works 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 <firstterm>break</firstterm> within either the inner or outer
loop would 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
<link linkend="whileloopref">while loops</link>, and <xref
linkend="ex68"/> to see a while loop nested inside an <link
linkend="untilloopref">until loop</link>.</para>
</sect1> <!-- Nested Loops -->
<sect1 id="loopcontrol">
<title>Loop Control</title>
<epigraph>
<para>Tournez cent tours, tournez mille tours,</para>
<para>Tournez souvent et tournez toujours . . .</para>
<para>--Verlaine, <quote>Chevaux de bois</quote></para>
</epigraph>
<variablelist id="brkcont">
<title><anchor id="brkcont1"/>Commands affecting loop behavior</title>
<varlistentry>
<term><command>break</command></term>
<term><command>continue</command></term>
<listitem>
<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>
<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 <link
linkend="whileloopref">while</link> and <link
linkend="caseesac1">case</link>, 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 (<emphasis>breaks</emphasis>
out of it), while <command>continue</command> causes a jump
to the next <link linkend="iterationref">iteration</link>
of the loop, skipping all the remaining commands in that
particular loop cycle.</para>
<example id="ex28">
<title>Effects of <firstterm>break</firstterm> and
<command>continue</command> in a loop</title>
<programlisting>&ex28;</programlisting>
</example>
<para><anchor id="breakparam"/></para>
<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>
<example id="continuenex">
<title>Using <firstterm>continue N</firstterm> in an actual task</title>
<programlisting>&continuenex;</programlisting>
</example>
<caution><para>The <command>continue N</command> construct is
difficult to understand and tricky to use in any meaningful
context. It is probably best avoided.</para></caution>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Loop Control Commands -->
<sect1 id="testbranch">
<title>Testing and Branching</title>
<para>The <command>case</command> and <command>select</command>
constructs are technically not loops, since they do not iterate the
execution of a code block. Like loops, however, they direct
program flow according to conditions at the top or bottom of
the block.</para>
<variablelist id="caseesac">
<title><anchor id="caseesac1"/>Controlling program flow in a code
block</title>
<varlistentry>
<term><command>case (in) / esac</command></term>
<listitem>
<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>
<para>The <command>case</command> construct is the shell
scripting analog to <replaceable>switch</replaceable>
in <command>C/C++</command>.
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/>
<command>esac</command>
</cmdsynopsis></para>
<note><para>
<itemizedlist>
<listitem><para>Quoting the variables is not mandatory,
since word splitting does not take place.</para>
</listitem>
<listitem>
<para><anchor id="caseparen"/>Each test line
ends with a right paren <command>)</command>.
<footnote><para>Pattern-match lines may also <emphasis>start</emphasis>
with a <command>(</command> left paren to give the layout
a more structured appearance.</para>
<para><programlisting>case $( arch ) in # $( arch ) returns machine architecture.
( i386 ) echo "80386-based machine";;
# ^ ^
( i486 ) echo "80486-based machine";;
( i586 ) echo "Pentium-based machine";;
( i686 ) echo "Pentium2+-based machine";;
( * ) echo "Other type of machine";;
esac</programlisting></para></footnote>
</para>
</listitem>
<listitem><para>Each condition block ends
with a <emphasis>double</emphasis> semicolon
<token>;;</token>.</para>
</listitem>
<listitem><para>If a condition tests
<firstterm>true</firstterm>, then the associated
commands execute and the <command>case</command>
block terminates.</para></listitem>
<listitem><para>The entire <command>case</command>
block ends with an <command>esac</command>
(<wordasword>case</wordasword> spelled backwards).</para>
</listitem>
</itemizedlist>
</para></note>
<example id="ex29">
<title>Using <firstterm>case</firstterm></title>
<programlisting>&ex29;</programlisting>
</example>
<example id="ex30">
<title>Creating menus using <firstterm>case</firstterm></title>
<programlisting>&ex30;</programlisting>
</example>
<para><anchor id="casecl"/></para>
<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 $E_PARAM;;
# 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>
<para>Here is a more straightforward example of
command-line parameter handling:
<programlisting>#! /bin/bash
while [ $# -gt 0 ]; do # Until you run out of parameters . . .
case "$1" in
-d|--debug)
# "-d" or "--debug" parameter?
DEBUG=1
;;
-c|--conf)
CONFFILE="$2"
shift
if [ ! -f $CONFFILE ]; then
echo "Error: Supplied file doesn't exist!"
exit $E_CONFFILE # File not found error.
fi
;;
esac
shift # Check next set of parameters.
done
# From Stefano Falsetto's "Log2Rot" script,
#+ part of his "rottlog" package.
# Used with permission.</programlisting></para>
<example id="casecmd">
<title>Using <firstterm>command substitution</firstterm>
to generate the <firstterm>case</firstterm> variable</title>
<programlisting>&casecmd;</programlisting>
</example>
<para><anchor id="csglob"/></para>
<para>A <command>case</command> construct can filter strings for
<link linkend="globbingref">globbing</link> patterns.</para>
<example id="matchstring">
<title>Simple string matching</title>
<programlisting>&matchstring;</programlisting>
</example>
<example id="isalpha">
<title>Checking for alphabetic input</title>
<programlisting>&isalpha;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="selectref"/><command>select</command></term>
<listitem>
<indexterm>
<primary>select</primary>
</indexterm>
<indexterm>
<primary>menus</primary>
</indexterm>
<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 this may be changed.</para>
<example id="ex31">
<title>Creating menus using <firstterm>select</firstterm></title>
<programlisting>&ex31;</programlisting>
</example>
<para><anchor id="inlistomit"/></para>
<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
the function containing the <command>select</command>
construct.</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 <firstterm>select</firstterm>
in a function</title>
<programlisting>&ex32;</programlisting>
</example>
<para>See also <xref linkend="resistor"/>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Testing and Branching -->
</chapter> <!-- Loops -->
<chapter id="commandsub">
<title>Command Substitution</title>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>`</secondary>
</indexterm>
<para>
<anchor id="commandsubref"/><command>Command
substitution</command> reassigns the output of a command
<footnote><para>For purposes of <firstterm>command
substitution</firstterm>, a <command>command</command>
may be an external system command, an internal scripting
<link linkend="builtinref">builtin</link>, or even <link
linkend="rvt">a script function</link>.</para></footnote>
or even multiple commands; it literally plugs the command
output into another context.
<footnote><para>In a more technically correct sense,
<firstterm>command substitution</firstterm> extracts the
<filename>stdout</filename> of a command, then assigns
it to a variable using the <token>=</token>
operator.</para></footnote>
</para>
<para><anchor id="backquotesref"/>The classic form of command
substitution uses <firstterm>backquotes</firstterm>
(`...`). Commands within backquotes (backticks) generate
command-line text.
<programlisting>script_name=`basename $0`
echo "The name of this script is $script_name."</programlisting></para>
<formalpara>
<title>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 <link linkend="forloopref1">for</link>
loop.</title>
<para></para>
</formalpara>
<para>
<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 -- &lt; 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_listing2
# 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>
<note><para><anchor id="cssubsh"/>Command substitution
invokes a <link
linkend="subshellsref">subshell</link>.</para></note>
<caution><para><anchor id="csws"/>Command substitution may
result in <link linkend="wsplitref">word splitting</link>.
<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>
<para><anchor id="cstrnl"/></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.
#Code snippet by St&eacute;phane Chazelas.</programlisting>
</para>
</caution>
<caution>
<para>Using <command>echo</command> to output an
<firstterm>unquoted</firstterm> variable set with command
substitution removes trailing newlines characters from
the output of the reassigned command(s). This can cause
unpleasant surprises.
<programlisting>dir_listing=`ls -l`
echo $dir_listing # unquoted
# Expecting a nicely ordered directory listing.
# 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.
echo "$dir_listing" # quoted
# -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</programlisting>
</para>
</caution>
<para>Command substitution even permits setting a variable to the
contents of a file, using either <link
linkend="ioredirref">redirection</link> or the <link
linkend="catref">cat</link> command.</para>
<para>
<programlisting>variable1=`&lt;file1` # Set "variable1" to contents of "file1".
variable2=`cat file2` # Set "variable2" to contents of "file2".
# This, however, forks a new process,
#+ so the line of code executes slower than the above version.
# Note that the variables may contain embedded whitespace,
#+ or even (horrors), control characters.
# It is not necessary to explicitly assign a variable.
echo "` &lt;$0`" # Echoes the script itself to stdout.</programlisting>
</para>
<para>
<programlisting># Excerpts from system file, /etc/rc.d/rc.sysinit
#+ (on a Red Hat Linux installation)
if [ -f /fsckoptions ]; then
fsckoptions=`cat /fsckoptions`
...
fi
#
#
if [ -e "/proc/ide/${disk[$device]}/media" ] ; then
hdmedia=`cat /proc/ide/${disk[$device]}/media`
...
fi
#
#
if [ ! -n "`uname -r | grep -- "-"`" ]; then
ktag="`cat /proc/version`"
...
fi
#
#
if [ $usb = "1" ]; then
sleep 5
mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"`
kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"`
...
fi</programlisting>
</para>
<caution>
<para>Do not set a variable to the contents of a
<emphasis>long</emphasis> text file unless you have a very good
reason for doing so. Do not set a variable to the contents of a
<firstterm>binary</firstterm> file, even as a joke.</para>
<example id="stupscr">
<title>Stupid script tricks</title>
<programlisting>&stupscr;</programlisting>
</example>
<para>Notice that a <firstterm>buffer overrun</firstterm>
does not occur. This is one instance where an interpreted
language, such as Bash, provides more protection from
programmer mistakes than a compiled language.</para>
</caution>
<para><anchor id="csvl"/></para>
<para>Command substitution permits setting a variable to the
output of a <link linkend="forloopref1">loop</link>. The
key to this is grabbing the output of an <link
linkend="echoref">echo</link> command within the
loop.</para>
<example id="csubloop">
<title>Generating a variable from a loop</title>
<programlisting>&csubloop;</programlisting>
</example>
<para><anchor id="cstoolset"/></para>
<sidebar>
<para>Command substitution makes it possible to extend the
toolset available to Bash. It is simply a matter
of writing a program or script that outputs to
<filename>stdout</filename> (like a well-behaved UNIX
tool should) and assigning that output to a variable.</para>
<para>
<programlisting>#include &lt;stdio.h&gt;
/* "Hello, world." C program */
int main()
{
printf( "Hello, world.\n" );
return (0);
}</programlisting>
<screen><prompt>bash$ </prompt><userinput>gcc -o hello hello.c</userinput>
</screen>
</para>
<para>
<programlisting>#!/bin/bash
# hello.sh
greeting=`./hello`
echo $greeting</programlisting>
<screen><prompt>bash$ </prompt><userinput>sh hello.sh</userinput>
<computeroutput>Hello, world.</computeroutput>
</screen>
</para>
</sidebar>
<note>
<para><anchor id="csparens"/>The <command>$(...)</command>
form has superseded backticks for command
substitution.</para>
<para><programlisting>output=$(sed -n /"$1"/p $file) # From "grp.sh" example.
# Setting a variable to the contents of a text file.
File_contents1=$(cat $file1)
File_contents2=$(&lt;$file2) # Bash permits this also.</programlisting></para>
<para>The <command>$(...)</command> form of command substitution
treats a double backslash in a different way than
<command>`...`</command>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo `echo \\`</userinput>
<computeroutput></computeroutput>
<prompt>bash$ </prompt><userinput>echo $(echo \\)</userinput>
<computeroutput>\</computeroutput>
</screen>
</para>
<para><anchor id="csnest"/></para>
<para>The <command>$(...)</command> form of command
substitution permits nesting.
<footnote>
<para>
In fact, nesting with backticks is also possible,
but only by escaping the inner backticks, as John
Default points out.
<programlisting>word_count=` wc -w \`echo * | awk '{print $8}'\` `</programlisting>
</para>
</footnote>
</para>
<para><programlisting>word_count=$( wc -w $(echo * | awk '{print $8}') )</programlisting>
</para>
<para>Or, for something a bit more elaborate . . .</para>
<example id="agram2">
<title>Finding anagrams</title>
<programlisting>&agram2;</programlisting>
</example>
</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>
<listitem><para><xref linkend="altbc"/></para></listitem>
</orderedlist>
</para>
</chapter> <!-- Command Substitution -->
<chapter id="arithexp">
<title>Arithmetic Expansion</title>
<para><anchor id="arithexpref"/>Arithmetic expansion provides a
powerful tool for performing (integer) arithmetic
operations in scripts. Translating a string into a
numerical expression is relatively straightforward using
<firstterm>backticks</firstterm>, <firstterm>double
parentheses</firstterm>, or <firstterm>let</firstterm>.</para>
<variablelist id="arithexpvar">
<title><anchor id="arithexpvar1"/>Variations</title>
<varlistentry>
<term>Arithmetic expansion with <link linkend="backquotesref">backticks</link> (often used in
conjunction with <link linkend="exprref">expr</link>)</term>
<listitem>
<indexterm>
<primary>arithmetic</primary> <secondary>expansion</secondary>
</indexterm> <indexterm>
<primary>arithmetic</primary> <secondary>expansion</secondary>
</indexterm>
<para><programlisting>z=`expr $z + 3` # The 'expr' command performs the expansion.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term>Arithmetic expansion with <link linkend="dblparens">double
parentheses</link></term>
<term>and using <link linkend="letref">let</link></term>
<listitem>
<indexterm><primary>double</primary>
<secondary>parentheses</secondary></indexterm>
<indexterm><primary>let</primary>
<secondary>let</secondary></indexterm>
<para>The use of <firstterm>backticks</firstterm>
(<firstterm>backquotes</firstterm>) in arithmetic
expansion has been superseded by <firstterm>double
parentheses</firstterm> --
<userinput>((...))</userinput> and
<userinput>$((...))</userinput> -- and also by the very
convenient <link linkend="letref">let</link> construction.</para>
<para>
<programlisting>z=$(($z+3))
z=$((z+3)) # Also correct.
# Within double parentheses,
#+ parameter dereferencing
#+ is optional.
# $((EXPRESSION)) is arithmetic expansion. # Not to be confused with
#+ command substitution.
# You may also use operations within double parentheses without assignment.
n=0
echo "n = $n" # n = 0
(( n += 1 )) # Increment.
# (( $n += 1 )) is incorrect!
echo "n = $n" # n = 1
let z=z+3
let "z += 3" # Quotes permit the use of spaces in variable assignment.
# The 'let' operator actually performs arithmetic evaluation,
#+ rather than expansion.</programlisting>
</para>
<para>Examples of arithmetic expansion in scripts:
<orderedlist>
<listitem><para><xref linkend="ex45"/></para></listitem>
<listitem><para><xref linkend="ex25"/></para></listitem>
<listitem><para><xref linkend="ex66"/></para></listitem>
<listitem><para><xref linkend="bubble"/></para></listitem>
<listitem><para><xref linkend="tree"/></para></listitem>
</orderedlist>
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Arithmetic Expansion -->
<chapter id="Recess-Time">
<title>Recess Time</title>
<para><emphasis>
This bizarre little intermission gives the reader a chance to
relax and maybe laugh a bit.
</emphasis></para>
<blockquote>
<literallayout>
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 making the copies,
send a 100-line Bash script to the first person on the list
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>Tuxracer</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!
</literallayout>
</blockquote>
<para><emphasis>Courtesy 'NIX "fortune cookies", with some
alterations and many apologies</emphasis></para>
</chapter> <!-- Recess Time -->
</part> <!-- Part 3 (Beyond the Basics) -->
<part label="Part 4" id="part4">
<title>Commands</title>
<partintro>
<para><anchor id="part4A"/></para>
<para>Mastering the commands on your Linux machine is an indispensable
prelude to writing effective shell scripts.</para>
<para>This section covers the following commands:</para>
<itemizedlist id="Commandlist">
<listitem><para><link linkend="dotref">.</link>
(See also <link linkend="sourceref">source</link>)</para></listitem>
<listitem><para><link linkend="acref">ac</link></para></listitem>
<listitem><para><link linkend="useraddref">adduser</link></para></listitem>
<listitem><para><link linkend="agettyref">agetty</link></para></listitem>
<listitem><para><link linkend="agrepref">agrep</link></para></listitem>
<listitem><para><link linkend="arref">ar</link></para></listitem>
<listitem><para><link linkend="archref">arch</link></para></listitem>
<listitem><para><link linkend="atref">at</link></para></listitem>
<listitem><para><link linkend="autoloadref">autoload</link></para></listitem>
<listitem><para><link linkend="awkref">awk</link>
(See also <link linkend="awkmath">Using
<command>awk</command> for
math operations</link>)</para></listitem>
<listitem><para><link linkend="badblocksref">badblocks</link></para></listitem>
<listitem><para><link linkend="bannerref">banner</link></para></listitem>
<listitem><para><link linkend="basenameref">basename</link></para></listitem>
<listitem><para><link linkend="batchref">batch</link></para></listitem>
<listitem><para><link linkend="bcref">bc</link></para></listitem>
<listitem><para><link linkend="bgref">bg</link></para></listitem>
<listitem><para><link linkend="bindref">bind</link></para></listitem>
<listitem><para><link linkend="bisonref">bison</link></para></listitem>
<listitem><para><link linkend="bltref">builtin</link></para></listitem>
<listitem><para><link linkend="bzgrepref">bzgrep</link></para></listitem>
<listitem><para><link linkend="bzipref">bzip2</link></para></listitem>
<listitem><para><link linkend="calref">cal</link></para></listitem>
<listitem><para><link linkend="callerref">caller</link></para></listitem>
<listitem><para><link linkend="catref">cat</link></para></listitem>
<listitem><para><link linkend="cdref">cd</link></para></listitem>
<listitem><para><link linkend="chattrref">chattr</link></para></listitem>
<listitem><para><link linkend="chfnref">chfn</link></para></listitem>
<listitem><para><link linkend="chgrpref">chgrp</link></para></listitem>
<listitem><para><link linkend="chkconfigref">chkconfig</link></para></listitem>
<listitem><para><link linkend="chmodref">chmod</link></para></listitem>
<listitem><para><link linkend="chownref">chown</link></para></listitem>
<listitem><para><link linkend="chrootref">chroot</link></para></listitem>
<listitem><para><link linkend="cksumref">cksum</link></para></listitem>
<listitem><para><link linkend="clearref">clear</link></para></listitem>
<listitem><para><link linkend="clockref">clock</link></para></listitem>
<listitem><para><link linkend="cmpref">cmp</link></para></listitem>
<listitem><para><link linkend="colref">col</link></para></listitem>
<listitem><para><link linkend="colrmref">colrm</link></para></listitem>
<listitem><para><link linkend="columnref">column</link></para></listitem>
<listitem><para><link linkend="commref">comm</link></para></listitem>
<listitem><para><link linkend="commandref">command</link></para></listitem>
<listitem><para><link linkend="compgenref">compgen</link></para></listitem>
<listitem><para><link linkend="completeref">complete</link></para></listitem>
<listitem><para><link linkend="compressref">compress</link></para></listitem>
<listitem><para><link linkend="coprocref">coproc</link></para></listitem>
<listitem><para><link linkend="cpref">cp</link></para></listitem>
<listitem><para><link linkend="cpioref">cpio</link></para></listitem>
<listitem><para><link linkend="cronref">cron</link></para></listitem>
<listitem><para><link linkend="cryptref">crypt</link></para></listitem>
<listitem><para><link linkend="csplitref">csplit</link></para></listitem>
<listitem><para><link linkend="curef">cu</link></para></listitem>
<listitem><para><link linkend="cutref">cut</link></para></listitem>
<listitem><para><link linkend="dateref">date</link></para></listitem>
<listitem><para><link linkend="dcref">dc</link></para></listitem>
<listitem><para><link linkend="ddref">dd</link></para></listitem>
<listitem><para><link linkend="debugfsref">debugfs</link></para></listitem>
<listitem><para><link linkend="declareref">declare</link></para></listitem>
<listitem><para><link linkend="depmodref">depmod</link></para></listitem>
<listitem><para><link linkend="dfref">df</link></para></listitem>
<listitem><para><link linkend="dialogref">dialog</link></para></listitem>
<listitem><para><link linkend="diffref">diff</link></para></listitem>
<listitem><para><link linkend="diff3ref">diff3</link></para></listitem>
<listitem><para><link linkend="diffstatref">diffstat</link></para></listitem>
<listitem><para><link linkend="digref">dig</link></para></listitem>
<listitem><para><link linkend="dirnameref">dirname</link></para></listitem>
<listitem><para><link linkend="dirsd">dirs</link></para></listitem>
<listitem><para><link linkend="disownref">disown</link></para></listitem>
<listitem><para><link linkend="dmesgref">dmesg</link></para></listitem>
<listitem><para><link linkend="doexecref">doexec</link></para></listitem>
<listitem><para><link linkend="dos2unixref">dos2unix</link></para></listitem>
<listitem><para><link linkend="duref">du</link></para></listitem>
<listitem><para><link linkend="dumpref">dump</link></para></listitem>
<listitem><para><link linkend="dumpe2fsref">dumpe2fs</link></para></listitem>
<listitem><para><link linkend="e2fsckref">e2fsck</link></para></listitem>
<listitem><para><link linkend="echoref">echo</link></para></listitem>
<listitem><para><link linkend="egrepref">egrep</link></para></listitem>
<listitem><para><link linkend="enableref">enable</link></para></listitem>
<listitem><para><link linkend="enscriptref">enscript</link></para></listitem>
<listitem><para><link linkend="envvref">env</link></para></listitem>
<listitem><para><link linkend="eqnref">eqn</link></para></listitem>
<listitem><para><link linkend="evalref">eval</link></para></listitem>
<listitem><para><link linkend="execref">exec</link></para></listitem>
<listitem><para><link linkend="exitref">exit</link>
(Related topic: <link linkend="exitstatusref">exit
status</link>)</para></listitem>
<listitem><para><link linkend="expandref">expand</link></para></listitem>
<listitem><para><link linkend="exportref">export</link></para></listitem>
<listitem><para><link linkend="exprref">expr</link></para></listitem>
<listitem><para><link linkend="factorref">factor</link></para></listitem>
<listitem><para><link linkend="falseref">false</link></para></listitem>
<listitem><para><link linkend="fdformatref">fdformat</link></para></listitem>
<listitem><para><link linkend="fdiskref">fdisk</link></para></listitem>
<listitem><para><link linkend="fgref">fg</link></para></listitem>
<listitem><para><link linkend="fgrepref">fgrep</link></para></listitem>
<listitem><para><link linkend="fileref">file</link></para></listitem>
<listitem><para><link linkend="findref">find</link></para></listitem>
<listitem><para><link linkend="fingerref">finger</link></para></listitem>
<listitem><para><link linkend="flexref">flex</link></para></listitem>
<listitem><para><link linkend="flockref">flock</link></para></listitem>
<listitem><para><link linkend="fmtref">fmt</link></para></listitem>
<listitem><para><link linkend="foldref">fold</link></para></listitem>
<listitem><para><link linkend="freeref">free</link></para></listitem>
<listitem><para><link linkend="fsckref">fsck</link></para></listitem>
<listitem><para><link linkend="ftpref">ftp</link></para></listitem>
<listitem><para><link linkend="fuserref">fuser</link></para></listitem>
<listitem><para><link linkend="getfaclref">getfacl</link></para></listitem>
<listitem><para><link linkend="getopty">getopt</link></para></listitem>
<listitem><para><link linkend="getoptsx">getopts</link></para></listitem>
<listitem><para><link linkend="gettextref">gettext</link></para></listitem>
<listitem><para><link linkend="gettyref">getty</link></para></listitem>
<listitem><para><link linkend="gnomemountref">gnome-mount</link></para></listitem>
<listitem><para><link linkend="grepref">grep</link></para></listitem>
<listitem><para><link linkend="groffref">groff</link></para></listitem>
<listitem><para><link linkend="groupmodref">groupmod</link></para></listitem>
<listitem><para><link linkend="groupscmdref">groups</link>
(Related topic: the <link linkend="groupsref">$GROUPS</link>
variable)</para></listitem>
<listitem><para><link linkend="gsref">gs</link></para></listitem>
<listitem><para><link linkend="gzipref">gzip</link></para></listitem>
<listitem><para><link linkend="haltref">halt</link></para></listitem>
<listitem><para><link linkend="hashcmdref">hash</link></para></listitem>
<listitem><para><link linkend="hdparmref">hdparm</link></para></listitem>
<listitem><para><link linkend="headref">head</link></para></listitem>
<listitem><para><link linkend="helpref">help</link></para></listitem>
<listitem><para><link linkend="hexdumpref">hexdump</link></para></listitem>
<listitem><para><link linkend="hostref">host</link></para></listitem>
<listitem><para><link linkend="hostidref">hostid</link></para></listitem>
<listitem><para><link linkend="hnameref">hostname</link>
(Related topic: the <link linkend="hostnameref">$HOSTNAME</link>
variable)</para></listitem>
<listitem><para><link linkend="hwclockref">hwclock</link></para></listitem>
<listitem><para><link linkend="iconvref">iconv</link></para></listitem>
<listitem><para><link linkend="idref">id</link>
(Related topic: the <link linkend="uidref">$UID</link>
variable)</para></listitem>
<listitem><para><link linkend="ifconfigref">ifconfig</link></para></listitem>
<listitem><para><link linkend="inforef">info</link></para></listitem>
<listitem><para><link linkend="infocmpref">infocmp</link></para></listitem>
<listitem><para><link linkend="initref">init</link></para></listitem>
<listitem><para><link linkend="insmodref">insmod</link></para></listitem>
<listitem><para><link linkend="installref">install</link></para></listitem>
<listitem><para><link linkend="ipref">ip</link></para></listitem>
<listitem><para><link linkend="ipcalcref">ipcalc</link></para></listitem>
<listitem><para><link linkend="iptablesref">iptables</link></para></listitem>
<listitem><para><link linkend="iwconfigref">iwconfig</link></para></listitem>
<listitem><para><link linkend="jobsref">jobs</link></para></listitem>
<listitem><para><link linkend="joinref">join</link></para></listitem>
<listitem><para><link linkend="jotref">jot</link></para></listitem>
<listitem><para><link linkend="killref">kill</link></para></listitem>
<listitem><para><link linkend="killallref">killall</link></para></listitem>
<listitem><para><link linkend="lastref">last</link></para></listitem>
<listitem><para><link linkend="lastcommref">lastcomm</link></para></listitem>
<listitem><para><link linkend="lastlogref">lastlog</link></para></listitem>
<listitem><para><link linkend="lddref">ldd</link></para></listitem>
<listitem><para><link linkend="lessref">less</link></para></listitem>
<listitem><para><link linkend="letref">let</link></para></listitem>
<listitem><para><link linkend="lexref">lex</link></para></listitem>
<listitem><para><link linkend="lidref">lid</link></para></listitem>
<listitem><para><link linkend="linkref">ln</link></para></listitem>
<listitem><para><link linkend="locateref">locate</link></para></listitem>
<listitem><para><link linkend="lockfileref">lockfile</link></para></listitem>
<listitem><para><link linkend="loggerref">logger</link></para></listitem>
<listitem><para><link linkend="lognameref">logname</link></para></listitem>
<listitem><para><link linkend="logoutref">logout</link></para></listitem>
<listitem><para><link linkend="logrotateref">logrotate</link></para></listitem>
<listitem><para><link linkend="lookref">look</link></para></listitem>
<listitem><para><link linkend="losetupref">losetup</link></para></listitem>
<listitem><para><link linkend="lpref">lp</link></para></listitem>
<listitem><para><link linkend="lsref">ls</link></para></listitem>
<listitem><para><link linkend="lsdevref">lsdev</link></para></listitem>
<listitem><para><link linkend="lsmodref">lsmod</link></para></listitem>
<listitem><para><link linkend="lsofref">lsof</link></para></listitem>
<listitem><para><link linkend="lspciref">lspci</link></para></listitem>
<listitem><para><link linkend="lsusbref">lsusb</link></para></listitem>
<listitem><para><link linkend="ltraceref">ltrace</link></para></listitem>
<listitem><para><link linkend="lynxref">lynx</link></para></listitem>
<listitem><para><link linkend="lzmaref">lzcat</link></para></listitem>
<listitem><para><link linkend="lzmaref">lzma</link></para></listitem>
<listitem><para><link linkend="m4ref">m4</link></para></listitem>
<listitem><para><link linkend="commmail1">mail</link></para></listitem>
<listitem><para><link linkend="mailstatsref">mailstats</link></para></listitem>
<listitem><para><link linkend="mailtoref">mailto</link></para></listitem>
<listitem><para><link linkend="makeref">make</link></para></listitem>
<listitem><para><link linkend="MAKEDEVref">MAKEDEV</link></para></listitem>
<listitem><para><link linkend="manref">man</link></para></listitem>
<listitem><para><link linkend="mapfileref">mapfile</link></para></listitem>
<listitem><para><link linkend="mcookieref">mcookie</link></para></listitem>
<listitem><para><link linkend="md5sumref">md5sum</link></para></listitem>
<listitem><para><link linkend="mergeref">merge</link></para></listitem>
<listitem><para><link linkend="mesgref">mesg</link></para></listitem>
<listitem><para><link linkend="mimencoderef">mimencode</link></para></listitem>
<listitem><para><link linkend="mkbootdiskref">mkbootdisk</link></para></listitem>
<listitem><para><link linkend="mkdirref">mkdir</link></para></listitem>
<listitem><para><link linkend="mkdosfsref">mkdosfs</link></para></listitem>
<listitem><para><link linkend="mke2fsref">mke2fs</link></para></listitem>
<listitem><para><link linkend="mkfiforef">mkfifo</link></para></listitem>
<listitem><para><link linkend="mkisofsref">mkisofs</link></para></listitem>
<listitem><para><link linkend="mknodref">mknod</link></para></listitem>
<listitem><para><link linkend="mkswapref">mkswap</link></para></listitem>
<listitem><para><link linkend="mktempref">mktemp</link></para></listitem>
<listitem><para><link linkend="mmencoderef">mmencode</link></para></listitem>
<listitem><para><link linkend="modinforef">modinfo</link></para></listitem>
<listitem><para><link linkend="modproberef">modprobe</link></para></listitem>
<listitem><para><link linkend="moreref">more</link></para></listitem>
<listitem><para><link linkend="mountref">mount</link></para></listitem>
<listitem><para><link linkend="msgfmtref">msgfmt</link></para></listitem>
<listitem><para><link linkend="mvref">mv</link></para></listitem>
<listitem><para><link linkend="ncref">nc</link></para></listitem>
<listitem><para><link linkend="netconfigref">netconfig</link></para></listitem>
<listitem><para><link linkend="netstatref">netstat</link></para></listitem>
<listitem><para><link linkend="newgrpref">newgrp</link></para></listitem>
<listitem><para><link linkend="niceref">nice</link></para></listitem>
<listitem><para><link linkend="nlref">nl</link></para></listitem>
<listitem><para><link linkend="nmref">nm</link></para></listitem>
<listitem><para><link linkend="nmapref">nmap</link></para></listitem>
<listitem><para><link linkend="nohupref">nohup</link></para></listitem>
<listitem><para><link linkend="nslookupref">nslookup</link></para></listitem>
<listitem><para><link linkend="objdumpref">objdump</link></para></listitem>
<listitem><para><link linkend="odref">od</link></para></listitem>
<listitem><para><link linkend="opensslref">openssl</link></para></listitem>
<listitem><para><link linkend="passwdref">passwd</link></para></listitem>
<listitem><para><link linkend="pasteref">paste</link></para></listitem>
<listitem><para><link linkend="patchref">patch</link>
(Related topic: <link linkend="diffref">diff</link>)</para></listitem>
<listitem><para><link linkend="pathchkref">pathchk</link></para></listitem>
<listitem><para><link linkend="paxref">pax</link></para></listitem>
<listitem><para><link linkend="pgrepref">pgrep</link></para></listitem>
<listitem><para><link linkend="pidofref">pidof</link></para></listitem>
<listitem><para><link linkend="pingref">ping</link></para></listitem>
<listitem><para><link linkend="pkillref">pkill</link></para></listitem>
<listitem><para><link linkend="dirsd">popd</link></para></listitem>
<listitem><para><link linkend="prref">pr</link></para></listitem>
<listitem><para><link linkend="printenvref">printenv</link></para></listitem>
<listitem><para><link linkend="printfref">printf</link></para></listitem>
<listitem><para><link linkend="procinforef">procinfo</link></para></listitem>
<listitem><para><link linkend="ppssref">ps</link></para></listitem>
<listitem><para><link linkend="pstreeref">pstree</link></para></listitem>
<listitem><para><link linkend="ptxref">ptx</link></para></listitem>
<listitem><para><link linkend="dirsd">pushd</link></para></listitem>
<listitem><para><link linkend="pwd2ref">pwd</link>
(Related topic: the <link linkend="pwdref">$PWD</link>
variable)</para></listitem>
<listitem><para><link linkend="quotaref">quota</link></para></listitem>
<listitem><para><link linkend="rcpref">rcp</link></para></listitem>
<listitem><para><link linkend="rdevref">rdev</link></para></listitem>
<listitem><para><link linkend="rdistref">rdist</link></para></listitem>
<listitem><para><link linkend="readref">read</link></para></listitem>
<listitem><para><link linkend="readelfref">readelf</link></para></listitem>
<listitem><para><link linkend="readlinkref">readlink</link></para></listitem>
<listitem><para><link linkend="readonlyref">readonly</link></para></listitem>
<listitem><para><link linkend="rebootref">reboot</link></para></listitem>
<listitem><para><link linkend="recoderef">recode</link></para></listitem>
<listitem><para><link linkend="nice2ref">renice</link></para></listitem>
<listitem><para><link linkend="resetref">reset</link></para></listitem>
<listitem><para><link linkend="resizeref">resize</link></para></listitem>
<listitem><para><link linkend="restoreref">restore</link></para></listitem>
<listitem><para><link linkend="revref">rev</link></para></listitem>
<listitem><para><link linkend="rloginref">rlogin</link></para></listitem>
<listitem><para><link linkend="rmref">rm</link></para></listitem>
<listitem><para><link linkend="rmdirref">rmdir</link></para></listitem>
<listitem><para><link linkend="rmmodref">rmmod</link></para></listitem>
<listitem><para><link linkend="routeref">route</link></para></listitem>
<listitem><para><link linkend="rpmref">rpm</link></para></listitem>
<listitem><para><link linkend="rpm2cpioref">rpm2cpio</link></para></listitem>
<listitem><para><link linkend="rshref">rsh</link></para></listitem>
<listitem><para><link linkend="rsyncref">rsync</link></para></listitem>
<listitem><para><link linkend="runlevelref">runlevel</link></para></listitem>
<listitem><para><link linkend="runpartsref">run-parts</link></para></listitem>
<listitem><para><link linkend="rxref">rx</link></para></listitem>
<listitem><para><link linkend="rzref">rz</link></para></listitem>
<listitem><para><link linkend="sarref">sar</link></para></listitem>
<listitem><para><link linkend="scpref">scp</link></para></listitem>
<listitem><para><link linkend="scriptref">script</link></para></listitem>
<listitem><para><link linkend="sdiffref">sdiff</link></para></listitem>
<listitem><para><link linkend="sedref">sed</link></para></listitem>
<listitem><para><link linkend="seqref">seq</link></para></listitem>
<listitem><para><link linkend="serviceref">service</link></para></listitem>
<listitem><para><link linkend="setref">set</link></para></listitem>
<listitem><para><link linkend="setfaclref">setfacl</link></para></listitem>
<listitem><para><link linkend="setquotaref">setquota</link></para></listitem>
<listitem><para><link linkend="setserialref">setserial</link></para></listitem>
<listitem><para><link linkend="settermref">setterm</link></para></listitem>
<listitem><para><link linkend="sha1sumref">sha1sum</link></para></listitem>
<listitem><para><link linkend="sharref">shar</link></para></listitem>
<listitem><para><link linkend="shoptref">shopt</link></para></listitem>
<listitem><para><link linkend="shredref">shred</link></para></listitem>
<listitem><para><link linkend="shutdownref">shutdown</link></para></listitem>
<listitem><para><link linkend="sizeref">size</link></para></listitem>
<listitem><para><link linkend="nice2ref">skill</link></para></listitem>
<listitem><para><link linkend="sleepref">sleep</link></para></listitem>
<listitem><para><link linkend="slocateref">slocate</link></para></listitem>
<listitem><para><link linkend="nice2ref">snice</link></para></listitem>
<listitem><para><link linkend="sortref">sort</link></para></listitem>
<listitem><para><link linkend="sourceref">source</link></para></listitem>
<listitem><para><link linkend="soxref">sox</link></para></listitem>
<listitem><para><link linkend="splitref">split</link></para></listitem>
<listitem><para><link linkend="sqref">sq</link></para></listitem>
<listitem><para><link linkend="sshref">ssh</link></para></listitem>
<listitem><para><link linkend="statref">stat</link></para></listitem>
<listitem><para><link linkend="straceref">strace</link></para></listitem>
<listitem><para><link linkend="stringsref">strings</link></para></listitem>
<listitem><para><link linkend="stripref">strip</link></para></listitem>
<listitem><para><link linkend="sttyref">stty</link></para></listitem>
<listitem><para><link linkend="suref">su</link></para></listitem>
<listitem><para><link linkend="sudoref">sudo</link></para></listitem>
<listitem><para><link linkend="sumref">sum</link></para></listitem>
<listitem><para><link linkend="suspendref">suspend</link></para></listitem>
<listitem><para><link linkend="swaponref">swapoff</link></para></listitem>
<listitem><para><link linkend="swaponref">swapon</link></para></listitem>
<listitem><para><link linkend="rxref">sx</link></para></listitem>
<listitem><para><link linkend="syncref">sync</link></para></listitem>
<listitem><para><link linkend="rzref">sz</link></para></listitem>
<listitem><para><link linkend="catref">tac</link></para></listitem>
<listitem><para><link linkend="tailref">tail</link></para></listitem>
<listitem><para><link linkend="tarref">tar</link></para></listitem>
<listitem><para><link linkend="tblref">tbl</link></para></listitem>
<listitem><para><link linkend="tcpdumpref">tcpdump</link></para></listitem>
<listitem><para><link linkend="teeref">tee</link></para></listitem>
<listitem><para><link linkend="telinitref">telinit</link></para></listitem>
<listitem><para><link linkend="telnetref">telnet</link></para></listitem>
<listitem><para><link linkend="texref">Tex</link></para></listitem>
<listitem><para><link linkend="texexecref">texexec</link></para></listitem>
<listitem><para><link linkend="timref">time</link></para></listitem>
<listitem><para><link linkend="timesref">times</link></para></listitem>
<listitem><para><link linkend="tmpwatchref">tmpwatch</link></para></listitem>
<listitem><para><link linkend="topref">top</link></para></listitem>
<listitem><para><link linkend="touchref">touch</link></para></listitem>
<listitem><para><link linkend="tputref">tput</link></para></listitem>
<listitem><para><link linkend="trref">tr</link></para></listitem>
<listitem><para><link linkend="tracerouteref">traceroute</link></para></listitem>
<listitem><para><link linkend="trueref">true</link></para></listitem>
<listitem><para><link linkend="tsetref">tset</link></para></listitem>
<listitem><para><link linkend="tsortref">tsort</link></para></listitem>
<listitem><para><link linkend="ttyref">tty</link></para></listitem>
<listitem><para><link linkend="tune2fsref">tune2fs</link></para></listitem>
<listitem><para><link linkend="typeref">type</link></para></listitem>
<listitem><para><link linkend="declareref">typeset</link></para></listitem>
<listitem><para><link linkend="ulimitref">ulimit</link></para></listitem>
<listitem><para><link linkend="umaskref">umask</link></para></listitem>
<listitem><para><link linkend="umountref">umount</link></para></listitem>
<listitem><para><link linkend="unameref">uname</link></para></listitem>
<listitem><para><link linkend="unarcref">unarc</link></para></listitem>
<listitem><para><link linkend="unarcref">unarj</link></para></listitem>
<listitem><para><link linkend="uncompressref">uncompress</link></para></listitem>
<listitem><para><link linkend="expandref">unexpand</link></para></listitem>
<listitem><para><link linkend="uniqref">uniq</link></para></listitem>
<listitem><para><link linkend="unitsref">units</link></para></listitem>
<listitem><para><link linkend="lzmaref">unlzma</link></para></listitem>
<listitem><para><link linkend="unarcref">unrar</link></para></listitem>
<listitem><para><link linkend="unsetref">unset</link></para></listitem>
<listitem><para><link linkend="sqref">unsq</link></para></listitem>
<listitem><para><link linkend="zipref">unzip</link></para></listitem>
<listitem><para><link linkend="uptimeref">uptime</link></para></listitem>
<listitem><para><link linkend="lsusbref">usbmodules</link></para></listitem>
<listitem><para><link linkend="useraddref">useradd</link></para></listitem>
<listitem><para><link linkend="useraddref">userdel</link></para></listitem>
<listitem><para><link linkend="usermodref">usermod</link></para></listitem>
<listitem><para><link linkend="usersref">users</link></para></listitem>
<listitem><para><link linkend="usleepref">usleep</link></para></listitem>
<listitem><para><link linkend="uucpref">uucp</link></para></listitem>
<listitem><para><link linkend="uudecoderef">uudecode</link></para></listitem>
<listitem><para><link linkend="uuencoderef">uuencode</link></para></listitem>
<listitem><para><link linkend="uuxref">uux</link></para></listitem>
<listitem><para><link linkend="vacationref">vacation</link></para></listitem>
<listitem><para><link linkend="vdirref">vdir</link></para></listitem>
<listitem><para><link linkend="vmstatref">vmstat</link></para></listitem>
<listitem><para><link linkend="vrfyref">vrfy</link></para></listitem>
<listitem><para><link linkend="wref">w</link></para></listitem>
<listitem><para><link linkend="waitref">wait</link></para></listitem>
<listitem><para><link linkend="wallref">wall</link></para></listitem>
<listitem><para><link linkend="watchref">watch</link></para></listitem>
<listitem><para><link linkend="wcref">wc</link></para></listitem>
<listitem><para><link linkend="wgetref">wget</link></para></listitem>
<listitem><para><link linkend="whatisref">whatis</link></para></listitem>
<listitem><para><link linkend="whereisref">whereis</link></para></listitem>
<listitem><para><link linkend="whichref">which</link></para></listitem>
<listitem><para><link linkend="whoref">who</link></para></listitem>
<listitem><para><link linkend="whoamiref">whoami</link></para></listitem>
<listitem><para><link linkend="whoisref">whois</link></para></listitem>
<listitem><para><link linkend="writeref">write</link></para></listitem>
<listitem><para><link linkend="xargsref">xargs</link></para></listitem>
<listitem><para><link linkend="xrandrref">xrandr</link></para></listitem>
<listitem><para><link linkend="xzref">xz</link></para></listitem>
<listitem><para><link linkend="yaccref">yacc</link></para></listitem>
<listitem><para><link linkend="yesref">yes</link></para></listitem>
<listitem><para><link linkend="zcatref">zcat</link></para></listitem>
<listitem><para><link linkend="zdiffref">zdiff</link></para></listitem>
<listitem><para><link linkend="zdumpref">zdump</link></para></listitem>
<listitem><para><link linkend="zegrepref">zegrep</link></para></listitem>
<listitem><para><link linkend="zegrepref">zfgrep</link></para></listitem>
<listitem><para><link linkend="zegrepref">zgrep</link></para></listitem>
<listitem><para><link linkend="zipref">zip</link></para></listitem>
</itemizedlist>
</partintro>
<chapter id="internal">
<title>Internal Commands and Builtins</title>
<indexterm>
<primary>builtin</primary>
</indexterm>
<para><anchor id="builtinref"/>A <firstterm>builtin</firstterm>
is a <command>command</command> contained within the Bash tool
set, literally <firstterm>built in</firstterm>. This is either
for performance reasons -- builtins execute faster than external
commands, which usually require <firstterm>forking off</firstterm>
<footnote><para>As Nathan Coulter points out, "while forking a
process is a low-cost operation, executing a new program in
the newly-forked child process adds more
overhead."</para></footnote>
a separate process -- or because a particular builtin needs
direct access to the shell internals.</para>
<para><anchor id="forkref"/></para>
<sidebar>
<para><anchor id="parentref"/>When a command or
the shell itself initiates (or
<firstterm>spawns</firstterm>) a new
subprocess to carry out a task, this is called
<firstterm>forking</firstterm>. This new process
is the <firstterm>child</firstterm>, and the process
that <firstterm>forked</firstterm> it off is the
<firstterm>parent</firstterm>. While the <firstterm>child
process</firstterm> is doing its work, the
<firstterm>parent process</firstterm> is still
executing.</para>
<para>Note that while a <firstterm>parent
process</firstterm> gets the <firstterm>process
ID</firstterm> of the <firstterm>child
process</firstterm>, and can thus pass arguments to it,
<emphasis>the reverse is not true</emphasis>. <link
linkend="parchildprobref">This can create problems
that are subtle and hard to track down.</link></para>
<example id="spawnscr">
<title>A script that spawns multiple instances of itself</title>
<programlisting>&spawnscr;</programlisting>
</example>
<para><anchor id="bltinfrk"/></para>
<para>Generally, a Bash <firstterm>builtin</firstterm>
does not fork a subprocess when it executes within
a script. An external system command or filter in
a script usually <emphasis>will</emphasis> fork a
subprocess.</para>
</sidebar>
<para>A builtin may be a synonym to a system command of the same
name, but Bash reimplements it internally. For example,
the Bash <command>echo</command> command is not the same as
<filename>/bin/echo</filename>, although their behavior is
almost identical.
<programlisting>#!/bin/bash
echo "This line uses the \"echo\" builtin."
/bin/echo "This line uses the /bin/echo system command."</programlisting>
</para>
<para><anchor id="keywordref"/>A <firstterm>keyword</firstterm>
is a <firstterm>reserved</firstterm> 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, <firstterm>for</firstterm>,
<firstterm>while</firstterm>, <firstterm>do</firstterm>,
and <firstterm>!</firstterm> are keywords. Similar to a <link
linkend="builtinref">builtin</link>, a keyword is hard-coded into
Bash, but unlike a <firstterm>builtin</firstterm>, a keyword is
not in itself a command, but <emphasis>a subunit of a command
construct</emphasis>.
<footnote><para>An exception to this is the <link
linkend="timref">time</link> command, listed in the
official Bash documentation as a keyword (<quote>reserved
word</quote>).</para></footnote>
</para>
<variablelist id="intio">
<title><anchor id="intio1"/>I/O</title>
<varlistentry>
<term><anchor id="echoref"/><command>echo</command></term>
<listitem>
<indexterm>
<primary>echo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>echo</secondary>
</indexterm>
<para>prints (to <filename>stdout</filename>) an expression
or variable (see <xref linkend="ex9"/>).
<programlisting>echo Hello
echo $a</programlisting></para>
<para>An <command>echo</command> requires the
<option>-e</option> option to print escaped characters. See
<xref linkend="escaped"/>.</para>
<para>Normally, each <command>echo</command> command prints
a terminal newline, but the <option>-n</option> option
suppresses this.</para>
<para><anchor id="echogrepref"/></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>
<para><anchor id="echocs"/></para>
<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>
<para>Be aware that <command>echo `command`</command>
deletes any linefeeds that the output
of <replaceable>command</replaceable>
generates.</para>
<para>The <link linkend="ifsref">$IFS</link> (internal field
separator) variable normally contains
<token>\n</token> (linefeed) as one of its set of
<link linkend="whitespaceref">whitespace</link>
characters. Bash therefore splits the output of
<replaceable>command</replaceable> at linefeeds
into arguments to <command>echo</command>. Then
<command>echo</command> outputs these arguments,
separated by spaces.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>ls -l /usr/share/apps/kjezz/sounds</userinput>
<computeroutput>-rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au
-rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au</computeroutput>
<prompt>bash$ </prompt><userinput>echo `ls -l /usr/share/apps/kjezz/sounds`</userinput>
<computeroutput>total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root ...</computeroutput>
</screen>
</para>
<para>
So, how can we embed a linefeed within an
<link linkend="echoref">echoed</link> character string?
<programlisting># Embedding a linefeed?
echo "Why doesn't this string \n split on two lines?"
# Doesn't split.
# Let's try something else.
echo
echo $"A line of text containing
a linefeed."
# Prints as two distinct lines (embedded linefeed).
# But, is the "$" variable prefix really necessary?
echo
echo "This string splits
on two lines."
# No, the "$" is not needed.
echo
echo "---------------"
echo
echo -n $"Another line of text containing
a linefeed."
# Prints as two distinct lines (embedded linefeed).
# Even the -n option fails to suppress the linefeed here.
echo
echo
echo "---------------"
echo
echo
# However, the following doesn't work as expected.
# Why not? Hint: Assignment to a variable.
string1=$"Yet another line of text containing
a linefeed (maybe)."
echo $string1
# Yet another line of text containing a linefeed (maybe).
# ^
# Linefeed becomes a space.
# Thanks, Steve Parker, for pointing this out.</programlisting>
</para>
<para><anchor id="binecho"/></para>
<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><anchor id="printfref"/><command>printf</command></term>
<listitem>
<indexterm>
<primary>printf</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>printf</secondary>
</indexterm>
<para>The <command>printf</command>, formatted print, command is an
enhanced <command>echo</command>. It is a limited variant
of the <firstterm>C</firstterm> language
<function>printf()</function> library function, and its
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 <firstterm>builtin</firstterm> version
of the <filename>/bin/printf</filename> or
<filename>/usr/bin/printf</filename> command. See the
<command>printf</command> <link
linkend="manref">manpage</link> (of the system command)
for in-depth coverage.</para>
<caution><para>Older versions of Bash may not support
<command>printf</command>.</para></caution>
<example id="ex47">
<title><firstterm>printf</firstterm> in action</title>
<programlisting>&ex47;</programlisting>
</example>
<para>Formatting error messages is a useful application of
<command>printf</command></para>
<para>
<programlisting>E_BADDIR=85
var=nonexistent_directory
error()
{
printf "$@" >&amp;2
# Formats positional params passed, and sends them to stderr.
echo
exit $E_BADDIR
}
cd $var || error $"Can't cd to %s." "$var"
# Thanks, S.C.</programlisting>
</para>
<para>See also <xref linkend="progressbar"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="readref"/><command>read</command></term>
<listitem>
<indexterm>
<primary>read</primary>
</indexterm> <indexterm>
<primary>command</primary> <secondary>read</secondary>
</indexterm>
<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 <firstterm>read</firstterm></title>
<programlisting>&ex36;</programlisting>
</example>
<para>A <command>read</command> without an associated variable
assigns its input to the dedicated variable <link
linkend="replyref">$REPLY</link>.</para>
<example id="readnovar">
<title>What happens when <firstterm>read</firstterm> has no
variable</title>
<programlisting>&readnovar;</programlisting>
</example>
<para>Normally, inputting a <userinput>\</userinput>
suppresses a newline during input to
a <command>read</command>. The <option>-r</option>
option causes an inputted <userinput>\</userinput> to be
interpreted literally.</para>
<example id="readr">
<title>Multi-line input to <firstterm>read</firstterm></title>
<programlisting>&readr;</programlisting>
</example>
<para><anchor id="readoptions"/></para>
<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><anchor id="readarrow"/></para>
<para>The <option>-n</option> option to <command>read</command>
also allows detection of the <keycap>arrow keys</keycap>
and certain of the other unusual keys.</para>
<example id="arrowdetect">
<title>Detecting the arrow keys</title>
<programlisting>&arrowdetect;</programlisting>
</example>
<note><para>The <option>-n</option> option to <command>read</command>
will not detect the <keycap>ENTER</keycap> (newline)
key.</para></note>
<para><anchor id="readtimed"/></para>
<para>The <option>-t</option> option to <command>read</command>
permits timed input (see <xref linkend="tout"/> and <xref
linkend="qky"/>).</para>
<para><anchor id="readfd"/>The <option>-u</option> option
takes the <link linkend="fdref">file descriptor</link>
of the target file.</para>
<para><anchor id="readredir0"/></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 <firstterm>read</firstterm> with
<link linkend="ioredirref">file redirection</link></title>
<programlisting>&readredir;</programlisting>
</example>
<note>
<para><anchor id="pipereadref0"/></para>
<para><link linkend="piperef">Piping</link> output
to a <firstterm>read</firstterm>, using <link
linkend="echoref">echo</link> to set variables <link
linkend="badread0">will fail</link>.</para>
<para><anchor id="readpiperef"/>Yet, piping the output of <link
linkend="catref">cat</link> <emphasis>seems</emphasis> to
work.</para>
<para><anchor id="whilereadref"/></para>
<para><programlisting>cat file1 file2 |
while read line
do
echo $line
done</programlisting></para>
<para>However, as Bj&ouml;n Eriksson shows:</para>
<example id="readpipe">
<title>Problems reading from a pipe</title>
<programlisting>&readpipe;</programlisting>
</example>
<para>The <firstterm>gendiff</firstterm> script, usually
found in <filename>/usr/bin</filename> on
many Linux distros, pipes the output of <link
linkend="findref">find</link> to a <firstterm>while
read</firstterm> construct.
<programlisting>find $1 \( -name "*$2" -o -name ".*$2" \) -print |
while read f; do
. . .</programlisting>
</para>
</note>
<tip>
<para>It is possible to <firstterm>paste</firstterm> text into
the input field of a <firstterm>read</firstterm> (but
<emphasis>not</emphasis> multiple lines!). See <xref
linkend="padsw"/>.</para>
</tip>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intfilesystem">
<title><anchor id="intfilesystem1"/>Filesystem</title>
<varlistentry>
<term><anchor id="cdref"/><command>cd</command></term>
<listitem>
<indexterm>
<primary>cd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cd</secondary>
</indexterm>
<para>The familiar <command>cd</command> change directory
command finds use in scripts where execution of a command
requires being in a specified directory.</para>
<para>
<programlisting>(cd /source/directory &amp;&amp; tar cf - . ) | (cd /dest/directory &amp;&amp; tar xpvf -)</programlisting>
[from the <link linkend="coxex">previously cited</link>
example by Alan Cox]</para>
<para>The <option>-P</option> (physical) option to
<command>cd</command> causes it to ignore symbolic
links.</para>
<para><command>cd -</command> changes to <link
linkend="oldpwd">$OLDPWD</link>, the previous working
directory.</para>
<para><anchor id="doubleslashref"/></para>
<caution><para>The <command>cd</command> command does not function
as expected when presented with two forward slashes.
<screen>
<prompt>bash$ </prompt><userinput>cd //</userinput>
<prompt>bash$ </prompt><userinput>pwd</userinput>
<computeroutput>//</computeroutput>
</screen>
The output should, of course, be <computeroutput>/</computeroutput>.
This is a problem both from the command-line and in a script.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pwd2ref"/><command>pwd</command></term>
<listitem>
<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>
<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>
<listitem>
<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>
<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 <link
linkend="stackdefref">stack</link> 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 (to the <firstterm>top</firstterm> of the 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 the directory now at the <firstterm>top</firstterm> of
the stack.</para>
<para><anchor id="dirsref"/><command>dirs</command> lists
the contents of the directory stack (compare this
with the <link linkend="dirstackref">$DIRSTACK</link>
variable). A successful <command>pushd</command>
or <command>popd</command> will automatically invoke
<command>dirs</command>.</para>
<para>Scripts that require various changes to the current
working directory without hard-coding the directory name
changes can make good use of these commands. Note that
the implicit <varname>$DIRSTACK</varname> array variable,
accessible from within a script, holds the contents of
the directory stack.
</para>
<example id="ex37">
<title>Changing the current working directory</title>
<programlisting>&ex37;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intvar">
<title><anchor id="intvar1"/>Variables</title>
<varlistentry>
<term><anchor id="letref"/><command>let</command></term>
<listitem>
<indexterm>
<primary>let</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>let</secondary>
</indexterm>
<para>The <command>let</command> command carries out
<firstterm>arithmetic</firstterm> operations on variables.
<footnote><para>Note that <firstterm>let</firstterm>
<link linkend="letbad">cannot be used
for setting <firstterm>string</firstterm>
variables.</link></para></footnote>
In many cases, it functions as a less complex version
of <link linkend="exprref">expr</link>.</para>
<example id="ex46">
<title>Letting <firstterm>let</firstterm> do arithmetic.</title>
<programlisting>&ex46;</programlisting>
</example>
<para><anchor id="exitvalanomaly02"/></para>
<caution>
<para>The <firstterm>let</firstterm> command can,
in certain contexts, return a surprising <link
linkend="exitstatusref">exit status</link>.</para>
<para><programlisting># Evgeniy Ivanov points out:
var=0
echo $? # 0
# As expected.
let var++
echo $? # 1
# The command was successful, so why isn't $?=0 ???
# Anomaly!
let var++
echo $? # 0
# As expected.
# Likewise . . .
let var=0
echo $? # 1
# The command was successful, so why isn't $?=0 ???
# However, as Jeff Gorak points out,
#+ this is part of the design spec for 'let' . . .
# "If the last ARG evaluates to 0, let returns 1;
# let returns 0 otherwise." ['help let']</programlisting></para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="evalref"/><command>eval</command></term>
<listitem>
<indexterm>
<primary>eval</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>eval</secondary>
</indexterm>
<para><userinput>eval arg1 [arg2] ... [argN]</userinput></para>
<para>Combines the arguments in an expression or list of
expressions and <replaceable>evaluates</replaceable> them.
Any variables within the expression are expanded. The
net result is to <command>convert a string into a
command</command>.</para>
<tip><para>The <command>eval</command> command can be used for
code generation from the command-line or within a script.
</para></tip>
<para>
<screen>
<prompt>bash$ </prompt><userinput>command_string="ps ax"</userinput>
<prompt>bash$ </prompt><userinput>process="ps ax"</userinput>
<prompt>bash$ </prompt><userinput>eval "$command_string" | grep "$process"</userinput>
<computeroutput>26973 pts/3 R+ 0:00 grep --color ps ax
26974 pts/3 R+ 0:00 ps ax</computeroutput>
</screen>
</para>
<para><anchor id="evalforced"/></para>
<para>Each invocation of <firstterm>eval</firstterm> forces
a re-<emphasis>evaluation</emphasis> of its arguments.
<programlisting>a='$b'
b='$c'
c=d
echo $a # $b
# First level.
eval echo $a # $c
# Second level.
eval eval echo $a # d
# Third level.
# Thank you, E. Choroba.</programlisting></para>
<para><anchor id="evaleff"/></para>
<example id="ex43">
<title>Showing the effect of <firstterm>eval</firstterm></title>
<programlisting>&ex43;</programlisting>
</example>
<para><anchor id="arrchoice0"/></para>
<example id="arrchoice">
<title>Using <firstterm>eval</firstterm> to select
among variables</title>
<programlisting>&arrchoice;</programlisting>
</example>
<example id="echoparams">
<title><firstterm>Echoing</firstterm> the
<firstterm>command-line parameters</firstterm></title>
<programlisting>&echoparams;</programlisting>
</example>
<example id="ex44">
<title>Forcing a log-off</title>
<programlisting>&ex44;</programlisting>
</example>
<example id="rot14">
<title>A version of <firstterm>rot13</firstterm></title>
<programlisting>&rot14;</programlisting>
</example>
<para>Here is another example of using
<firstterm>eval</firstterm> to
<emphasis>evaluate</emphasis> a complex expression,
this one from an earlier version of YongYe's <ulink
url="https://github.com/yongye/shell/blob/master/Tetris_Game.sh">Tetris
game script</ulink>.</para>
<para>
<programlisting>eval ${1}+=\"${x} ${y} \"</programlisting>
</para>
<para><xref linkend="samorse"/> uses
<firstterm>eval</firstterm> to convert <link
linkend="arrayref">array</link> elements into a command
list.</para>
<para>The <firstterm>eval</firstterm> command occurs
in the older version of <link linkend="ivrref">indirect
referencing</link>.
<programlisting>eval var=\$$var</programlisting>
</para>
<tip><para>The <firstterm>eval</firstterm> command can
be used to <link linkend="braceexpref3">parameterize
<firstterm>brace expansion</firstterm></link>.</para></tip>
<para><anchor id="evalrisk"/></para>
<caution><para>The <command>eval</command> command can be
risky, and normally should be avoided when there
exists a reasonable alternative. An <userinput>eval
$COMMANDS</userinput> executes the contents of
<replaceable>COMMANDS</replaceable>, which may
contain such unpleasant surprises as <command>rm -rf
*</command>. Running an <command>eval</command> on
unfamiliar code written by persons unknown is living
dangerously.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="setref"/><command>set</command></term>
<listitem>
<indexterm>
<primary>set</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>set</secondary>
</indexterm>
<para>The <command>set</command> command changes
the value of internal script variables/options. 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
<link linkend="fieldref">fields</link> of the command
output.</para>
<example id="ex34">
<title>Using <firstterm>set</firstterm> with positional
parameters</title>
<programlisting>&ex34;</programlisting>
</example>
<para>More fun with positional parameters.</para>
<example id="revposparams">
<title>Reversing the positional parameters</title>
<programlisting>&revposparams;</programlisting>
</example>
<para>Invoking <command>set</command> without any options or
arguments simply lists all the <link
linkend="envref">environmental</link> and other variables
that have been initialized.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>set</userinput>
<computeroutput>AUTHORCOPY=/home/bozo/posts
BASH=/bin/bash
BASH_VERSION=$'2.05.8(1)-release'
...
XAUTHORITY=/home/bozo/.Xauthority
_=/etc/bashrc
variable22=abc
variable23=xzy</computeroutput>
</screen>
</para>
<para>Using <command>set</command> with the <option>--</option>
option explicitly assigns the contents of a variable to
the positional parameters. If no variable follows the
<option>--</option> it <firstterm>unsets</firstterm>
the positional parameters.</para>
<example id="setpos">
<title>Reassigning the positional parameters</title>
<programlisting>&setpos;</programlisting>
</example>
<para>See also <xref linkend="ex22a"/> and <xref linkend="ex33a"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="unsetref"/><command>unset</command></term>
<listitem>
<indexterm>
<primary>unset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unset</secondary>
</indexterm>
<para>The <command>unset</command> command deletes a
shell variable, effectively setting it to
<firstterm>null</firstterm>. 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>
<note><para>In most contexts, an <firstterm>undeclared</firstterm>
variable and one that has been <firstterm>unset</firstterm>
are equivalent. However, the <link linkend="unddr">
${parameter:-default}</link> parameter substitution
construct can distinguish between the two.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exportref"/><command>export</command></term>
<listitem>
<indexterm>
<primary>export</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>export</secondary>
</indexterm>
<para><anchor id="exportref2"/></para>
<para>The <command>export</command>
<footnote><para>To <firstterm>Export</firstterm>
information is to make it available in a more general context.
See also <link
linkend="scoperef">scope</link>.</para></footnote>
command makes available variables to all child processes
of the running script or shell. One important use
of the <command>export</command> command is in <link
linkend="filesref1">startup files</link>, to initialize
and make accessible <link linkend="envref">environmental
variables</link> to subsequent user processes.</para>
<caution><para>Unfortunately, <link linkend="parchildprobref">
there is no way to export variables back to the parent
process</link>, to the process that called or invoked the
script or shell.</para></caution>
<para><anchor id="exportawk"/></para>
<example id="coltotaler3">
<title>Using <firstterm>export</firstterm> to pass a variable to an
embedded <firstterm>awk</firstterm> 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>
<para>However, as Greg Keraunen points out, in certain
situations this may have a different effect than
setting a variable, then exporting it.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>export var=(a b); echo ${var[0]}</userinput>
<computeroutput>(a b)</computeroutput>
<prompt>bash$ </prompt><userinput>var=(a b); export var; echo ${var[0]}</userinput>
<computeroutput>a</computeroutput>
</screen>
</para>
</tip>
<note>
<para>A variable to be exported may require special
treatment. See <xref linkend="bashprof"/>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="declare2ref"/><command>declare</command></term>
<term><command>typeset</command></term>
<listitem>
<indexterm>
<primary>declare</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>declare</secondary>
</indexterm>
<indexterm>
<primary>typeset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>typeset</secondary>
</indexterm>
<para>The <link linkend="declareref">declare</link> and
<link linkend="declareref">typeset</link> commands specify
and/or restrict properties of variables.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="readonlyref"/><command>readonly</command></term>
<listitem>
<indexterm>
<primary>readonly</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>readonly</secondary>
</indexterm>
<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
<firstterm>C</firstterm> language <command>const</command>
type qualifier.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="getoptsx"/><command>getopts</command></term>
<listitem>
<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>
<para>This powerful tool parses command-line arguments passed
to the script. This is the Bash analog of the <link
linkend="getopty">getopt</link> external command and the
<firstterm>getopt</firstterm> library function familiar to
<firstterm>C</firstterm> programmers. It permits passing
and concatenating multiple options
<footnote><para>An <firstterm>option</firstterm> 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><anchor id="getoptsopt"/></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 increments the implicit
<varname>$OPTIND</varname> variable to point to the
next.</para>
<note>
<para>
<orderedlist>
<listitem>
<para>The arguments passed from the command-line to
the script must be preceded by a
dash (<option>-</option>). It is the
prefixed <option>-</option> that lets
<command>getopts</command> recognize command-line
arguments as <firstterm>options</firstterm>.
In fact, <command>getopts</command> will not process
arguments without the prefixed <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 <link
linkend="whileloopref">while loop</link>, in that
it lacks condition brackets.</para>
</listitem>
<listitem>
<para>The <command>getopts</command> construct is a highly
functional replacement for the traditional
<link linkend="getopty">getopt</link> external
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 <firstterm>getopts</firstterm> to read the
options/arguments passed to a script</title>
<programlisting>&ex33;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intscrbeh">
<title><anchor id="intscrbeh1"/>Script Behavior</title>
<varlistentry>
<term><anchor id="sourceref"/><command>source</command></term>
<term><token>.</token> (<link linkend="dotref">dot</link> command)</term>
<listitem>
<indexterm>
<primary>source</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>source</secondary>
</indexterm>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>.</secondary>
</indexterm>
<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>.
<firstterm>Sourcing</firstterm> a file (dot-command)
<firstterm>imports</firstterm>
code into the script, appending to the script (same effect
as the <userinput>#include</userinput> directive in a
<firstterm>C</firstterm> program). The net result is the
same as if the <quote>sourced</quote> lines of code were
physically present in the body of the script. This is useful
in situations when multiple scripts use a common data file
or function library.</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>
<para>If the <firstterm>sourced</firstterm> file is itself
an executable script, then it will run, then return
control to the script that called it. A
<firstterm>sourced</firstterm> executable script may use a
<link linkend="returnref">return</link> for this
purpose.</para>
<para><anchor id="sourceparams"/></para>
<para>
Arguments may be (optionally) passed to the
<firstterm>sourced</firstterm> file as <link
linkend="posparamref1">positional parameters</link>.
<programlisting>source $filename $arg1 arg2</programlisting>
</para>
<para>It is even possible for a script to
<firstterm>source</firstterm> itself, though this does not
seem to have any practical applications.</para>
<example id="selfsource">
<title>A (useless) script that sources itself</title>
<programlisting>&selfsource;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exitref"/><command>exit</command></term>
<listitem>
<indexterm>
<primary>exit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exit</secondary>
</indexterm>
<para>Unconditionally terminates a script.
<footnote><para>Technically, an
<command>exit</command> only terminates the
process (or shell) in which it is running,
<emphasis>not</emphasis> the <firstterm>parent
process</firstterm>.</para></footnote>
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 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>. This is equivalent to an
<command>exit $?</command>.</para></note>
<note><para>An <command>exit</command> command may also be used to
terminate a <link
linkend="subshellsref">subshell</link>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="execref"/><command>exec</command></term>
<listitem>
<indexterm>
<primary>exec</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>exec</secondary>
</indexterm>
<para>
This shell builtin replaces the current process with
a specified command. Normally, when the shell encounters
a command, it <link linkend="forkref">forks off</link> a
child process to actually execute the command. Using the
<command>exec</command> builtin, the shell does not fork,
and the command <firstterm>exec</firstterm>'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.
<footnote><para>Unless the <command>exec</command> is used
to <link linkend="usingexecref">reassign file
descriptors</link>.</para></footnote>
</para>
<example id="ex54">
<title>Effects of <firstterm>exec</firstterm></title>
<programlisting>&ex54;</programlisting>
</example>
<example id="selfexec">
<title>A script that <firstterm>exec's</firstterm> itself</title>
<programlisting>&selfexec;</programlisting>
</example>
<para>An <command>exec</command> also serves to <link
linkend="usingexecref">reassign
file descriptors</link>. For example, <userinput>exec
&lt;zzz-file</userinput> replaces <filename>stdin</filename>
with the file <filename>zzz-file</filename>.</para>
<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="shoptref"/><command>shopt</command></term>
<listitem>
<indexterm>
<primary>shopt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>shopt</secondary>
</indexterm>
<para>This command permits changing <firstterm>shell
options</firstterm> on the fly (see <xref linkend="al"/>
and <xref linkend="unal"/>). It often appears in the Bash
<link linkend="filesref1">startup files</link>, but also has
its uses in scripts. Needs <link linkend="bash2ref">version
2</link> or later of Bash.</para>
<para><programlisting>shopt -s cdspell
# Allows minor misspelling of directory names with 'cd'
# Option -s sets, -u unsets.
cd /hpme # Oops! Mistyped '/home'.
pwd # /home
# The shell corrected the misspelling.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>caller</command><anchor id="callerref"/></term>
<listitem>
<indexterm>
<primary>caller</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>caller</secondary>
</indexterm>
<para>Putting a <command>caller</command> command
inside a <link linkend="functionref">function</link>
echoes to <filename>stdout</filename> information about
the <firstterm>caller</firstterm> of that function.</para>
<para><programlisting>#!/bin/bash
function1 ()
{
# Inside function1 ().
caller 0 # Tell me about it.
}
function1 # Line 9 of script.
# 9 main test.sh
# ^ Line number that the function was called from.
# ^^^^ Invoked from "main" part of script.
# ^^^^^^^ Name of calling script.
caller 0 # Has no effect because it's not inside a function.</programlisting></para>
<para>A <command>caller</command> command can also return
<firstterm>caller</firstterm> information from a script <link
linkend="sourceref">sourced</link> within another
script. Analogous to a function, this is a <quote>subroutine
call.</quote></para>
<para>You may find this command useful in debugging.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="intcommand">
<title><anchor id="intcommand1"/>Commands</title>
<varlistentry>
<term><anchor id="trueref"/><command>true</command></term>
<listitem>
<indexterm>
<primary>true</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>true</secondary>
</indexterm>
<para>A command that returns a successful
(<returnvalue>zero</returnvalue>) <link
linkend="exitstatusref">exit status</link>, but does
nothing else.
</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>true</userinput>
<prompt>bash$ </prompt><userinput>echo $?</userinput>
<computeroutput>0</computeroutput>
</screen>
</para>
<para><programlisting># Endless loop
while true # alias for ":"
do
operation-1
operation-2
...
operation-n
# Need a way to break out of loop or script will hang.
done</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="falseref"/><command>false</command></term>
<listitem>
<indexterm>
<primary>false</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>false</secondary>
</indexterm>
<para>A command that returns an unsuccessful <link
linkend="exitstatusref">exit status</link>,
but does nothing else.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>false</userinput>
<prompt>bash$ </prompt><userinput>echo $?</userinput>
<computeroutput>1</computeroutput>
</screen>
</para>
<para><programlisting># Testing "false"
if false
then
echo "false evaluates \"true\""
else
echo "false evaluates \"false\""
fi
# false evaluates "false"
# Looping while "false" (null loop)
while false
do
# The following code will not execute.
operation-1
operation-2
...
operation-n
# Nothing happens!
done
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="typeref"/><command>type [cmd]</command></term>
<listitem>
<indexterm>
<primary>type</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>type</secondary>
</indexterm>
<indexterm>
<primary>variable</primary>
<secondary>which</secondary>
</indexterm>
<para>Similar to the <link
linkend="whichref">which</link> external command,
<command>type cmd</command> identifies
<quote>cmd.</quote> Unlike <command>which</command>,
<command>type</command> is a Bash builtin. The useful
<option>-a</option> option to <command>type</command>
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>
<prompt>bash$ </prompt><userinput>type type</userinput>
<computeroutput>type is a shell builtin</computeroutput>
</screen>
</para>
<para>The <command>type</command> command can be useful
for <link linkend="devnullredirect">testing whether a
certain command exists</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hashcmdref"/><command>hash [cmds]</command></term>
<listitem>
<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>
<para>Records the <firstterm>path</firstterm>
name of specified commands -- in the shell <firstterm>hash
table</firstterm>
<footnote>
<para><anchor id="hashref"/></para>
<para><firstterm>Hashing</firstterm> is a method of
creating lookup keys for data stored in a table. The
<emphasis>data items themselves</emphasis> are
<quote>scrambled</quote> to create keys, using one of
a number of simple mathematical
<firstterm>algorithms</firstterm> (methods, or
recipes).</para>
<para>An advantage of <firstterm>hashing</firstterm> is that
it is fast. A disadvantage is that
<firstterm>collisions</firstterm> -- where a single key
maps to more than one data item -- are possible.</para>
<para>For examples of hashing see <xref linkend="hashlib"/> and
<xref linkend="hashexample"/>.</para>
</footnote>
-- so the shell or script will not need to search the
<link linkend="pathref">$PATH</link> 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><anchor id="bindref"/><command>bind</command></term>
<listitem>
<indexterm>
<primary>bind</primary>
</indexterm>
<indexterm>
<primary>bind</primary>
<secondary>key bindings </secondary>
</indexterm>
<para>The <command>bind</command> builtin displays or modifies
<firstterm>readline</firstterm>
<footnote><para><anchor id="readlineref"/>The
<firstterm>readline</firstterm> library is what
Bash uses for reading input in an
interactive shell.</para></footnote>
key bindings.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="helpref"/><command>help</command></term>
<listitem>
<indexterm>
<primary>help</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary></secondary>
</indexterm>
<para>Gets a short usage summary of a shell builtin. This is
the counterpart to <link linkend="whatisref">whatis</link>,
but for builtins. The display of <firstterm>help</firstterm>
information got a much-needed update in the <link
linkend="bash4ref">version 4 release</link> of Bash.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>help exit</userinput>
<computeroutput>exit: exit [n]
Exit the shell with a status of N. If N is omitted, the exit status
is that of the last command executed.</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<sect1>
<title>Job Control Commands</title>
<para>Certain of the following job control commands take a
<firstterm>job identifier</firstterm> as an argument. See
the <link linkend="jobidtable">table</link> at end of the
chapter.</para>
<variablelist id="jccommandlist">
<varlistentry>
<term><anchor id="jobsref"/><command>jobs</command></term>
<listitem>
<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>
<para>Lists the jobs running in the background, giving
the <firstterm>job number</firstterm>.
Not as useful as <link linkend="ppssref">ps</link>.</para>
<note>
<para>It is all too easy to confuse
<firstterm>jobs</firstterm> and
<firstterm>processes</firstterm>. 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 <link linkend="fgref">fg</link>,
<link linkend="bgref">bg</link> and <command>jobs</command>
commands accept only a job number.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>sleep 100 &amp;</userinput>
<computeroutput>[1] 1384</computeroutput>
<prompt>bash $ </prompt><userinput>jobs</userinput>
<computeroutput>[1]+ Running sleep 100 &amp;</computeroutput></screen>
</para>
<para><quote>1</quote> is the job number (jobs are
maintained by the current shell). <quote>1384</quote>
is the <link linkend="ppidref">PID</link> or <firstterm>process ID
number</firstterm> (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="disownref"/><command>disown</command></term>
<listitem>
<indexterm>
<primary>disown</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>disown</secondary>
</indexterm>
<para>Remove job(s) from the shell's table of active jobs.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fgref"/><command>fg</command></term>
<term><anchor id="bgref"/><command>bg</command></term>
<listitem>
<indexterm>
<primary>fg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>foreground</secondary>
</indexterm>
<indexterm>
<primary>background</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bg</secondary>
</indexterm>
<para>The <command>fg</command> command switches a job
running in the background into the foreground. The
<command>bg</command> command restarts a suspended job, and
runs it in the background. If no job number is specified,
then the <command>fg</command> or <command>bg</command>
command acts upon the currently running job.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="waitref"/><command>wait</command></term>
<listitem>
<indexterm>
<primary>wait</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wait</secondary>
</indexterm>
<para>Suspend 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
<link linkend="zombieref">orphan process</link>).</para>
<example id="ex39">
<title>Waiting for a process to finish before proceeding</title>
<programlisting>&ex39;</programlisting>
</example>
<para>Optionally, <command>wait</command> can take a <firstterm>job
identifier</firstterm> as an argument, for example,
<replaceable>wait%1</replaceable> or <replaceable>wait
$PPID</replaceable>.
<footnote><para>This only applies to <firstterm>child
processes</firstterm>, of course.</para></footnote>
See the <link linkend="jobidtable">job id table</link>.</para>
<para><anchor id="waithang"/></para>
<tip>
<para>Within a script, running a command in the background
with an ampersand (&amp;) may cause the script
to hang until <keycap>ENTER</keycap> is hit. This
seems to occur with commands that write to
<filename>stdout</filename>. It can be a major annoyance.
<programlisting>#!/bin/bash
# test.sh
ls -l &amp;
echo "Done."</programlisting>
<screen><prompt>bash$ </prompt><userinput>./test.sh</userinput>
<computeroutput>Done.
[bozo@localhost test-scripts]$ total 1
-rwxr-xr-x 1 bozo bozo 34 Oct 11 15:09 test.sh
_
</computeroutput>
</screen>
</para>
<blockquote>
<literallayout>
As Walter Brameld IV explains it:
As far as I can tell, such scripts don't actually hang. It just
seems that they do because the background command writes text to
the console after the prompt. The user gets the impression that
the prompt was never displayed. Here's the sequence of events:
1. Script launches background command.
2. Script exits.
3. Shell displays the prompt.
4. Background command continues running and writing text to the
console.
5. Background command finishes.
6. User doesn't see a prompt at the bottom of the output, thinks script
is hanging.
</literallayout>
</blockquote>
<para>Placing a <command>wait</command> after the background
command seems to remedy this.
<programlisting>#!/bin/bash
# test.sh
ls -l &amp;
echo "Done."
wait</programlisting>
<screen><prompt>bash$ </prompt><userinput>./test.sh</userinput>
<computeroutput>Done.
[bozo@localhost test-scripts]$ total 1
-rwxr-xr-x 1 bozo bozo 34 Oct 11 15:09 test.sh</computeroutput>
</screen>
<link linkend="ioredirref">Redirecting</link> the
output of the command to a file or even to
<filename>/dev/null</filename> also takes care of this
problem.
</para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="suspendref"/><command>suspend</command></term>
<listitem>
<indexterm>
<primary>suspend</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>suspend</secondary>
</indexterm>
<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><anchor id="logoutref"/><command>logout</command></term>
<listitem>
<indexterm>
<primary>logout</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>log out</secondary>
</indexterm>
<para>Exit a login shell, optionally specifying an <link
linkend="exitstatusref">exit status</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="timesref"/><command>times</command></term>
<listitem>
<indexterm>
<primary>times</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>times</secondary>
</indexterm>
<para>Gives statistics on the system time elapsed when
executing commands, in the following form:
<screen><computeroutput>0m0.020s 0m0.020s</computeroutput></screen></para>
<para>This capability is of relatively limited value, since it is not common to
profile and benchmark shell scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="killref"/><command>kill</command></term>
<listitem>
<indexterm>
<primary>kill</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>kill</secondary>
</indexterm>
<para>Forcibly terminate a process by sending it an
appropriate <firstterm>terminate</firstterm> signal
(see <xref linkend="killprocess"/>).</para>
<example id="selfdestruct">
<title>A script that kills itself</title>
<programlisting>&selfdestruct;</programlisting>
</example>
<para><anchor id="zombieref"/></para>
<note><para><userinput>kill -l</userinput> lists all the
<link linkend="signald">signals</link> (as does the
file <filename>/usr/include/asm/signal.h</filename>).
A <userinput>kill -9</userinput> is a <firstterm>sure
kill</firstterm>, 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 <firstterm>zombie</firstterm> process,
that is, a child process that has terminated, but that
the <link linkend="forkref">parent process</link>
has not (yet) killed, cannot be killed by a logged-on
user -- you can't kill something that is already dead --
but <command>init</command> will generally clean it up
sooner or later.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="killallref"/><command>killall</command></term>
<listitem>
<indexterm>
<primary>killall</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>kill</secondary>
</indexterm>
<para>The <command>killall</command> command
kills a running process by <firstterm>name</firstterm>,
rather than by <link linkend="processidref">process ID</link>.
If there are multiple instances of a particular command running,
then doing a <firstterm>killall</firstterm> on that command will
terminate them <emphasis>all</emphasis>.</para>
<note><para>This refers to the <command>killall</command>
command in <filename class="directory">/usr/bin</filename>,
<emphasis>not</emphasis> the <link
linkend="killall2ref">killall script</link> in <filename
class="directory">/etc/rc.d/init.d</filename>.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="commandref"/><command>command</command></term>
<listitem>
<indexterm>
<primary>command</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>command</secondary>
</indexterm>
<para>The <command>command</command> directive
disables aliases and functions for the command immediately
following it.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>command ls</userinput>
</screen>
</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>
<listitem>
<indexterm>
<primary>builtin</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>builtin</secondary>
</indexterm>
<para>Invoking <command>builtin
BUILTIN_COMMAND</command> runs the command
<replaceable>BUILTIN_COMMAND</replaceable> 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>
<listitem>
<indexterm>
<primary>enable</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>enable</secondary>
</indexterm>
<para>This either enables or disables a shell
builtin command. As an example, <replaceable>enable -n
kill</replaceable> disables the shell builtin <link
linkend="killref">kill</link>, so that when Bash
subsequently encounters <firstterm>kill</firstterm>, it invokes
the external command <filename>/bin/kill</filename>.</para>
<para><anchor id="enableref1"/>The <option>-a</option>
option to <firstterm>enable</firstterm> lists all the
shell builtins, indicating whether or not they
are enabled. The <option>-f filename</option>
option lets <firstterm>enable</firstterm> load a <link
linkend="builtinref">builtin</link> as a shared library
(DLL) module from a properly compiled object file.
<footnote>
<para>The C source for a number of loadable builtins is
typically found in the <filename
class="directory">/usr/share/doc/bash-?.??/functions</filename>
directory.</para>
<para>Note that the <option>-f</option> option to
<command>enable</command> is not <link
linkend="portabilityissues">portable</link> to all
systems.</para>
</footnote>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="autoloadref"/><command>autoload</command></term>
<listitem>
<indexterm>
<primary>autoload</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>autoloader</secondary>
</indexterm>
<para>This is a port to Bash of the
<firstterm>ksh</firstterm> autoloader. With
<command>autoload</command> in place, a function with
an <firstterm>autoload</firstterm> declaration will load from an
external file at its first invocation.
<footnote><para>The same effect as
<command>autoload</command> can be achieved with <link
linkend="declareref">typeset -fu</link>.</para></footnote>
This saves system resources.</para>
<para>Note that <firstterm>autoload</firstterm> is not a part of the
core Bash installation. It needs to be loaded in with
<replaceable>enable -f</replaceable> (see above).</para>
</listitem>
</varlistentry>
</variablelist>
<para><anchor id="jobidtable0"/></para>
<table id="jobidtable">
<title>Job identifiers</title>
<tgroup cols="2">
<thead>
<row>
<entry>Notation</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>%N</option></entry>
<entry>Job number [N]</entry>
</row>
<row>
<entry><option>%S</option></entry>
<entry>Invocation (command-line) of job begins with string <emphasis>S</emphasis></entry>
</row>
<row>
<entry><option>%?S</option></entry>
<entry>Invocation (command-line) of job contains within it string <emphasis>S</emphasis></entry>
</row>
<row>
<entry><option>%%</option></entry>
<entry><quote>current</quote> job (last job stopped in
foreground or started in background)</entry>
</row>
<row>
<entry><option>%+</option></entry>
<entry><quote>current</quote> job (last job stopped in
foreground or started in background)</entry>
</row>
<row>
<entry><option>%-</option></entry>
<entry>Last job</entry>
</row>
<row>
<entry><option>$!</option></entry>
<entry>Last background process</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1> <!-- Job Control Commands -->
</chapter> <!-- Internal Commands and Builtins -->
<chapter id="external">
<title>External Filters, Programs and Commands</title>
<para><anchor id="externalref"/></para>
<para>Standard UNIX commands make shell scripts more versatile. The
power of scripts comes from coupling system commands and shell
directives with simple programming constructs.</para>
<sect1 id="basic">
<title>Basic Commands</title>
<variablelist id="basiccommands">
<title><anchor id="basiccommands1"/>The first commands a novice learns</title>
<varlistentry>
<term><anchor id="lsref"/><command>ls</command></term>
<listitem>
<indexterm>
<primary>ls</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ls</secondary>
</indexterm>
<para>The basic file <quote>list</quote> command. It is all too easy
to underestimate the power of this humble command. For
example, using the <option>-R</option>, recursive option,
<command>ls</command> provides a tree-like listing of
a directory structure. Other useful options are
<option>-S</option>, sort listing by file size,
<option>-t</option>, sort by file modification time,
<option>-v</option>, sort by (numerical) version numbers
embedded in the filenames,
<footnote><para>The <option>-v</option> option also orders the
sort by <emphasis>upper- and lowercase prefixed</emphasis>
filenames.</para></footnote>
<option>-b</option>, show escape characters, and
<option>-i</option>, show file inodes (see <xref
linkend="idelete"/>).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter10.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter11.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter12.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter1.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter2.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter3.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:49 Chapter_headings.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:49 Preface.txt</computeroutput>
<prompt>bash$ </prompt><userinput>ls -lv</userinput>
<computeroutput> total 0
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:49 Chapter_headings.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:49 Preface.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter1.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter2.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter3.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter10.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter11.txt
-rw-rw-r-- 1 bozo bozo 0 Sep 14 18:44 chapter12.txt</computeroutput></screen>
</para>
<tip><para>
The <firstterm>ls</firstterm> command returns a
non-zero <link linkend="exitstatusref">exit status</link> when
attempting to list a non-existent file.
<screen><prompt>bash$ </prompt><userinput>ls abc</userinput>
<computeroutput>ls: abc: No such file or directory</computeroutput>
<prompt>bash$ </prompt><userinput>echo $?</userinput>
<computeroutput>2</computeroutput></screen>
</para></tip>
<example id="ex40">
<title>Using <firstterm>ls</firstterm> to create a table of contents
for burning a <abbrev>CDR</abbrev> disk</title>
<programlisting>&ex40;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="catref"/><command>cat</command></term>
<term><command>tac</command></term>
<listitem>
<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>
<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.
<anchor id="catuses"/>
<programlisting># Uses of 'cat'
cat filename # Lists the file.
cat file.1 file.2 file.3 &gt; file.123 # Combines three files into one.</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. The <option>-s</option> option squeezes multiple
consecutive blank lines into a single blank line.</para>
<para>See also <xref linkend="lnum"/> and <xref linkend="rot13"/>.</para>
<note>
<para><anchor id="catlesseff"/>
In a <link linkend="piperef">pipe</link>, it may be
more efficient to <link linkend="ioredirref">redirect</link>
the <filename>stdin</filename> to a file, rather than to
<command>cat</command> the file.
</para>
<para>
<programlisting>cat filename | tr a-z A-Z
tr a-z A-Z &lt; filename # Same effect, but starts one less process,
#+ and also dispenses with the pipe.</programlisting>
</para>
</note>
<para><command>tac</command>, is the inverse of
<wordasword>cat</wordasword>, listing a file backwards from its end.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="revref"/><command>rev</command></term>
<listitem>
<indexterm>
<primary>rev</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rev</secondary>
</indexterm>
<para>reverses each line of a file, and outputs to
<filename>stdout</filename>. This does not have the same effect
as <command>tac</command>, as it preserves the order of
the lines, but flips each one around (mirror image).</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><anchor id="cpref"/><command>cp</command></term>
<listitem>
<indexterm>
<primary>cp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cp</secondary>
</indexterm>
<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),
the <option>-u</option> update flag (which prevents
overwriting identically-named newer files), and the
<option>-r</option> and <option>-R</option> recursive
flags.</para>
<para><programlisting>cp -u source_dir/* dest_dir
# "Synchronize" dest_dir to source_dir
#+ by copying over all newer and not previously existing files.</programlisting></para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mvref"/><command>mv</command></term>
<listitem>
<para>This is the file <firstterm>move</firstterm> 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, or even to rename a directory. For
some examples of using <command>mv</command> in a script,
see <xref linkend="rfe"/> and <xref linkend="rn"/>.</para>
<note>
<para>When used in a non-interactive script,
<command>mv</command> takes the <option>-f</option>
(<firstterm>force</firstterm>) option to bypass user
input.</para>
<para>When a directory is moved to a preexisting directory,
it becomes a subdirectory of the destination directory.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>mv source_directory target_directory</userinput>
<prompt>bash$ </prompt><userinput>ls -lF target_directory</userinput>
<computeroutput>total 1
drwxrwxr-x 2 bozo bozo 1024 May 28 19:20 source_directory/</computeroutput>
</screen>
</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rmref"/><command>rm</command></term>
<listitem>
<indexterm>
<primary>rm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rm</secondary>
</indexterm>
<para>Delete (remove) a file or files. The <option>-f</option>
option forces removal of even readonly files, and is useful
for bypassing user input in a script.</para>
<note>
<para><anchor id="dashrem"/></para>
<para>The <firstterm>rm</firstterm> command will, by
itself, fail to remove filenames beginning with
a dash. Why? Because <firstterm>rm</firstterm>
sees a dash-prefixed filename as an
<firstterm>option</firstterm>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>rm -badname</userinput>
<computeroutput>rm: invalid option -- b
Try `rm --help' for more information.</computeroutput></screen>
</para>
<para>
One clever workaround is to precede
the filename with a <quote> -- </quote> (the
<firstterm>end-of-options</firstterm> flag).
<screen><prompt>bash$ </prompt><userinput>rm -- -badname</userinput></screen>
</para>
<para>
Another method to is to preface the filename to be removed
with a <filename>dot-slash</filename> .
<screen><prompt>bash$ </prompt><userinput>rm ./-badname</userinput></screen>
</para>
</note>
<warning><para><anchor id="rmrecurs"/>When used with the
recursive flag <option>-r</option>, this command removes
files all the way down the directory tree from the current
directory. A careless <command>rm -rf *</command> can wipe
out a big chunk of a directory structure.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rmdirref"/><command>rmdir</command></term>
<listitem>
<indexterm>
<primary>rmdir</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rmdir</secondary>
</indexterm>
<para>Remove directory. The directory must be empty of
all files -- including <quote>invisible</quote>
<firstterm>dotfiles</firstterm>
<footnote>
<para><anchor id="dotfilesref"/></para>
<para><firstterm>Dotfiles</firstterm> are files whose
names begin with a <firstterm>dot</firstterm>, such as
<filename>~/.Xdefaults</filename>. Such filenames do
not appear in a normal <command>ls</command> listing
(although an <command>ls -a</command> will show
them), 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><anchor id="mkdirref"/><command>mkdir</command></term>
<listitem>
<indexterm>
<primary>mkdir</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkdir</secondary>
</indexterm>
<para>Make directory, creates a new directory. For example,
<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>
<listitem>
<indexterm>
<primary>chmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chmod</secondary>
</indexterm>
<para>Changes the attributes of an existing file or directory
(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).
chmod 444 filename
# Makes "filename" read-only for all.
# Modifying the file (for example, with a text editor)
#+ not allowed for a user who does not own the file (except for root),
#+ and even the file owner must force a file-save
#+ if she modifies the file.
# Same restrictions apply for deleting the file.</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.
chmod 111 directory-name
# Gives everyone execute-only permission in a directory.
# This means that you can execute and READ the files in that directory
#+ (execute permission necessarily includes read permission
#+ because you can't execute a file without being able to read it).
# But you can't list the files or search for them with the "find" command.
# These restrictions do not apply to root.
chmod 000 directory-name
# No permissions at all for that directory.
# Can't read, write, or execute files in it.
# Can't even list files in it or "cd" to it.
# But, you can rename (mv) the directory
#+ or delete it (rmdir) if it is empty.
# You can even symlink to files in the directory,
#+ but you can't read, write, or execute the symlinks.
# These restrictions do not apply to root.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="chattrref"/><command>chattr</command></term>
<listitem>
<indexterm>
<primary>chattr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chattr</secondary>
</indexterm>
<para><command>Ch</command>ange file
<command>attr</command>ibutes. This is analogous to
<command>chmod</command> above, but with different options
and a different invocation syntax, and it works only on
<firstterm>ext2/ext3</firstterm> filesystems.</para>
<para>One particularly interesting <command>chattr</command>
option is <option>i</option>. A <command>chattr +i
<filename>filename</filename></command> marks the file
as immutable. The file cannot be modified, linked to, or
deleted, <emphasis>not even by root</emphasis>. This
file attribute can be set or removed only by
<firstterm>root</firstterm>. In a similar fashion,
the <option>a</option> option marks the file as append
only.</para>
<para>
<screen>
<prompt>root# </prompt><userinput>chattr +i file1.txt</userinput>
<prompt>root# </prompt><userinput>rm file1.txt</userinput>
<computeroutput>rm: remove write-protected regular file `file1.txt'? y
rm: cannot remove `file1.txt': Operation not permitted</computeroutput>
</screen>
</para>
<para>If a file has the <option>s</option> (secure)
attribute set, then when it is deleted its block is
overwritten with binary zeroes.
<footnote><para>This particular feature may not yet be
implemented in the version of the ext2/ext3 filesystem
installed on your system. Check the documentation for
your Linux distro.</para></footnote>
</para>
<para>If a file has the <option>u</option> (undelete)
attribute set, then when it is deleted, its contents can still
be retrieved (undeleted).</para>
<para>If a file has the <option>c</option> (compress)
attribute set, then it will automatically be compressed
on writes to disk, and uncompressed on reads.</para>
<note><para>The file attributes set with
<command>chattr</command> do not show in a file listing
(<command>ls -l</command>).</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="linkref"/><command>ln</command></term>
<listitem>
<para>Creates links to pre-existings files. A <quote>link</quote>
is a reference to a file, an alternate name for it.
The <command>ln</command> command permits referencing
the linked file by more than one name and is a superior
alternative to aliasing (see <xref linkend="ex18"/>).</para>
<para>The <command>ln</command> creates only a reference, a
pointer to the file only a few bytes in size.</para>
<para><anchor id="symlinkref"/></para>
<para>The <command>ln</command> command is most often used
with the <option>-s</option>, symbolic or
<quote>soft</quote> link flag. Advantages of using the
<option>-s</option> flag are that it permits linking across
file systems or to directories.</para>
<para>The syntax of the command is a bit tricky. For example:
<userinput>ln -s oldfile newfile</userinput> links the
previously existing <filename>oldfile</filename> to the
newly created link, <filename>newfile</filename>.</para>
<caution><para>If a file named <filename>newfile</filename> has
previously existed, an error message will
result.</para></caution>
<sidebar><title>Which type of link to use?</title>
<para>As John Macdonald explains it:</para>
<para>Both of these [types of links] provide a certain measure of dual reference
-- if you edit the contents of the file using any name,
your changes will affect both the original name and either
a hard or soft new name. The differences between them
occurs when you work at a higher level. The advantage of
a hard link is that the new name is totally independent
of the old name -- if you remove or rename the old name,
that does not affect the hard link, which continues
to point to the data while it would leave a soft link
hanging pointing to the old name which is no longer
there. The advantage of a soft link is that it can refer
to a different file system (since it is just a reference
to a file name, not to actual data). And, unlike a hard
link, a symbolic link can refer to a directory.</para>
</sidebar>
<para><anchor id="linkminvok"/></para>
<para>Links give the ability to invoke a script (or any other type
of executable) with multiple names, and having that script
behave according to how it was invoked.</para>
<example id="hellol">
<title>Hello or Good-bye</title>
<programlisting>&hellol;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="manref"/><command>man</command></term>
<term><anchor id="inforef"/><command>info</command></term>
<listitem>
<indexterm>
<primary>man</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>man</secondary>
</indexterm>
<indexterm>
<primary>info</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>info</secondary>
</indexterm>
<para>These commands access the manual and information pages on
system commands and installed utilities. When available, the
<firstterm>info</firstterm> pages usually contain more detailed
descriptions than do the <firstterm>man</firstterm> pages.</para>
<para>There have been various attempts at
<quote>automating</quote> the writing of <firstterm>man
pages</firstterm>. For a script that makes a tentative first
step in that direction, see <xref linkend="maned"/>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Basic Commands -->
<sect1 id="moreadv">
<title>Complex Commands</title>
<variablelist id="cclisting">
<title><anchor id="cclisting1"/>Commands for more advanced users</title>
<varlistentry>
<term><anchor id="findref"/><command>find</command></term>
<listitem>
<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>
<para><anchor id="findref0"/></para>
<para>-exec <replaceable>COMMAND</replaceable> \;</para>
<para>Carries out <replaceable>COMMAND</replaceable> on
each file that <command>find</command> matches. The
command sequence terminates with <token>;</token> (the
<quote>;</quote> is <link linkend="escp">escaped</link> to
make certain the shell passes it to <command>find</command>
literally, without interpreting it as a special character).</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><anchor id="curlybracketsref"/></para>
<para>If <replaceable>COMMAND</replaceable> contains
<token>{}</token>, then <command>find</command>
substitutes the full path name of the selected file for
<quote>{}</quote>.</para>
<para>
<programlisting>find ~/ -name 'core*' -exec rm {} \;
# Removes all core dump files from user's home directory.</programlisting>
</para>
<para>
<programlisting>find /home/bozo/projects -mtime -1
# ^ Note minus sign!
# Lists all files in /home/bozo/projects directory tree
#+ that were modified within the last day (current_day - 1).
#
find /home/bozo/projects -mtime 1
# Same as above, but modified *exactly* one day ago.
#
# mtime = last modification time of the target file
# ctime = last status change time (via 'chmod' or otherwise)
# atime = last access time
DIR=/home/bozo/junk_files
find "$DIR" -type f -atime +5 -exec rm {} \;
# ^ ^^
# Curly brackets are placeholder for the path name output by "find."
#
# Deletes all files in "/home/bozo/junk_files"
#+ that have not been accessed in *at least* 5 days (plus sign ... +5).
#
# "-type filetype", where
# f = regular file
# d = directory
# l = symbolic link, etc.
#
# (The 'find' manpage and info page have complete option listings.)</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. Can they be filtered out?
# Possibly by:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
#
# [:digit:] is one of the character classes
#+ introduced with the POSIX 1003.2 standard.
# Thanks, St&eacute;phane Chazelas.
</programlisting></para>
<note><para>The <option>-exec</option> option to
<command>find</command> should not be confused with the <link
linkend="execref">exec</link> shell builtin.</para></note>
<example id="ex57">
<title><firstterm>Badname</firstterm>, eliminate file names
in current directory containing bad characters and <link
linkend="whitespaceref">whitespace</link>.</title>
<programlisting>&ex57;</programlisting>
</example>
<example id="idelete">
<title>Deleting a file by its <firstterm>inode</firstterm>
number</title>
<programlisting>&idelete;</programlisting>
</example>
<para>The <command>find</command> command also works
without the <option>-exec</option> option.</para>
<para>
<programlisting>#!/bin/bash
# Find suid root files.
# A strange suid file might indicate a security hole,
#+ or even a system intrusion.
directory="/usr/sbin"
# Might also try /sbin, /bin, /usr/bin, /usr/local/bin, etc.
permissions="+4000" # suid root (dangerous!)
for file in $( find "$directory" -perm "$permissions" )
do
ls -ltF --author "$file"
done</programlisting>
</para>
<para>See <xref linkend="ex48"/>, <xref linkend="ex58"/>,
and <xref linkend="findstring"/> for scripts using
<command>find</command>. Its <link
linkend="manref">manpage</link> provides more detail
on this complex and powerful command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xargsref"/><command>xargs</command></term>
<listitem>
<indexterm>
<primary>xargs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>xargs</secondary>
</indexterm>
<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 <link linkend="backquotesref">backquotes</link>.
In situations where <link linkend="commandsubref">command
substitution</link> fails with a <errorname>too
many arguments</errorname> error,
substituting <command>xargs</command> often
works.
<footnote><para>And even when <firstterm>xargs</firstterm> is
not strictly necessary, it can speed up execution of a command
involving <link
linkend="batchprocref">batch-processing</link> of multiple
files.</para></footnote>
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>. This means that input
piped to <command>xargs</command> may have linefeeds and
other whitespace characters stripped out.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 0
-rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1
-rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file2</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l | xargs</userinput>
<computeroutput>total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1 -rw-rw-r-- 1 bozo bozo 0 Jan...</computeroutput>
<prompt>bash$ </prompt><userinput>find ~/mail -type f | xargs grep "Linux"</userinput>
<computeroutput>./misc:User-Agent: slrn/0.9.8.1 (Linux)
./sent-mail-jul-2005: hosted by the Linux Documentation Project.
./sent-mail-jul-2005: (Linux Documentation Project Site, rtf version)
./sent-mail-jul-2005: Subject: Criticism of Bozo's Windows/Linux article
./sent-mail-jul-2005: while mentioning that the Linux ext2/ext3 filesystem
. . .</computeroutput>
</screen>
</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>
<para><anchor id="xargsoneatatime"/></para>
<note>
<para>Note that <firstterm>xargs</firstterm> processes the
arguments passed to it sequentially, <emphasis>one at
a time</emphasis>.</para>
<para><screen>
<prompt>bash$ </prompt><userinput>find /usr/bin | xargs file</userinput>
<computeroutput>/usr/bin: directory
/usr/bin/foomatic-ppd-options: perl script text executable
. . .</computeroutput>
</screen>
</para>
</note>
<para><anchor id="xargslimargs"/></para>
<tip>
<para>An interesting <firstterm>xargs</firstterm>
option is <option>-n <replaceable>NN</replaceable></option>,
which limits to <replaceable>NN</replaceable> the number
of arguments passed.</para>
<para><userinput>ls | xargs -n 8 echo</userinput> lists the files in the
current directory in <literal>8</literal> columns.</para>
</tip>
<para><anchor id="xargsws"/></para>
<tip>
<para>Another useful option is
<option>-0</option>, in combination with <userinput>find
-print0</userinput> or <userinput>grep -lZ</userinput>. 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>
<para>Or:
<programlisting>cat /proc/"$pid"/"$OPTION" | xargs -0 echo
# Formats output: ^^^^^^^^^^^^^^^
# From Han Holl's fixup of "get-commandline.sh"
#+ script in "/dev and /proc" chapter.</programlisting></para>
</tip>
<tip>
<para><anchor id="xargsmultiprocess"/></para>
<para>The <option>-P</option> option to
<firstterm>xargs</firstterm> permits running
processes in parallel. This speeds up execution
in a machine with a multicore CPU.</para>
<para><programlisting>#!/bin/bash
ls *gif | xargs -t -n1 -P2 gif2png
# Converts all the gif images in current directory to png.
# Options:
# =======
# -t Print command to stderr.
# -n1 At most 1 argument per command line.
# -P2 Run up to 2 processes simultaneously.
# Thank you, Roberto Polli, for the inspiration.</programlisting></para>
</tip>
<example id="ex41">
<title>Logfile: Using <firstterm>xargs</firstterm> to monitor system log</title>
<programlisting>&ex41;</programlisting>
</example>
<para><anchor id="xargscurlyref"/></para>
<para><link linkend="curlybracketsref">As in
<command>find</command></link>, a curly bracket
pair serves as a placeholder for replacement text.</para>
<example id="ex42">
<title>Copying files in current directory to another</title>
<programlisting>&ex42;</programlisting>
</example>
<example id="killbyname">
<title>Killing processes by name</title>
<programlisting>&killbyname;</programlisting>
</example>
<example id="wf2">
<title>Word frequency analysis using
<firstterm>xargs</firstterm></title>
<programlisting>&wf2;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="exprref"/><userinput>expr</userinput></term>
<listitem>
<indexterm>
<primary>expr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>expr</secondary>
</indexterm>
<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>expr 1 / 0</userinput></term>
<listitem>
<para>returns the error message, <errorcode>expr: division by
zero</errorcode></para>
<para>Illegal arithmetic operations not allowed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>expr 5 \* 3</userinput></term>
<listitem>
<para>returns 15</para>
<para>The multiplication operator
must be escaped when used in an arithmetic expression
with <command>expr</command>.</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><anchor id="expextrsub"/><userinput>z=`expr substr
$string $position $length`</userinput></term>
<listitem>
<para>Extract substring of $length characters, starting
at $position.</para>
</listitem>
</varlistentry>
</variablelist>
<example id="ex45">
<title>Using <firstterm>expr</firstterm></title>
<programlisting>&ex45;</programlisting>
</example>
<important>
<para>The <link linkend="nullref">:
(<firstterm>null</firstterm>)</link> operator
can substitute for <command>match</command>. For example,
<userinput>b=`expr $a : [0-9]*`</userinput> is the
exact equivalent of <userinput>b=`expr match $a
[0-9]*`</userinput> in the above listing.</para>
<para><programlisting>&ex45a;</programlisting></para>
</important>
</listitem>
</varlistentry>
</variablelist>
<para>The above script illustrates how
<command>expr</command> uses the <firstterm>escaped
parentheses -- \( ... \) --</firstterm> grouping operator
in tandem with <link linkend="regexref">regular
expression</link> parsing to match a substring.
Here is a another example, this time from <quote>real
life.</quote>
<programlisting># Strip the whitespace from the beginning and end.
LRFDATE=`expr "$LRFDATE" : '[[:space:]]*\(.*\)[[:space:]]*$'`
# From Peter Knowles' "booklistgen.sh" script
#+ for converting files to Sony Librie/PRS-50X format.
# (http://booklistgensh.peterknowles.com)</programlisting>
</para>
<para><link linkend="perlref">Perl</link>,
<link linkend="sedref">sed</link>, and <link
linkend="awkref">awk</link> have far superior string
parsing facilities. A short <command>sed</command> or
<command>awk</command> <quote>subroutine</quote> within
a script (see <xref linkend="wrapper"/>) is an attractive
alternative to <command>expr</command>.</para>
<para>See <xref linkend="String-Manipulation"/> for more on
using <command>expr</command> in string operations.</para>
</sect1> <!-- End Complex Commands -->
<sect1 id="timedate">
<title>Time / Date Commands</title>
<variablelist id="tdlisting">
<title><anchor id="tdlisting1"/>Time/date and timing</title>
<varlistentry>
<term><anchor id="dateref"/><command>date</command></term>
<listitem>
<indexterm>
<primary>date</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>date</secondary>
</indexterm>
<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 <firstterm>date</firstterm></title>
<programlisting>&ex51;</programlisting>
</example>
<para>The <option>-u</option> option gives the UTC (Universal
Coordinated Time).</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>date</userinput>
<computeroutput>Fri Mar 29 21:07:39 MST 2002</computeroutput>
<prompt>bash$ </prompt><userinput>date -u</userinput>
<computeroutput>Sat Mar 30 04:07:42 UTC 2002</computeroutput>
</screen>
</para>
<para>This option facilitates calculating the time between
different dates.</para>
<example id="datecalc">
<title><firstterm>Date</firstterm> calculations</title>
<programlisting>&datecalc;</programlisting>
</example>
<para><anchor id="daterandref"/></para>
<para>The <firstterm>date</firstterm> command has quite a
number of <firstterm>output</firstterm> options. For
example <option>%N</option> gives the nanosecond portion
of the current time. One interesting use for this is to
generate random integers.
<programlisting>date +%N | sed -e 's/000$//' -e 's/^0//'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Strip off leading and trailing zeroes, if present.
# Length of generated integer depends on
#+ how many zeroes stripped off.
# 115281032
# 63408725
# 394504284</programlisting>
</para>
<para>There are many more options (try <command>man
date</command>).</para>
<para><programlisting>date +%j
# Echoes day of the year (days elapsed since January 1).
date +%k%M
# Echoes hour and minute in 24-hour format, as a single digit string.
# The 'TZ' parameter permits overriding the default time zone.
date # Mon Mar 28 21:42:16 MST 2005
TZ=EST date # Mon Mar 28 23:42:16 EST 2005
# Thanks, Frank Kannemann and Pete Sjoberg, for the tip.
SixDaysAgo=$(date --date='6 days ago')
OneMonthAgo=$(date --date='1 month ago') # Four weeks back (not a month!)
OneYearAgo=$(date --date='1 year ago')</programlisting></para>
<para>See also <xref linkend="ex58"/> and <xref
linkend="stopwatch"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="zdumpref"/><command>zdump</command></term>
<listitem>
<indexterm>
<primary>zdump</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>time zone dump</secondary>
</indexterm>
<para>Time zone dump: echoes the time in a specified time zone.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>zdump EST</userinput>
<computeroutput>EST Tue Sep 18 22:09:22 2001 EST</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="timref"/><command>time</command></term>
<listitem>
<indexterm>
<primary>time</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>time</secondary>
</indexterm>
<para>Outputs verbose timing statistics for executing a command.</para>
<para><userinput>time ls -l /</userinput> gives something
like this:</para>
<para>
<screen><computeroutput>real 0m0.067s
user 0m0.004s
sys 0m0.005s</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>
<listitem>
<indexterm>
<primary>touch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>touch</secondary>
</indexterm>
<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>
<note><para>The <command>touch</command> command is
equivalent to <userinput>: &gt;&gt; newfile</userinput>
or <userinput>&gt;&gt; newfile</userinput> (for ordinary
files).</para></note>
<tip>
<para>Before doing a <link linkend="cpref">cp -u</link>
(<firstterm>copy/update</firstterm>), use
<command>touch</command> to update the time stamp of files
you don't wish overwritten.</para>
<para>As an example, if the directory <filename
class="directory">/home/bozo/tax_audit</filename> contains the
files <filename>spreadsheet-051606.data</filename>,
<filename>spreadsheet-051706.data</filename>, and
<filename>spreadsheet-051806.data</filename>, then
doing a <command>touch spreadsheet*.data</command>
will protect these files from being overwritten
by files with the same names during a
<command>cp -u /home/bozo/financial_info/spreadsheet*data
/home/bozo/tax_audit</command>.</para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="atref"/><command>at</command></term>
<listitem>
<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>
<para>The <command>at</command> job control command executes
a given set of commands at a specified time. Superficially,
it resembles <link linkend="cronref">cron</link>, however,
<command>at</command> is chiefly useful for one-time execution
of a command set.</para>
<para><userinput>at 2pm January 15</userinput> prompts for a set of
commands to execute at that time. These commands should be
shell-script compatible, since, for all practical
purposes, the user is typing in an executable shell
script a line at a time. Input terminates with a <link
linkend="ctldref">Ctl-D</link>.</para>
<para>Using either the <option>-f</option> option or input
redirection (<token>&lt;</token>), <command>at</command>
reads a command list from a file. This file is an
executable shell script, though it should, of course,
be non-interactive. Particularly clever is including the
<link linkend="runpartsref">run-parts</link> command in
the file to execute a different set of scripts.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>at 2:30 am Friday &lt; at-jobs.list</userinput>
<computeroutput>job 2 at 2000-10-27 02:30</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="batchref"/><command>batch</command></term>
<listitem>
<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>
<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>
<para><anchor id="batchprocref"/></para>
<sidebar>
<para>The concept of <firstterm>batch processing</firstterm>
dates back to the era of mainframe computers. It means
running a set of commands without user intervention.</para>
</sidebar>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="calref"/><command>cal</command></term>
<listitem>
<indexterm>
<primary>cal</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cal</secondary>
</indexterm>
<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><anchor id="sleepref"/><command>sleep</command></term>
<listitem>
<indexterm>
<primary>sleep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sleep</secondary>
</indexterm>
<para>This is the shell equivalent of a <firstterm>wait
loop</firstterm>. It pauses for a specified number of
seconds, doing nothing. It can be useful for timing or
in processes running in the background, checking for
a specific event every so often (polling), as in <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>
<note><para>The <link linkend="watchref">watch</link> command may
be a better choice than <command>sleep</command> for running
commands at timed intervals.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="usleepref"/><command>usleep</command></term>
<listitem>
<indexterm>
<primary>usleep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>usleep</secondary>
</indexterm>
<para><firstterm>Microsleep</firstterm> (the
<firstterm>u</firstterm> may be read as the Greek
<firstterm>mu</firstterm>, or <firstterm>micro-</firstterm>
prefix). This is the same as <command>sleep</command>,
above, but <quote>sleeps</quote> in microsecond
intervals. It can be used for fine-grained timing,
or for polling an ongoing process at very frequent
intervals.</para>
<para>
<programlisting>usleep 30 # Pauses 30 microseconds.</programlisting>
</para>
<para>This command is part of the Red Hat
<firstterm>initscripts / rc-scripts</firstterm> package.</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><anchor id="hwclockref"/><command>hwclock</command></term>
<term><anchor id="clockref"/><command>clock</command></term>
<listitem>
<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>
<para>The <command>hwclock</command> command accesses or
adjusts the machine's hardware clock. Some options
require <firstterm>root</firstterm> privileges. The
<filename>/etc/rc.d/rc.sysinit</filename> startup file
uses <command>hwclock</command> to set the system time
from the hardware clock at bootup.</para>
<para>The <command>clock</command> command is a synonym for
<command>hwclock</command>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Time / Date Commands -->
<sect1 id="textproc">
<title>Text Processing Commands</title>
<variablelist id="tpcommandlisting">
<title><anchor id="tpcommandlisting1"/>Commands affecting text and
text files</title>
<varlistentry>
<term><anchor id="sortref"/><command>sort</command></term>
<listitem>
<indexterm>
<primary>sort</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sort</secondary>
</indexterm>
<para>File sort utility, often used as a filter in a pipe. This
command sorts a <firstterm>text stream</firstterm>
or file forwards or backwards, or according to various
keys or character positions. Using the <option>-m</option>
option, it merges presorted input files. The <firstterm>info
page</firstterm> lists its many capabilities and options. See
<xref linkend="findstring"/>, <xref linkend="symlinks"/>,
and <xref linkend="makedict"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tsortref"/><command>tsort</command></term>
<listitem>
<indexterm>
<primary>tsort</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>topological sort</secondary>
</indexterm>
<para><firstterm>Topological sort</firstterm>, reading in
pairs of whitespace-separated strings and sorting
according to input patterns. The original purpose of
<command>tsort</command> was to sort a list of dependencies
for an obsolete version of the <firstterm>ld</firstterm>
linker in an <quote>ancient</quote> version of UNIX.</para>
<para>The results of a <firstterm>tsort</firstterm> will usually
differ markedly from those of the standard
<command>sort</command> command, above.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uniqref"/><command>uniq</command></term>
<listitem>
<indexterm>
<primary>uniq</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uniq</secondary>
</indexterm>
<para>This filter removes duplicate lines from a sorted
file. It is often seen in a pipe coupled with
<link linkend="sortref">sort</link>.</para>
<para><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 its number of occurrences.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cat testfile</userinput>
<computeroutput>This line occurs only once.
This line occurs twice.
This line occurs twice.
This line occurs three times.
This line occurs three times.
This line occurs three times.</computeroutput>
<prompt>bash$ </prompt><userinput>uniq -c testfile</userinput>
<computeroutput> 1 This line occurs only once.
2 This line occurs twice.
3 This line occurs three times.</computeroutput>
<prompt>bash$ </prompt><userinput>sort testfile | uniq -c | sort -nr</userinput>
<computeroutput> 3 This line occurs three times.
2 This line occurs twice.
1 This line occurs only once.</computeroutput>
</screen>
</para>
<para>The <userinput>sort INPUTFILE | uniq -c | sort -nr</userinput>
command string produces a <firstterm>frequency
of occurrence</firstterm> listing on the
<filename>INPUTFILE</filename> file (the
<option>-nr</option> options to <command>sort</command>
cause a reverse numerical sort). This template finds
use in analysis of log files and dictionary lists, and
wherever the lexical structure of a document needs to
be examined.</para>
<example id="wf">
<title>Word Frequency Analysis</title>
<programlisting>&wf;</programlisting>
</example>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cat testfile</userinput>
<computeroutput>This line occurs only once.
This line occurs twice.
This line occurs twice.
This line occurs three times.
This line occurs three times.
This line occurs three times.</computeroutput>
<prompt>bash$ </prompt><userinput>./wf.sh testfile</userinput>
<computeroutput> 6 this
6 occurs
6 line
3 times
3 three
2 twice
1 only
1 once</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="expandref"/><command>expand</command></term>
<term><command>unexpand</command></term>
<listitem>
<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>
<para>The <command>expand</command> filter converts tabs to
spaces. It is often used in a <link
linkend="piperef">pipe</link>.</para>
<para>The <command>unexpand</command> filter
converts spaces to tabs. This reverses the effect of
<command>expand</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cutref"/><command>cut</command></term>
<listitem>
<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>
<para>A tool for extracting <link
linkend="fieldref">fields</link> 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 <firstterm>cut</firstterm> in a script than
<firstterm>awk</firstterm>. 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>cut -d ' ' -f1,2 /etc/mtab</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>Using <command>cut</command> to extract message headers from
an e-mail folder:
<screen><prompt>bash$ </prompt><userinput>grep '^Subject:' read-messages | cut -c10-80</userinput>
<computeroutput>Re: Linux suitable for mission-critical apps?
MAKE MILLIONS WORKING AT HOME!!!
Spam complaint
Re: Spam complaint</computeroutput></screen>
</para>
<para>Using <command>cut</command> to parse a file:
<programlisting># List all the users in /etc/passwd.
FILENAME=/etc/passwd
for user in $(cut -d: -f1 $FILENAME)
do
echo $user
done
# Thanks, Oleg Philon for suggesting this.</programlisting></para>
<para><userinput>cut -d ' ' -f2,3 filename</userinput> is equivalent to
<userinput>awk -F'[ ]' '{ print $2, $3 }' filename</userinput></para>
<note>
<para>It is even possible to specify a linefeed as a
delimiter. The trick is to actually embed a linefeed
(<keycap>RETURN</keycap>) in the command sequence.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cut -d'
' -f3,7,19 testfile</userinput>
<computeroutput>This is line 3 of testfile.
This is line 7 of testfile.
This is line 19 of testfile.</computeroutput>
</screen>
</para>
<para>Thank you, Jaka Kranjc, for pointing this out.</para>
</note>
<para>See also <xref linkend="base"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pasteref"/><command>paste</command></term>
<listitem>
<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>
<para>Tool for merging together different files into a single,
multi-column file. In combination with
<link linkend="cutref">cut</link>, useful for creating system log
files.
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cat items</userinput>
<computeroutput>alphabet blocks
building blocks
cables</computeroutput>
<prompt>bash$ </prompt><userinput>cat prices</userinput>
<computeroutput>$1.00/dozen
$2.50 ea.
$3.75</computeroutput>
<prompt>bash$ </prompt><userinput>paste items prices</userinput>
<computeroutput>alphabet blocks $1.00/dozen
building blocks $2.50 ea.
cables $3.75</computeroutput></screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="joinref"/><command>join</command></term>
<listitem>
<indexterm>
<primary>join</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>join</secondary>
</indexterm>
<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 <link linkend="fieldref">field</link>
(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><anchor id="headref"/><command>head</command></term>
<listitem>
<indexterm>
<primary>head</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>head</secondary>
</indexterm>
<para>lists the beginning of a file to <filename>stdout</filename>.
The default is <literal>10</literal> lines, but a different
number can be specified. The command has a number of
interesting options.
<example id="scriptdetector">
<title>Which files are scripts?</title>
<programlisting>&scriptdetector;</programlisting>
</example>
<example id="rnd">
<title>Generating 10-digit random numbers</title>
<programlisting>&rnd;</programlisting>
</example>
See also <xref linkend="ex52"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tailref"/><command>tail</command></term>
<listitem>
<indexterm>
<primary>tail</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tail</secondary>
</indexterm>
<para>lists the (tail) end of a file to <filename>stdout</filename>.
The default is <literal>10</literal> lines, but this can
be changed with the <option>-n</option> option.
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 <firstterm>tail</firstterm> to monitor the system log</title>
<programlisting>&ex12;</programlisting>
</example>
<tip>
<para>To list a specific line of a text file,
<link linkend="piperef">pipe</link> the output of
<command>head</command> to <command>tail -n 1</command>.
For example <userinput>head -n 8 database.txt | tail
-n 1</userinput> lists the 8th line of the file
<filename>database.txt</filename>.</para>
<para>To set a variable to a given block of a text file:
<programlisting>var=$(head -n $m $filename | tail -n $n)
# filename = name of file
# m = from beginning of file, number of lines to end of block
# n = number of lines to set variable to (trim from end of block)</programlisting></para>
</tip>
<note>
<para>Newer implementations of <command>tail</command>
deprecate the older <command>tail -$LINES
filename</command> usage. The standard <command>tail -n $LINES
filename</command> is correct.</para>
</note>
<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>
<listitem>
<indexterm>
<primary>grep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>grep</secondary>
</indexterm>
<para>A multi-purpose file search tool that uses
<link linkend="regexref">Regular Expressions</link>.
It was originally a command/filter in the
venerable <command>ed</command> line editor:
<userinput>g/re/p</userinput> -- <firstterm>global -
regular expression - print</firstterm>.</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 target file(s) for
occurrences of <replaceable>pattern</replaceable>, where
<replaceable>pattern</replaceable> may be literal text
or a Regular Expression.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep '[rst]ystem.$' osinfo.txt</userinput>
<computeroutput>The GPL governs the distribution of the Linux operating system.</computeroutput>
</screen>
</para>
<para>If no target file(s) specified, <command>grep</command>
works as a filter on <filename>stdout</filename>, as in
a <link linkend="piperef">pipe</link>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ps ax | grep clock</userinput>
<computeroutput>765 tty1 S 0:00 xclock
901 pts/1 S 0:00 grep clock</computeroutput>
</screen>
</para>
<para>The <option>-i</option> option causes a case-insensitive
search.</para>
<para>The <option>-w</option> option matches only whole
words.</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>-r</option> (recursive) option searches files in
the current working directory and all subdirectories below
it.</para>
<para>The <option>-n</option> option lists the matching lines,
together with line numbers.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep -n Linux osinfo.txt</userinput>
<computeroutput>2:This is a file containing information about Linux.
6:The GPL governs the distribution of the Linux operating system.</computeroutput>
</screen>
</para>
<para>The <option>-v</option> (or <option>--invert-match</option>)
option <firstterm>filters out</firstterm> 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 *.xml # (number of occurrences of "txt" in "*.xml" 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 . # 3
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>The <option>--color</option> (or <option>--colour</option>)
option marks the matching string in color (on the console
or in an <firstterm>xterm</firstterm> window). Since
<firstterm>grep</firstterm> prints out each entire line
containing the matching pattern, this lets you see exactly
<emphasis>what</emphasis> is being matched. See also
the <option>-o</option> option, which shows only the
matching portion of the line(s).</para>
<example id="fromsh">
<title>Printing out the <firstterm>From</firstterm> lines in
stored e-mail messages</title>
<programlisting>&fromsh;</programlisting>
</example>
<para>When invoked with more than one target file given,
<command>grep</command> specifies which file contains
matches.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep Linux osinfo.txt misc.txt</userinput>
<computeroutput>osinfo.txt:This is a file containing information about Linux.
osinfo.txt:The GPL governs the distribution of the Linux operating system.
misc.txt:The Linux operating system is steadily gaining in popularity.</computeroutput>
</screen>
</para>
<tip>
<para>To force <command>grep</command> to show the filename
when searching only one target file, simply give
<filename>/dev/null</filename> as the second file.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>grep Linux osinfo.txt /dev/null</userinput>
<computeroutput>osinfo.txt:This is a file containing information about Linux.
osinfo.txt:The GPL governs the distribution of the Linux operating system.</computeroutput>
</screen>
</para>
</tip>
<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, especially in combination with the <option>-q</option>
option to suppress output.
<programlisting>SUCCESS=0 # if grep lookup succeeds
word=Linux
filename=data.file
grep -q "$word" "$filename" # The "-q" option
#+ causes nothing to echo to stdout.
if [ $? -eq $SUCCESS ]
# if grep -q "$word" "$filename" can replace lines 5 - 7.
then
echo "$word found in $filename"
else
echo "$word not found in $filename"
fi</programlisting>
</para>
<para><xref linkend="online"/> demonstrates how to use
<command>grep</command> to search for a word pattern in
a system logfile.</para>
<example id="grp">
<title>Emulating <firstterm>grep</firstterm> in a script</title>
<programlisting>&grp;</programlisting>
</example>
<para>How can <command>grep</command> search for two (or
more) separate patterns? What if you want
<command>grep</command> to display all lines in a file
or files that contain both <quote>pattern1</quote>
<emphasis>and</emphasis> <quote>pattern2</quote>?</para>
<para>One method is to <link
linkend="piperef">pipe</link> the result of <command>grep
pattern1</command> to <command>grep pattern2</command>.</para>
<para>For example, given the following file:</para>
<para>
<programlisting># Filename: tstfile
This is a sample file.
This is an ordinary text file.
This file does not contain any unusual text.
This file is not unusual.
Here is some text.</programlisting>
</para>
<para>Now, let's search this file for lines containing
<emphasis>both</emphasis> <quote>file</quote> and
<quote>text</quote> . . . </para>
<screen><prompt>bash$ </prompt><userinput>grep file tstfile</userinput>
<computeroutput># Filename: tstfile
This is a sample file.
This is an ordinary text file.
This file does not contain any unusual text.
This file is not unusual.</computeroutput>
<prompt>bash$ </prompt><userinput>grep file tstfile | grep text</userinput>
<computeroutput>This is an ordinary text file.
This file does not contain any unusual text.</computeroutput></screen>
<para>Now, for an interesting recreational use
of <firstterm>grep</firstterm> . . .</para>
<example id="cwsolver">
<title>Crossword puzzle solver</title>
<programlisting>&cwsolver;</programlisting>
</example>
<para><anchor id="egrepref"/><command>egrep</command>
-- <firstterm>extended grep</firstterm> -- 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 a bit more
flexible. It also allows the boolean |
(<firstterm>or</firstterm>) operator.
<screen><prompt>bash $ </prompt><userinput>egrep 'matches|Matches' file.txt</userinput>
<computeroutput>Line 1 matches.
Line 3 Matches.
Line 4 contains matches, but also Matches</computeroutput>
</screen>
</para>
<para><anchor id="fgrepref"/><command>fgrep</command> --
<firstterm>fast grep</firstterm> -- is the same as
<command>grep -F</command>. It does a literal string search
(no <link linkend="regexref">Regular Expressions</link>),
which generally speeds things up a bit.</para>
<note><para>On some Linux distros, <command>egrep</command> and
<command>fgrep</command> are symbolic links to, or aliases for
<command>grep</command>, but invoked with the
<option>-E</option> and <option>-F</option> options,
respectively.</para></note>
<example id="dictlookup">
<title>Looking up definitions in <citetitle
pubwork="book">Webster's 1913 Dictionary</citetitle></title>
<programlisting>&dictlookup;</programlisting>
</example>
<note><para>See also <xref linkend="qky"/> for an example
of speedy <firstterm>fgrep</firstterm> lookup on a large
text file.</para></note>
<para><anchor id="agrepref"/></para>
<para><command>agrep</command> (<firstterm>approximate
grep</firstterm>) extends the capabilities of
<command>grep</command> to approximate matching. The search
string may differ by a specified number of characters
from the resulting matches. This utility is not part of
the core Linux distribution.</para>
<para><anchor id="zegrepref"/></para>
<tip> <para>To search compressed files, use
<command>zgrep</command>, <command>zegrep</command>, or
<command>zfgrep</command>. These also work on non-compressed
files, though slower than plain <command>grep</command>,
<command>egrep</command>, <command>fgrep</command>.
They are handy for searching through a mixed set of files,
some compressed, some not.</para>
<para><anchor id="bzgrepref"/></para>
<para>To search <link linkend="bzipref">bzipped</link>
files, use <command>bzgrep</command>.</para> </tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lookref"/><command>look</command></term>
<listitem>
<indexterm>
<primary>look</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>look</secondary>
</indexterm>
<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>
<listitem>
<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>
<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 <link
linkend="batchprocref">batch</link> 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 <link
linkend="fieldref">fields</link> (columns) in structured
text files. Its syntax is similar to C.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="wcref"/><command>wc</command></term>
<listitem>
<indexterm>
<primary>wc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wc</secondary>
</indexterm>
<para><firstterm>wc</firstterm> gives a <quote>word
count</quote> on a file or I/O stream:
<screen><prompt>bash $ </prompt><userinput>wc /usr/share/doc/sed-4.1.2/README</userinput>
<computeroutput>13 70 447 README</computeroutput>
[13 lines 70 words 447 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 byte count.</para>
<para><userinput>wc -m</userinput> gives only the character count.</para>
<para><userinput>wc -L</userinput> gives only the length of the longest line.</para>
<para>Using <command>wc</command> to count how many
<filename>.txt</filename> files are in current working directory:
<programlisting>$ ls *.txt | wc -l
# Will work as long as none of the "*.txt" files
#+ have a linefeed embedded in their name.
# Alternative ways of doing this are:
# find . -maxdepth 1 -name \*.txt -print0 | grep -cz .
# (shopt -s nullglob; set -- *.txt; echo $#)
# Thanks, S.C.</programlisting>
</para>
<para>Using <command>wc</command> to total up the size of all the
files whose names begin with letters in the range d - h
<screen><prompt>bash$ </prompt><userinput>wc [d-h]* | grep total | awk '{print $3}'</userinput>
<computeroutput>71832</computeroutput>
</screen>
</para>
<para>Using <command>wc</command> to count the instances of the
word <quote>Linux</quote> in the main source file for
this book.
<screen><prompt>bash$ </prompt><userinput>grep Linux abs-book.xml | wc -l</userinput>
<computeroutput>138</computeroutput>
</screen>
</para>
<para>See also <xref linkend="ex52"/> and <xref
linkend="redir4"/>.</para>
<para>Certain commands include some of the
functionality of <command>wc</command> as options.
<programlisting>... | grep foo | wc -l
# This frequently used construct can be more concisely rendered.
... | grep -c foo
# Just use the "-c" (or "--count") option of grep.
# Thanks, S.C.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="trref"/><command>tr</command></term>
<listitem>
<indexterm>
<primary>tr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tr</secondary>
</indexterm>
<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><anchor id="troptions"/></para>
<para>The <option>-d</option> option deletes a range of
characters.
<programlisting>echo "abcdef" # abcdef
echo "abcdef" | tr -d b-d # aef
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>
<para>The <option>-c</option> <quote>complement</quote>
option <firstterm>inverts</firstterm> the character set to
match. With this option, <command>tr</command> acts only
upon those characters <emphasis>not</emphasis> matching
the specified set.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo "acfdeb123" | tr -c b-d +</userinput>
<computeroutput>+c+d+b++++</computeroutput></screen>
</para>
<para>Note that <command>tr</command> recognizes <link
linkend="posixref">POSIX character classes</link>.
<footnote><para>This is only true of the GNU version of
<command>tr</command>, not the generic version often found on
commercial UNIX systems.</para></footnote>
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo "abcd2ef1" | tr '[:alpha:]' -</userinput>
<computeroutput>----2--1</computeroutput>
</screen>
</para>
<example id="ex49">
<title><firstterm>toupper</firstterm>: Transforms a file
to all uppercase.</title>
<programlisting>&ex49;</programlisting>
</example>
<example id="lowercase">
<title><firstterm>lowercase</firstterm>: Changes all
filenames in working directory to lowercase.</title>
<programlisting>&lowercase;</programlisting>
</example>
<para><anchor id="trd2u"/></para>
<example id="du">
<title><firstterm>du</firstterm>: DOS to UNIX text file conversion.</title>
<programlisting>&du;</programlisting>
</example>
<example id="rot13">
<title><firstterm>rot13</firstterm>: ultra-weak encryption.</title>
<programlisting>&rot13;</programlisting>
</example>
<example id="cryptoquote">
<title>Generating <quote>Crypto-Quote</quote> Puzzles</title>
<programlisting>&cryptoquote;</programlisting>
</example>
<para><anchor id="jabh"/>Of course, <firstterm>tr</firstterm>
lends itself to <firstterm>code
obfuscation</firstterm>.</para>
<para><programlisting>#!/bin/bash
# jabh.sh
x="wftedskaebjgdBstbdbsmnjgz"
echo $x | tr "a-z" 'oh, turtleneck Phrase Jar!'
# Based on the Wikipedia "Just another Perl hacker" article.</programlisting></para>
<para><anchor id="trvariants"/></para>
<sidebar><title><firstterm>tr</firstterm> 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.
</para>
</sidebar>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="foldref"/><command>fold</command></term>
<listitem>
<indexterm>
<primary>fold</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fold</secondary>
</indexterm>
<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><anchor id="fmtref"/><command>fmt</command></term>
<listitem>
<indexterm>
<primary>fmt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fmt</secondary>
</indexterm>
<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><anchor id="colref"/><command>col</command></term>
<listitem>
<indexterm>
<primary>col</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>reverse line feed</secondary>
</indexterm>
<para>This deceptively named filter removes reverse line feeds
from an input stream. It also attempts to replace
whitespace with equivalent tabs. The chief use of
<command>col</command> is in filtering the output
from certain text processing utilities, such as
<command>groff</command> and <command>tbl</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="columnref"/><command>column</command></term>
<listitem>
<indexterm>
<primary>column</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>column</secondary>
</indexterm>
<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 <firstterm>column</firstterm> to format a directory
listing</title>
<programlisting>&colm;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="colrmref"/><command>colrm</command></term>
<listitem>
<indexterm>
<primary>colrm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>colrm</secondary>
</indexterm>
<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>
<caution><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></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="nlref"/><command>nl</command></term>
<listitem>
<indexterm>
<primary>nl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fmt</secondary>
</indexterm>
<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>
<para>The output of <command>nl</command> is very similar to
<userinput>cat -b</userinput>, since, by default
<command>nl</command> does not list blank lines.</para>
<example id="lnum">
<title><firstterm>nl</firstterm>: A self-numbering script.</title>
<programlisting>&lnum;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="prref"/><command>pr</command></term>
<listitem>
<indexterm>
<primary>pr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pr</secondary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>gettext</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>localization</secondary>
</indexterm>
<para>The GNU <command>gettext</command> package is a set of
utilities for <link linkend="localization">localizing</link>
and translating the text output of programs into foreign
languages. While originally intended for C programs, it
now supports quite a number of programming and scripting
languages.</para>
<para>The <command>gettext</command>
<emphasis>program</emphasis> works on shell scripts. See
the <replaceable>info page</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="msgfmtref"/><command>msgfmt</command></term>
<listitem>
<indexterm>
<primary>msgfmt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>localization</secondary>
</indexterm>
<para>A program for generating binary
message catalogs. It is used for <link
linkend="localization">localization</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="iconvref"/><command>iconv</command></term>
<listitem>
<indexterm>
<primary>iconv</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>encoding</secondary>
</indexterm>
<para>A utility for converting file(s) to a different encoding
(character set). Its chief use is for <link
linkend="localization">localization</link>.</para>
<para>
<programlisting># Convert a string from UTF-8 to UTF-16 and print to the BookList
function write_utf8_string {
STRING=$1
BOOKLIST=$2
echo -n "$STRING" | iconv -f UTF8 -t UTF16 | \
cut -b 3- | tr -d \\n >> "$BOOKLIST"
}
# From Peter Knowles' "booklistgen.sh" script
#+ for converting files to Sony Librie/PRS-50X format.
# (http://booklistgensh.peterknowles.com)</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="recoderef"/><command>recode</command></term>
<listitem>
<indexterm>
<primary>recode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>encoding</secondary>
</indexterm>
<para>Consider this a fancier version of
<command>iconv</command>, above. This very versatile utility
for converting a file to a different encoding scheme.
Note that <firstterm>recode</firstterm> is not part of the
standard Linux installation.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="texref"/><command>TeX</command></term>
<term><anchor id="gsref"/><command>gs</command></term>
<listitem>
<indexterm>
<primary>TeX</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>TeX</secondary>
</indexterm>
<indexterm>
<primary>gs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>Postscript</secondary>
</indexterm>
<para><command>TeX</command> and <command>Postscript</command>
are text markup languages used for preparing copy for
printing or formatted video display.</para>
<para><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>
<para><firstterm>Ghostscript</firstterm>
(<command>gs</command>) is a GPL-ed Postscript
interpreter.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="texexecref"/><command>texexec</command></term>
<listitem>
<indexterm>
<primary>texexec</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pdf</secondary>
</indexterm>
<para>Utility for processing <firstterm>TeX</firstterm> and
<firstterm>pdf</firstterm> files. Found in
<filename class="directory">/usr/bin</filename>
on many Linux distros, it is actually a <link
linkend="shwrapper">shell wrapper</link> that
calls <link linkend="perlref">Perl</link> to invoke
<firstterm>Tex</firstterm>.</para>
<para>
<programlisting>texexec --pdfarrange --result=Concatenated.pdf *pdf
# Concatenates all the pdf files in the current working directory
#+ into the merged file, Concatenated.pdf . . .
# (The --pdfarrange option repaginates a pdf file. See also --pdfcombine.)
# The above command-line could be parameterized and put into a shell script.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="enscriptref"/><command>enscript</command></term>
<listitem>
<indexterm>
<primary>enscript</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>PostScript</secondary>
</indexterm>
<para>Utility for converting plain text file to PostScript</para>
<para>For example, <command>enscript filename.txt -p filename.ps</command>
produces the PostScript output file
<filename>filename.ps</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="groffref"/><command>groff</command></term>
<term><anchor id="tblref"/><command>tbl</command></term>
<term><anchor id="eqnref"/><command>eqn</command></term>
<listitem>
<indexterm>
<primary>groff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>groff</secondary>
</indexterm>
<indexterm>
<primary>tbl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>table</secondary>
</indexterm>
<indexterm>
<primary>eqn</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>equation</secondary>
</indexterm>
<para>Yet another text markup and display formatting language
is <command>groff</command>. This is the enhanced GNU version
of the venerable UNIX <command>roff/troff</command> display
and typesetting package. <link linkend="manref">Manpages</link>
use <command>groff</command>.</para>
<para>The <command>tbl</command> table processing utility
is considered part of <command>groff</command>, as its
function is to convert table markup into
<command>groff</command> commands.</para>
<para>The <command>eqn</command> equation processing utility
is likewise part of <command>groff</command>, and
its function is to convert equation markup into
<command>groff</command> commands.</para>
<example id="manview">
<title><firstterm>manview</firstterm>: Viewing formatted manpages</title>
<programlisting>&manview;</programlisting>
</example>
<para>See also <xref linkend="maned"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lexref"/><command>lex</command></term>
<term><anchor id="yaccref"/><command>yacc</command></term>
<listitem>
<indexterm>
<primary>lex</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>flex</secondary>
</indexterm>
<indexterm>
<primary>yacc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bison</secondary>
</indexterm>
<para><anchor id="flexref"/></para>
<para>The <command>lex</command> lexical analyzer produces
programs for pattern matching. This has been replaced
by the nonproprietary <command>flex</command> on Linux
systems.</para>
<para><anchor id="bisonref"/></para>
<para>The <command>yacc</command> utility creates a
parser based on a set of specifications. This has been
replaced by the nonproprietary <command>bison</command>
on Linux systems.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Text Processing Commands -->
<sect1 id="filearchiv">
<title>File and Archiving Commands</title>
<variablelist id="faarchiving">
<title><anchor id="faarchiving1"/>Archiving</title>
<varlistentry>
<term><anchor id="tarref"/><command>tar</command></term>
<listitem>
<indexterm>
<primary>tar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tar</secondary>
</indexterm>
<para>The standard UNIX archiving utility.
<footnote><para>An <firstterm>archive</firstterm>,
in the sense discussed here, is simply a set of related
files stored in a single location.</para></footnote>
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
<firstterm>tar</firstterm> has been patched to accept
various compression filters, for example: <command>tar
czvf archive_name.tar.gz *</command>, which recursively
archives and <link linkend="gzipref">gzips</link>
all files in a directory tree except <link
linkend="dotfilesref">dotfiles</link> in the current
working directory (<link linkend="pwdref">$PWD</link>).
<footnote>
<para>
A <replaceable>tar czvf ArchiveName.tar.gz *</replaceable>
<emphasis>will</emphasis> include dotfiles in
subdirectories <emphasis>below</emphasis> the current
working directory. This is an undocumented GNU
<command>tar</command> <quote>feature.</quote>
</para>
</footnote>
</para>
<para>Some useful <command>tar</command> options:
<orderedlist>
<listitem><para><option>-c</option> create (a new
archive)</para></listitem>
<listitem><para><option>-x</option> extract (files from
existing archive)</para></listitem>
<listitem>
<para><option>--delete</option> delete (files
from existing archive)</para>
<caution><para>This option will not work on magnetic tape
devices.</para></caution>
</listitem>
<listitem><para><option>-r</option> append (files to
existing archive)</para></listitem>
<listitem><para><option>-A</option> append
(<firstterm>tar</firstterm> files to
existing archive)</para></listitem>
<listitem><para><option>-t</option> list (contents of
existing archive)</para></listitem>
<listitem><para><option>-u</option> update archive</para></listitem>
<listitem><para><option>-d</option> compare archive with
specified filesystem</para></listitem>
<listitem><para><option>--after-date</option> only process
files with a date stamp <emphasis>after</emphasis>
specified date</para></listitem>
<listitem>
<para><option>-z</option> <link
linkend="gzipref">gzip</link> the archive</para>
<para>(compress or uncompress, depending on whether
combined with the <option>-c</option> or
<option>-x</option>) option</para>
</listitem>
<listitem><para><option>-j</option>
<link linkend="bzipref">bzip2</link> the
archive</para></listitem>
</orderedlist>
</para>
<caution><para>It may be difficult to recover data from a
corrupted <firstterm>gzipped</firstterm> tar
archive. When archiving important files, make multiple
backups.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sharref"/><command>shar</command></term>
<listitem>
<indexterm>
<primary>shar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>archive</secondary>
</indexterm>
<para><firstterm>Shell archiving</firstterm> utility.
The text and/or binary 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, containing all the
necessary unarchiving commands, as well as the files
themselves. Unprintable binary characters in the target
file(s) are converted to printable ASCII characters in the
output <firstterm>shar</firstterm> file. <firstterm>Shar
archives</firstterm> still show up in Usenet newsgroups,
but otherwise <command>shar</command> has been replaced
by <command>tar</command>/<command>gzip</command>.
The <command>unshar</command> command unpacks
<firstterm>shar</firstterm> archives.</para> <para>The
<command>mailshar</command> command is a Bash script that
uses <command>shar</command> to concatenate multiple files
into a single one for e-mailing.
This script supports compression and <link
linkend="uuencoderef">uuencoding</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="arref"/><command>ar</command></term>
<listitem>
<indexterm>
<primary>ar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>archive</secondary>
</indexterm>
<para>Creation and manipulation utility for archives, mainly
used for binary object file libraries.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rpmref"/><command>rpm</command></term>
<listitem>
<indexterm>
<primary>rpm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>package manager</secondary>
</indexterm>
<para>The <firstterm>Red Hat Package Manager</firstterm>, or
<command>rpm</command> utility provides a wrapper for
source or binary archives. It includes commands for
installing and checking the integrity of packages, among
other things.</para>
<para>A simple <command>rpm -i package_name.rpm</command>
usually suffices to install a package, though there are many
more options available.</para>
<tip>
<para><userinput>rpm -qf</userinput> identifies which package a
file originates from.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>rpm -qf /bin/ls</userinput>
<computeroutput>coreutils-5.2.1-31</computeroutput>
</screen>
</para>
</tip>
<tip>
<para><userinput>rpm -qa</userinput> gives a
complete list of all installed <firstterm>rpm</firstterm> packages
on a given system. An <userinput>rpm -qa package_name</userinput>
lists only the package(s) corresponding to
<filename>package_name</filename>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>rpm -qa</userinput>
<computeroutput>redhat-logos-1.1.3-1
glibc-2.2.4-13
cracklib-2.7-12
dosfstools-2.7-1
gdbm-1.8.0-10
ksymoops-2.4.1-1
mktemp-1.5-11
perl-5.6.0-17
reiserfs-utils-3.x.0j-2
...</computeroutput>
<prompt>bash$ </prompt><userinput>rpm -qa docbook-utils</userinput>
<computeroutput>docbook-utils-0.6.9-2</computeroutput>
<prompt>bash$ </prompt><userinput>rpm -qa docbook | grep docbook</userinput>
<computeroutput>docbook-dtd31-sgml-1.0-10
docbook-style-dsssl-1.64-3
docbook-dtd30-sgml-1.0-10
docbook-dtd40-sgml-1.0-11
docbook-utils-pdf-0.6.9-2
docbook-dtd41-sgml-1.0-10
docbook-utils-0.6.9-2</computeroutput>
</screen>
</para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cpioref"/><command>cpio</command></term>
<listitem>
<indexterm>
<primary>cpio</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cpio</secondary>
</indexterm>
<para>This specialized archiving copy command
(<command>c</command>o<command>p</command>y
<command>i</command>nput and <command>o</command>utput)
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. With an
appropriate block size (for copying) specified, it
can be appreciably faster than <command>tar</command>.</para>
<example id="ex48">
<title>Using <firstterm>cpio</firstterm> to move a directory tree</title>
<programlisting>&ex48;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rpm2cpioref"/><command>rpm2cpio</command></term>
<listitem>
<indexterm>
<primary>rpm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cpio</secondary>
</indexterm>
<para>This command extracts a
<command>cpio</command> archive from an <link
linkend="rpmref">rpm</link> one.</para>
<example id="derpm">
<title>Unpacking an <firstterm>rpm</firstterm> archive</title>
<programlisting>&derpm;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="paxref"/><command>pax</command></term>
<listitem>
<indexterm>
<primary>pax</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>archive</secondary>
</indexterm>
<para>The <firstterm>pax</firstterm>
<command>p</command>ortable <command>a</command>rchive
e<command>x</command>change toolkit facilitates periodic
file backups and is designed to be cross-compatible
between various flavors of UNIX. It was designed
to replace <link linkend="tarref">tar</link> and <link
linkend="cpioref">cpio</link>.</para>
<para>
<programlisting>pax -wf daily_backup.pax ~/linux-server/files
# Creates a tar archive of all files in the target directory.
# Note that the options to pax must be in the correct order --
#+ pax -fw has an entirely different effect.
pax -f daily_backup.pax
# Lists the files in the archive.
pax -rf daily_backup.pax ~/bsd-server/files
# Restores the backed-up files from the Linux machine
#+ onto a BSD one.</programlisting>
</para>
<para>Note that <firstterm>pax</firstterm> handles many of
the standard archiving and compression commands.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="facompression">
<title><anchor id="facompression1"/>Compression</title>
<varlistentry>
<term><anchor id="gzipref"/><command>gzip</command></term>
<listitem>
<indexterm>
<primary>gzip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>gzip</secondary>
</indexterm>
<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>
<note><para>The <option>-c</option> option sends the output of
<command>gzip</command> to <filename>stdout</filename>. This
is useful when <link linkend="piperef">piping</link> to other
commands.</para></note>
<para><anchor id="zcatref"/></para>
<para>The <command>zcat</command> filter decompresses a
<firstterm>gzipped</firstterm> 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 <link
linkend="compressref">compress</link>
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 <firstterm>gzipped</firstterm>
files.</para></caution>
<para>See also <xref linkend="ex14"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bzipref"/><command>bzip2</command></term>
<listitem>
<indexterm>
<primary>bzip2</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bzip2</secondary>
</indexterm>
<para>An alternate compression utility, usually more efficient
(but slower) than <command>gzip</command>, especially on
large files. The corresponding decompression command is
<command>bunzip2</command>.</para>
<para>Similar to the <command>zcat</command> command,
<command>bzcat</command> decompresses a
<firstterm>bzipped2-ed</firstterm> file to
<filename>stdout</filename>.</para>
<note><para>Newer versions of <link
linkend="tarref">tar</link> have been patched with
<command>bzip2</command> support.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="compressref"/><command>compress</command></term>
<term><anchor id="uncompressref"/><command>uncompress</command></term>
<listitem>
<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>
<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>
<tip><para>The <command>znew</command> command transforms
<firstterm>compressed</firstterm> files into
<firstterm>gzipped</firstterm> ones.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sqref"/><command>sq</command></term>
<listitem>
<indexterm>
<primary>sq</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sq</secondary>
</indexterm>
<para>Yet another compression (<command>sq</command>ueeze)
utility, a filter that works only on sorted
<link linkend="asciidef">ASCII</link> word lists. It
uses the standard invocation syntax for a filter,
<command>sq &lt; 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><anchor id="zipref"/><command>zip</command></term>
<term><command>unzip</command></term>
<listitem>
<indexterm>
<primary>zip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pkzip.exe</secondary>
</indexterm>
<indexterm>
<primary>unzip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unzip</secondary>
</indexterm>
<para>Cross-platform file archiving and compression utility
compatible with DOS <firstterm>pkzip.exe</firstterm>.
<quote>Zipped</quote> archives seem to be a more
common medium of file exchange on the Internet than
<quote>tarballs.</quote></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="unarcref"/><command>unarc</command></term>
<term><command>unarj</command></term>
<term><command>unrar</command></term>
<listitem>
<indexterm>
<primary>unarc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>arc.exe</secondary>
</indexterm>
<indexterm>
<primary>unarj</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>arj.exe</secondary>
</indexterm>
<indexterm>
<primary>unrar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rar.exe</secondary>
</indexterm>
<para>These Linux utilities permit unpacking archives
compressed with the DOS <firstterm>arc.exe</firstterm>,
<firstterm>arj.exe</firstterm>, and
<firstterm>rar.exe</firstterm> programs.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lzmaref"/><command>lzma</command></term>
<term><command>unlzma</command></term>
<term><command>lzcat</command></term>
<listitem>
<indexterm>
<primary>lzma</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lzma</secondary>
</indexterm>
<indexterm>
<primary>unlzma</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unlzma</secondary>
</indexterm>
<indexterm>
<primary>lzcat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lzcat</secondary>
</indexterm>
<para>Highly efficient Lempel-Ziv-Markov compression.
The syntax of <firstterm>lzma</firstterm> is similar to
that of <firstterm>gzip</firstterm>. The <ulink
url="http://www.7-zip.org/sdk.html">7-zip Website</ulink>
has more information.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xzref"/><command>xz</command></term>
<term><command>unxz</command></term>
<term><command>xzcat</command></term>
<listitem>
<indexterm>
<primary>xz</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>xz</secondary>
</indexterm>
<indexterm>
<primary>unxz</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>unxz</secondary>
</indexterm>
<indexterm>
<primary>xzcat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>xzcat</secondary>
</indexterm>
<para>A new high-efficiency compression tool, backward compatible
with <firstterm>lzma</firstterm>, and with an invocation
syntax similar to <firstterm>gzip</firstterm>. For
more information, see the <ulink
url="http://en.wikipedia.org/wiki/Xz">Wikipedia
entry</ulink>.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="fainformation">
<title><anchor id="fainformation1"/>File Information</title>
<varlistentry>
<term><anchor id="fileref"/><command>file</command></term>
<listitem>
<indexterm>
<primary>file</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>file</secondary>
</indexterm>
<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>
<para>The <option>-f</option> option causes
<command>file</command> to run in <link
linkend="batchprocref">batch</link> mode, to read from
a designated file a list of filenames to analyze. The
<option>-z</option> option, when used on a compressed
target file, forces an attempt to analyze the uncompressed
file type.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>file test.tar.gz</userinput>
<computeroutput>test.tar.gz: gzip compressed data, deflated,
last modified: Sun Sep 16 13:34:51 2001, os: Unix</computeroutput>
<prompt>bash </prompt><userinput>file -z test.tar.gz</userinput>
<computeroutput>test.tar.gz: GNU tar archive (gzip compressed data, deflated,
last modified: Sun Sep 16 13:34:51 2001, os: Unix)</computeroutput>
</screen>
</para>
<para>
<programlisting># Find sh and Bash scripts in a given directory:
DIRECTORY=/usr/local/bin
KEYWORD=Bourne
# Bourne and Bourne-Again shell scripts
file $DIRECTORY/* | fgrep $KEYWORD
# Output:
# /usr/local/bin/burn-cd: Bourne-Again shell script text executable
# /usr/local/bin/burnit: Bourne-Again shell script text executable
# /usr/local/bin/cassette.sh: Bourne shell script text executable
# /usr/local/bin/copy-cd: Bourne-Again shell script text executable
# . . .</programlisting>
</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>
<listitem>
<indexterm>
<primary>which</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>which</secondary>
</indexterm>
<para><command>which command</command> gives the full path
to <quote>command.</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>
<para>For an interesting use of this command, see <xref
linkend="horserace"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whereisref"/><command>whereis</command></term>
<listitem>
<indexterm>
<primary>whereis</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whereis</secondary>
</indexterm>
<para>Similar to <command>which</command>, above,
<command>whereis command</command> gives the
full path to <quote>command,</quote> but also to its
<link linkend="manref">manpage</link>.</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>
<listitem>
<indexterm>
<primary>whatis</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whatis</secondary>
</indexterm>
<para><command>whatis command</command> looks up
<quote>command</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>Exploring <filename
class="directory">/usr/X11R6/bin</filename></title>
<programlisting>&what;</programlisting>
</example>
<para>See also <xref linkend="fileinfo"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="vdirref"/><command>vdir</command></term>
<listitem>
<indexterm>
<primary>vdir</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ls</secondary>
</indexterm>
<para>Show a detailed directory listing. The effect is similar to
<link linkend="lsref">ls -lb</link>.</para>
<para>This is one of the GNU
<firstterm>fileutils</firstterm>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>vdir</userinput>
<computeroutput>total 10
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo</computeroutput>
<prompt>bash </prompt><userinput>ls -l</userinput>
<computeroutput>total 10
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="locateref"/><command>locate</command></term>
<term><anchor id="slocateref"/><command>slocate</command></term>
<listitem>
<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>
<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><anchor id="getfaclref"/><command>getfacl</command></term>
<term><anchor id="setfaclref"/><command>setfacl</command></term>
<listitem>
<indexterm>
<primary>getfacl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>getfacl</secondary>
</indexterm>
<indexterm>
<primary>setfacl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>setfacl</secondary>
</indexterm>
<para>These commands <firstterm>retrieve</firstterm> or
<firstterm>set</firstterm> the <command>f</command>ile
<command>a</command>ccess <command>c</command>ontrol
<command>l</command>ist -- the <firstterm>owner</firstterm>,
<firstterm>group</firstterm>, and file permissions.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>getfacl *</userinput>
<computeroutput># file: test1.txt
# owner: bozo
# group: bozgrp
user::rw-
group::rw-
other::r--
# file: test2.txt
# owner: bozo
# group: bozgrp
user::rw-
group::rw-
other::r--</computeroutput>
<prompt>bash$ </prompt><userinput>setfacl -m u:bozo:rw yearly_budget.csv</userinput>
<prompt>bash$ </prompt><userinput>getfacl yearly_budget.csv</userinput>
<computeroutput># file: yearly_budget.csv
# owner: accountant
# group: budgetgrp
user::rw-
user:bozo:rw-
user:accountant:rw-
group::rw-
mask::rw-
other::r--</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="readlinkref"/><command>readlink</command></term>
<listitem>
<indexterm>
<primary>readlink</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>link</secondary>
</indexterm>
<para>Disclose the file that a symbolic link points to.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>readlink /usr/bin/awk</userinput>
<computeroutput>../../bin/gawk</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="stringsref"/><command>strings</command></term>
<listitem>
<indexterm>
<primary>strings</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>strings</secondary>
</indexterm>
<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 <firstterm>JFIF</firstterm>,
which would identify the file as a <firstterm>jpeg</firstterm>
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>
<example id="wstrings">
<title>An <quote>improved</quote>
<firstterm>strings</firstterm> command</title>
<programlisting>&wstrings;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="comparisonn">
<title><anchor id="comparisonn1"/>Comparison</title>
<varlistentry>
<term><anchor id="diffref"/><command>diff</command></term>
<term><command>patch</command></term>
<listitem>
<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>
<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. The
<option>-c</option> and <option>-u</option> options likewise
make the output of the command easier to interpret.</para>
<para>There are available various fancy frontends for
<command>diff</command>, such as <command>sdiff</command>,
<command>wdiff</command>, <command>xdiff</command>, and
<command>mgdiff</command>. </para>
<tip><para><anchor id="differr2"/>The <command>diff</command>
command returns an exit status of <errorcode>0</errorcode>
if the compared files are identical, and
<errorcode>1</errorcode> if they differ (or
<errorcode>2</errorcode> when <firstterm>binary</firstterm>
files are being compared). 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><anchor id="patchref"/></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 -r ~/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>
<tip>
<para><anchor id="zdiffref"/></para>
<para>Use <command>zdiff</command> to compare
<firstterm>gzipped</firstterm> files.</para>
</tip>
<tip>
<para><anchor id="diffstatref"/></para>
<para>Use <command>diffstat</command> to create
a histogram (point-distribution graph) of output from
<command>diff</command>.</para>
</tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="diff3ref"/><command>diff3</command></term>
<term><command>merge</command></term>
<listitem>
<indexterm>
<primary>diff3</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>diff3</secondary>
</indexterm>
<indexterm>
<primary>merge</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>merge</secondary>
</indexterm>
<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>
<para>
<screen><prompt>bash$ </prompt><userinput>diff3 file-1 file-2 file-3</userinput>
<computeroutput>====
1:1c
This is line 1 of "file-1".
2:1c
This is line 1 of "file-2".
3:1c
This is line 1 of "file-3"</computeroutput>
</screen>
</para>
<para><anchor id="mergeref"/>The <command>merge</command>
(3-way file merge) command is an interesting adjunct to
<firstterm>diff3</firstterm>. Its syntax is
<userinput>merge Mergefile file1 file2</userinput>.
The result is to output to <filename>Mergefile</filename>
the changes that lead from <filename>file1</filename>
to <filename>file2</filename>. Consider this command
a stripped-down version of <firstterm>patch</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sdiffref"/><command>sdiff</command></term>
<listitem>
<indexterm>
<primary>sdiff</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sdiff</secondary>
</indexterm>
<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><anchor id="cmpref"/><command>cmp</command></term>
<listitem>
<indexterm>
<primary>cmp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cmp</secondary>
</indexterm>
<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 <firstterm>cmp</firstterm> to compare two files
within a script.</title>
<programlisting>&filecomp;</programlisting>
</example>
<tip><para>Use <command>zcmp</command> on
<firstterm>gzipped</firstterm> files.</para></tip>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="commref"/><command>comm</command></term>
<listitem>
<indexterm>
<primary>comm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>comm</secondary>
</indexterm>
<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>
<para>This command is useful for comparing
<quote>dictionaries</quote> or <firstterm>word
lists</firstterm> -- sorted text files with one word per
line.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="fautils">
<title><anchor id="fautils1"/>Utilities</title>
<varlistentry>
<term><anchor id="basenameref"/><command>basename</command></term>
<listitem>
<indexterm>
<primary>basename</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>basename</secondary>
</indexterm>
<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><anchor id="dirnameref"/><command>dirname</command></term>
<listitem>
<indexterm>
<primary>dirname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dirname</secondary>
</indexterm>
<para>Strips the <command>basename</command> from
a filename, printing only the path information.</para>
<note>
<para><command>basename</command> and <command>dirname</command>
can operate on any arbitrary string. The argument
does not need to refer to an existing file, or
even be a filename for that matter (see <xref
linkend="daysbetween"/>).</para>
</note>
<example id="ex35">
<title><firstterm>basename</firstterm> and
<firstterm>dirname</firstterm></title>
<programlisting>&ex35;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="splitref"/><command>split</command></term>
<term><anchor id="csplitref"/><command>csplit</command></term>
<listitem>
<indexterm>
<primary>split</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>split</secondary>
</indexterm>
<indexterm>
<primary>csplit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>csplit</secondary>
</indexterm>
<para>These are utilities for splitting a file into smaller
chunks. Their usual use is for splitting up large files
in order to back them up on floppies or preparatory to
e-mailing or uploading them.</para>
<para>The <command>csplit</command> command splits a file
according to <firstterm>context</firstterm>, the split occuring
where patterns are matched.</para>
<example id="splitcopy">
<title>A script that copies itself in sections</title>
<programlisting>&splitcopy;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="faencencr">
<title><anchor id="faencencr1"/>Encoding and Encryption</title>
<varlistentry>
<term><anchor id="sumref"/><command>sum</command></term>
<term><anchor id="cksumref"/><command>cksum</command></term>
<term><anchor id="md5sumref"/><command>md5sum</command></term>
<term><anchor id="sha1sumref"/><command>sha1sum</command></term>
<listitem>
<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>
<indexterm>
<primary>command</primary>
<secondary>sha1sum</secondary>
</indexterm>
<para><anchor id="checksumref"/>These are utilities for
generating <firstterm>checksums</firstterm>. A
<firstterm>checksum</firstterm> is a number
<footnote><para>The checksum may be expressed as a
<firstterm>hexadecimal</firstterm> number, or to some
other base.</para></footnote>
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. For security applications, use
the <command>md5sum</command> (<command>m</command>essage
<command>d</command>igest <command>5</command>
check<command>sum</command>) command, or better yet, the
newer <command>sha1sum</command> (Secure Hash Algorithm).
<footnote><para>For even <emphasis>better</emphasis>
security, use the <firstterm>sha256sum</firstterm>,
<firstterm>sha512</firstterm>, and
<firstterm>sha1pass</firstterm>
commands.</para></footnote>
</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cksum /boot/vmlinuz</userinput>
<computeroutput>1670054224 804083 /boot/vmlinuz</computeroutput>
<prompt>bash$ </prompt><userinput>echo -n "Top Secret" | cksum</userinput>
<computeroutput>3391003827 10</computeroutput>
<prompt>bash$ </prompt><userinput>md5sum /boot/vmlinuz</userinput>
<computeroutput>0f43eccea8f09e0a0b2b5cf1dcf333ba /boot/vmlinuz</computeroutput>
<prompt>bash$ </prompt><userinput>echo -n "Top Secret" | md5sum</userinput>
<computeroutput>8babc97a6f62a4649716f4df8d61728f -</computeroutput>
</screen>
</para>
<note>
<para>The <command>cksum</command> command shows the size,
in bytes, of its target, whether file or
<filename>stdout</filename>.</para>
<para>The <command>md5sum</command> and
<command>sha1sum</command> commands display a
<link linkend="dashref2">dash</link> when they receive their input from
<filename>stdout</filename>.</para>
</note>
<example id="fileintegrity">
<title>Checking file integrity</title>
<programlisting>&fileintegrity;</programlisting>
</example>
<para>Also see <xref linkend="directoryinfo"/>, <xref
linkend="horserace"/>, and <xref linkend="randstring"/> for
creative uses of the <command>md5sum</command> command.</para>
<note>
<para>
There have been reports that the 128-bit
<command>md5sum</command> can be cracked, so the more secure
160-bit <command>sha1sum</command> is a welcome new addition
to the checksum toolkit.
</para>
<screen><prompt>bash$ </prompt><userinput>md5sum testfile</userinput>
<computeroutput>e181e2c8720c60522c4c4c981108e367 testfile</computeroutput>
<prompt>bash$ </prompt><userinput>sha1sum testfile</userinput>
<computeroutput>5d7425a9c08a66c3177f1e31286fa40986ffc996 testfile</computeroutput>
</screen></note>
<para>Security consultants have demonstrated that even
<command>sha1sum</command> can be compromised. Fortunately,
newer Linux distros include longer bit-length
<command>sha224sum</command>,
<command>sha256sum</command>,
<command>sha384sum</command>, and
<command>sha512sum</command> commands.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uuencoderef"/><command>uuencode</command></term>
<listitem>
<indexterm>
<primary>uuencode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uuencode</secondary>
</indexterm>
<para>This utility encodes binary files (images, sound files,
compressed files, etc.) into <link
linkend="asciidef">ASCII</link> characters, making
them suitable for transmission in the body of an
e-mail message or in a newsgroup posting. This is
especially useful where MIME (multimedia) encoding
is not available.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uudecoderef"/><command>uudecode</command></term>
<listitem>
<indexterm>
<primary>uudecode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uudecode</secondary>
</indexterm>
<para>This reverses the encoding, decoding
<firstterm>uuencoded</firstterm> 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><anchor id="mimencoderef"/><command>mimencode</command></term>
<term><anchor id="mmencoderef"/><command>mmencode</command></term>
<listitem>
<indexterm>
<primary>mimencode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mime</secondary>
</indexterm>
<indexterm>
<primary>mmencode</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>encode</secondary>
</indexterm>
<para>The <command>mimencode</command> and
<command>mmencode</command> commands process
multimedia-encoded e-mail attachments. Although
<firstterm>mail user agents</firstterm> (such as
<firstterm>pine</firstterm> or <firstterm>kmail</firstterm>)
normally handle this automatically, these particular
utilities permit manipulating such attachments manually from
the command-line or in <link linkend="batchprocref">batch
processing mode</link> by means of a shell script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cryptref"/><command>crypt</command></term>
<listitem>
<indexterm>
<primary>crypt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>crypt</secondary>
</indexterm>
<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 <firstterm>public key</firstterm>
cipher class, of which <firstterm>pgp</firstterm> 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><anchor id="opensslref"/><command>openssl</command></term>
<listitem>
<indexterm>
<primary>openssl</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>SSL</secondary>
</indexterm>
<para>This is an Open Source implementation of
<firstterm>Secure Sockets Layer</firstterm> encryption.
<programlisting># To encrypt a file:
openssl aes-128-ecb -salt -in file.txt -out file.encrypted \
-pass pass:my_password
# ^^^^^^^^^^^ User-selected password.
# aes-128-ecb is the encryption method chosen.
# To decrypt an openssl-encrypted file:
openssl aes-128-ecb -d -salt -in file.encrypted -out file.txt \
-pass pass:my_password
# ^^^^^^^^^^^ User-selected password.</programlisting></para>
<para><link linkend="piperef">Piping</link>
<firstterm>openssl</firstterm> to/from <link
linkend="tarref">tar</link> makes it possible to encrypt
an entire directory tree.
<programlisting># To encrypt a directory:
sourcedir="/home/bozo/testfiles"
encrfile="encr-dir.tar.gz"
password=my_secret_password
tar czvf - "$sourcedir" |
openssl des3 -salt -out "$encrfile" -pass pass:"$password"
# ^^^^ Uses des3 encryption.
# Writes encrypted file "encr-dir.tar.gz" in current working directory.
# To decrypt the resulting tarball:
openssl des3 -d -salt -in "$encrfile" -pass pass:"$password" |
tar -xzv
# Decrypts and unpacks into current working directory.</programlisting>
</para>
<para>Of course, <firstterm>openssl</firstterm> has many other uses,
such as obtaining signed <firstterm>certificates</firstterm>
for Web sites. See the <link linkend="inforef">info</link>
page.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="shredref"/><command>shred</command></term>
<listitem>
<indexterm>
<primary>shred</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>secure delete</secondary>
</indexterm>
<para>Securely erase a file by overwriting it multiple times with
random bit patterns before deleting it. This command has
the same effect as <xref linkend="blotout"/>, but does it
in a more thorough and elegant manner.</para>
<para>This is one of the GNU
<firstterm>fileutils</firstterm>.</para>
<caution><para>Advanced forensic technology may still be able to
recover the contents of a file, even after application of
<command>shred</command>.</para></caution>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="famisc">
<title><anchor id="famisc1"/>Miscellaneous</title>
<varlistentry>
<term><anchor id="mktempref"/><command>mktemp</command></term>
<listitem>
<indexterm>
<primary>temporary</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>filename</secondary>
</indexterm>
<para>Create a <firstterm>temporary file</firstterm>
<footnote><para>Creates a temporary
<firstterm>directory</firstterm> when invoked with the
<option>-d</option> option.</para></footnote>
with a <quote>unique</quote> filename. When invoked
from the command-line without additional arguments,
it creates a zero-length file in the <filename
class="directory">/tmp</filename> directory.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>mktemp</userinput>
<computeroutput>/tmp/tmp.zzsvql3154</computeroutput>
</screen>
</para>
<para><programlisting>PREFIX=filename
tempfile=`mktemp $PREFIX.XXXXXX`
# ^^^^^^ Need at least 6 placeholders
#+ in the filename template.
# If no filename template supplied,
#+ "tmp.XXXXXXXXXX" is the default.
echo "tempfile name = $tempfile"
# tempfile name = filename.QA2ZpY
# or something similar...
# Creates a file of that name in the current working directory
#+ with 600 file permissions.
# A "umask 177" is therefore unnecessary,
#+ but it's good programming practice nevertheless.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="makeref"/><command>make</command></term>
<listitem>
<indexterm>
<primary>make</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>Makefile</secondary>
</indexterm>
<para><anchor id="makefileref"/></para>
<para>Utility for building and compiling binary packages.
This can also be used for any set of operations triggered
by incremental changes in source files.</para>
<para>The <firstterm>make</firstterm> command checks a
<filename>Makefile</filename>, a list of file dependencies and
operations to be carried out.</para>
<para>The <firstterm>make</firstterm> utility is, in effect,
a powerful scripting language similar in many ways to
<firstterm>Bash</firstterm>, but with the capability of
recognizing <firstterm>dependencies</firstterm>. For in-depth
coverage of this useful tool set, see the <ulink
url="http://www.gnu.org/manual/manual.html">GNU software
documentation site</ulink>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="installref"/><command>install</command></term>
<listitem>
<indexterm>
<primary>install</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>install</secondary>
</indexterm>
<para>Special purpose file copying command, similar to
<link linkend="cpref">cp</link>, 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 prove
useful in installation scripts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dos2unixref"/><command>dos2unix</command></term>
<listitem>
<indexterm>
<primary>dos2unix</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>file converter</secondary>
</indexterm>
<para>This utility, written by Benjamin Lin and collaborators,
converts DOS-formatted text files (lines terminated by
CR-LF) to UNIX format (lines terminated by LF only),
and <link linkend="dosnewlines">vice-versa</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ptxref"/><command>ptx</command></term>
<listitem>
<indexterm>
<primary>ptx</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>index</secondary>
</indexterm>
<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><anchor id="moreref"/><command>more</command></term>
<term><anchor id="lessref"/><command>less</command></term>
<listitem>
<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>
<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
<filename>stdout</filename> . . . or of a script.</para>
<para>
An interesting application of <firstterm>more</firstterm>
is to <quote>test drive</quote> a command sequence,
to forestall potentially unpleasant consequences.
<programlisting>ls /home/bozo | awk '{print "rm -rf " $1}' | more
# ^^^^
# Testing the effect of the following (disastrous) command-line:
# ls /home/bozo | awk '{print "rm -rf " $1}' | sh
# Hand off to the shell to execute . . . ^^</programlisting>
</para>
<para>The <firstterm>less</firstterm> pager has the
interesting property of doing a formatted display of
<firstterm>man page</firstterm> source. See <xref
linkend="maned"/>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End File and Archiving Commands -->
<sect1 id="communications">
<title>Communications Commands</title>
<para>Certain of the following commands find use in
network data transfer and analysis, as well as in
<link linkend="cspammers">chasing spammers</link>.</para>
<variablelist id="communinfo">
<title><anchor id="communinfo1"/>Information and Statistics</title>
<varlistentry>
<term><anchor id="hostref"/><command>host</command></term>
<listitem>
<indexterm>
<primary>host</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>host</secondary>
</indexterm>
<para>Searches for information about an Internet host by name or
IP address, using DNS.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>host surfacemail.com</userinput>
<computeroutput>surfacemail.com. has address 202.92.42.236</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ipcalcref"/><command>ipcalc</command></term>
<listitem>
<indexterm>
<primary>ipcalc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ipcalc</secondary>
</indexterm>
<para>Displays IP information for a host.
With the <option>-h</option> option,
<command>ipcalc</command> does a reverse DNS lookup, finding
the name of the host (server) from the IP address.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ipcalc -h 202.92.42.236</userinput>
<computeroutput>HOSTNAME=surfacemail.com</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="nslookupref"/><command>nslookup</command></term>
<listitem>
<indexterm>
<primary>nslookup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>name server lookup</secondary>
</indexterm>
<para>Do an Internet <quote>name server lookup</quote>
on a host by IP address. This is essentially equivalent
to <command>ipcalc -h</command> or <command>dig -x
</command>. The command may be run either interactively
or noninteractively, i.e., from within a script.</para>
<para>The <command>nslookup</command> command has allegedly
been <quote>deprecated,</quote> but it is still useful.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>nslookup -sil 66.97.104.180</userinput>
<computeroutput>nslookup kuhleersparnis.ch
Server: 135.116.137.2
Address: 135.116.137.2#53
Non-authoritative answer:
Name: kuhleersparnis.ch</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="digref"/><command>dig</command></term>
<listitem>
<indexterm>
<primary>dig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>domain information groper</secondary>
</indexterm>
<para><command>D</command>omain <command>I</command>nformation
<command>G</command>roper. Similar to
<command>nslookup</command>, <firstterm>dig</firstterm> does
an Internet <firstterm>name server lookup</firstterm> on a host.
May be run from the command-line or from within a script.</para>
<para>Some interesting options to <firstterm>dig</firstterm> are
<option>+time=N</option> for setting a query timeout to
<parameter>N</parameter> seconds, <option>+nofail</option> for
continuing to query servers until a reply is received, and
<option>-x</option> for doing a reverse address lookup.</para>
<para>Compare the output of <command>dig -x</command> with
<command>ipcalc -h</command> and
<command>nslookup</command>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>dig -x 81.9.6.2</userinput>
<computeroutput>;; Got answer:
;; ->>HEADER&lt;&lt;- opcode: QUERY, status: NXDOMAIN, id: 11649
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
;; QUESTION SECTION:
;2.6.9.81.in-addr.arpa. IN PTR
;; AUTHORITY SECTION:
6.9.81.in-addr.arpa. 3600 IN SOA ns.eltel.net. noc.eltel.net.
2002031705 900 600 86400 3600
;; Query time: 537 msec
;; SERVER: 135.116.137.2#53(135.116.137.2)
;; WHEN: Wed Jun 26 08:35:24 2002
;; MSG SIZE rcvd: 91</computeroutput>
</screen>
</para>
<para><anchor id="spamlookup_0"/></para>
<example id="spamlookup">
<title>Finding out where to report a spammer</title>
<programlisting>&spamlookup;</programlisting>
</example>
<para><anchor id="isspammer_0"/></para>
<example id="isspammer">
<title>Analyzing a spam domain</title>
<programlisting>&isspammer;</programlisting>
</example>
<para>For a much more elaborate version of the above script, see
<xref linkend="isspammer2"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tracerouteref"/><command>traceroute</command></term>
<listitem>
<indexterm>
<primary>traceroute</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>traceroute</secondary>
</indexterm>
<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>
<para>
<screen><prompt>bash$ </prompt><userinput>traceroute 81.9.6.2</userinput>
<computeroutput>traceroute to 81.9.6.2 (81.9.6.2), 30 hops max, 38 byte packets
1 tc43.xjbnnbrb.com (136.30.178.8) 191.303 ms 179.400 ms 179.767 ms
2 or0.xjbnnbrb.com (136.30.178.1) 179.536 ms 179.534 ms 169.685 ms
3 192.168.11.101 (192.168.11.101) 189.471 ms 189.556 ms *
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pingref"/><command>ping</command></term>
<listitem>
<indexterm>
<primary>ping</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ping</secondary>
</indexterm>
<para>Broadcast an <replaceable>ICMP
ECHO_REQUEST</replaceable> packet to another machine,
either on a local or remote network. This is a
diagnostic tool for testing network connections,
and it should be used with caution.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ping localhost</userinput>
<computeroutput>PING localhost.localdomain (127.0.0.1) from 127.0.0.1 : 56(84) bytes of data.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=255 time=709 usec
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=255 time=286 usec
--- localhost.localdomain ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/mdev = 0.286/0.497/0.709/0.212 ms</computeroutput>
</screen>
</para>
<para>A successful <firstterm>ping</firstterm> returns
an <link linkend="exitstatusref">exit status</link> of
<errorcode>0</errorcode>. This can be tested for in a
script.</para>
<para><anchor id="ping0"/></para>
<para><programlisting> HNAME=news-15.net # Notorious spammer.
# HNAME=$HOST # Debug: test for localhost.
count=2 # Send only two pings.
if [[ `ping -c $count "$HNAME"` ]]
then
echo ""$HNAME" still up and broadcasting spam your way."
else
echo ""$HNAME" seems to be down. Pity."
fi</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whoisref"/><command>whois</command></term>
<listitem>
<indexterm>
<primary>whois</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>domain name server</secondary>
</indexterm>
<para>Perform a DNS (Domain Name System) lookup.
The <option>-h</option> option permits specifying which
particular <firstterm>whois</firstterm> server to query. See
<xref linkend="ex18"/> and <xref linkend="spamlookup"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fingerref"/><command>finger</command></term>
<listitem>
<indexterm>
<primary>finger</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>finger</secondary>
</indexterm>
<para>Retrieve information about users on a
network. Optionally, this command can display
a user's <filename>~/.plan</filename>,
<filename>~/.project</filename>, and
<filename>~/.forward</filename> files, if present.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>finger</userinput>
<computeroutput>Login Name Tty Idle Login Time Office Office Phone
bozo Bozo Bozeman tty1 8 Jun 25 16:59 (:0)
bozo Bozo Bozeman ttyp0 Jun 25 16:59 (:0.0)
bozo Bozo Bozeman ttyp1 Jun 25 17:07 (:0.0)</computeroutput>
<prompt>bash$ </prompt><userinput>finger bozo</userinput>
<computeroutput>Login: bozo Name: Bozo Bozeman
Directory: /home/bozo Shell: /bin/bash
Office: 2355 Clown St., 543-1234
On since Fri Aug 31 20:13 (MST) on tty1 1 hour 38 minutes idle
On since Fri Aug 31 20:13 (MST) on pts/0 12 seconds idle
On since Fri Aug 31 20:13 (MST) on pts/1
On since Fri Aug 31 20:31 (MST) on pts/2 1 hour 16 minutes idle
Mail last read Tue Jul 3 10:08 2007 (MST)
No Plan.</computeroutput>
</screen>
</para>
<para>Out of security considerations, many networks disable
<command>finger</command> and its associated daemon.
<footnote>
<para><anchor id="daemonref"/></para>
<para>A <firstterm>daemon</firstterm> is a background
process not attached to a terminal session. Daemons
perform designated services either at specified times
or explicitly triggered by certain events.</para>
<para>The word <quote>daemon</quote> means ghost in
Greek, and there is certainly something mysterious,
almost supernatural, about the way UNIX daemons
wander about behind the scenes, silently carrying
out their appointed tasks.</para>
</footnote>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="chfnref"/><command>chfn</command></term>
<listitem>
<indexterm>
<primary>chfn</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>finger</secondary>
</indexterm>
<para>Change information disclosed by the
<command>finger</command> command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="vrfyref"/><command>vrfy</command></term>
<listitem>
<indexterm>
<primary>vrfy</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>vrfy</secondary>
</indexterm>
<para>Verify an Internet e-mail address.</para>
<para>This command seems to be missing from newer Linux
distros.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commremote">
<title><anchor id="commremote1"/>Remote Host Access</title>
<varlistentry>
<term><anchor id="rxref"/><command>sx</command></term>
<term><command>rx</command></term>
<listitem>
<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>
<para>The <command>sx</command> and <command>rx</command>
command set serves to transfer files to and from a remote
host using the <firstterm>xmodem</firstterm> protocol. These
are generally part of a communications package, such as
<command>minicom</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rzref"/><command>sz</command></term>
<term><command>rz</command></term>
<listitem>
<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>
<para>The <command>sz</command> and <command>rz</command>
command set serves to transfer files to and from a remote
host using the <firstterm>zmodem</firstterm> protocol.
<firstterm>Zmodem</firstterm> has certain advantages over
<firstterm>xmodem</firstterm>, such as faster transmission
rate and resumption of interrupted file transfers.
Like <command>sx</command> and <command>rx</command>,
these are generally part of a communications package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ftpref"/><command>ftp</command></term>
<listitem>
<indexterm>
<primary>ftp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>file transfer</secondary>
</indexterm>
<para>Utility and protocol for uploading / downloading
files to or from a remote host. An ftp session can be automated
in a script (see <xref linkend="ex72"/> and <xref
linkend="encryptedpw"/>).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uucpref"/><command>uucp</command></term>
<term><anchor id="uuxref"/><command>uux</command></term>
<term><anchor id="curef"/><command>cu</command></term>
<listitem>
<indexterm>
<primary>uucp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uucp</secondary>
</indexterm>
<indexterm>
<primary>uux</primary>
<secondary>unix to unix execute</secondary>
</indexterm>
<indexterm>
<primary>cu</primary>
<secondary>call up</secondary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uucp</secondary>
</indexterm>
<para><command>uucp</command>: <firstterm>UNIX to UNIX
copy</firstterm>. 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. The advantage of <command>uucp</command>
is that it is fault-tolerant, so even if there is a service
interruption the copy operation will resume where it left
off when the connection is restored.</para>
<para>---</para>
<para><command>uux</command>: <firstterm>UNIX to UNIX
execute</firstterm>. Execute a command on a remote system.
This command is part of the <command>uucp</command>
package.</para>
<para>---</para>
<para><command>cu</command>: <command>C</command>all
<command>U</command>p a remote system and connect as a
simple terminal. It is a sort of dumbed-down version of
<link linkend="telnetref">telnet</link>. This command is
part of the <command>uucp</command> package.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="telnetref"/><command>telnet</command></term>
<listitem>
<indexterm>
<primary>telnet</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>telnet</secondary>
</indexterm>
<para>Utility and protocol for connecting to a remote host.</para>
<caution><para>The <firstterm>telnet</firstterm> protocol
contains security holes and should therefore probably be
avoided. Its use within a shell script is
<emphasis>not</emphasis> recommended.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="wgetref"/><command>wget</command></term>
<listitem>
<indexterm>
<primary>wget</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>download</secondary>
</indexterm>
<para>The <command>wget</command> utility
<firstterm>noninteractively</firstterm> retrieves or
downloads files from a Web or ftp site. It works well in a
script.</para>
<para><programlisting>wget -p http://www.xyz23.com/file01.html
# The -p or --page-requisite option causes wget to fetch all files
#+ required to display the specified page.
wget -r ftp://ftp.xyz24.net/~bozo/project_files/ -O $SAVEFILE
# The -r option recursively follows and retrieves all links
#+ on the specified site.
wget -c ftp://ftp.xyz25.net/bozofiles/filename.tar.bz2
# The -c option lets wget resume an interrupted download.
# This works with ftp servers and many HTTP sites.</programlisting></para>
<example id="quotefetch">
<title>Getting a stock quote</title>
<programlisting>&quotefetch;</programlisting>
</example>
<para>See also <xref linkend="wgetter2"/> and <xref
linkend="bashpodder"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lynxref"/><command>lynx</command></term>
<listitem>
<indexterm>
<primary>lynx</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>browser</secondary>
</indexterm>
<para>The <command>lynx</command> Web and file browser
can be used inside a script (with the
<option>-dump</option> option) to retrieve a file from a Web or
ftp site noninteractively.</para>
<para>
<programlisting>lynx -dump http://www.xyz23.com/file01.html &gt;$SAVEFILE</programlisting>
</para>
<para>With the <option>-traversal</option> option,
<command>lynx</command> starts at the HTTP URL specified
as an argument, then <quote>crawls</quote> through all
links located on that particular server. Used together
with the <option>-crawl</option> option, outputs page text
to a log file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rloginref"/><command>rlogin</command></term>
<listitem>
<indexterm>
<primary>rlogin</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote login</secondary>
</indexterm>
<para><replaceable>Remote login</replaceable>, initates a
session on a remote host. This command has security issues,
so use <link linkend="sshref">ssh</link> instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rshref"/><command>rsh</command></term>
<listitem>
<indexterm>
<primary>rsh</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote shell</secondary>
</indexterm>
<para><replaceable>Remote shell</replaceable>, executes
command(s) on a remote host. This has security issues,
so use <command>ssh</command> instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rcpref"/><command>rcp</command></term>
<listitem>
<indexterm>
<primary>rcp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote copy</secondary>
</indexterm>
<para><replaceable>Remote copy</replaceable>, copies files
between two different networked machines.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rsyncref"/><command>rsync</command></term>
<listitem>
<indexterm>
<primary>rsync</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>remote update</secondary>
</indexterm>
<para><replaceable>Remote synchronize</replaceable>, updates
(synchronizes) files
between two different networked machines.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>rsync -a ~/sourcedir/*txt /node1/subdirectory/</userinput>
</screen>
</para>
<example id="fc4upd">
<title>Updating FC4</title>
<programlisting>&fc4upd;</programlisting>
</example>
<para>See also <xref linkend="nightlybackup"/>.</para>
<note><para>Using <link linkend="rcpref">rcp</link>, <link
linkend="rsyncref">rsync</link>, and similar
utilities with security implications in a shell
script may not be advisable. Consider, instead, using
<command>ssh</command>, <link linkend="scpref">scp</link>,
or an <command>expect</command> script.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sshref"/><command>ssh</command></term>
<listitem>
<indexterm>
<primary>ssh</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>secure shell</secondary>
</indexterm>
<para><replaceable>Secure shell</replaceable>, logs onto
a remote host and executes commands there. This
secure replacement for <command>telnet</command>,
<command>rlogin</command>, <command>rcp</command>, and
<command>rsh</command> uses identity authentication
and encryption. See its <link linkend="manref">manpage</link>
for details.</para>
<example id="remote">
<title>Using <firstterm>ssh</firstterm></title>
<programlisting>&remote;</programlisting>
</example>
<caution>
<para>Within a loop, <command>ssh</command> may cause
unexpected behavior. According to a <ulink
url="http://groups-beta.google.com/group/comp.unix.shell/msg/dcb446b5fff7d230">
Usenet post</ulink> in the comp.unix shell archives,
<command>ssh</command> inherits the loop's
<filename>stdin</filename>. To remedy this, pass
<command>ssh</command> either the <option>-n</option>
or <option>-f</option> option.</para>
<para>Thanks, Jason Bechtel, for pointing this out.</para>
</caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="scpref"/><command>scp</command></term>
<listitem>
<indexterm>
<primary>scp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>secure copy</secondary>
</indexterm>
<para><replaceable>Secure copy</replaceable>, similar in
function to <command>rcp</command>, copies files between
two different networked machines, but does so using
authentication, and with a security level similar to
<command>ssh</command>.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commlocal">
<title><anchor id="commlocal1"/>Local Network</title>
<varlistentry>
<term><anchor id="writeref"/><command>write</command></term>
<listitem>
<indexterm>
<primary>write</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>write</secondary>
</indexterm>
<para>This is a utility for terminal-to-terminal communication.
It allows sending lines from your terminal (console or
<firstterm>xterm</firstterm>) 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><anchor id="netconfigref"/><command>netconfig</command></term>
<listitem>
<indexterm>
<primary>netconfig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>network</secondary>
</indexterm>
<para>A command-line utility for configuring a network adapter
(using <firstterm>DHCP</firstterm>). This command is native
to Red Hat centric Linux distros.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="commmail">
<title><anchor id="commmail1"/>Mail</title>
<varlistentry>
<term><command>mail</command></term>
<listitem>
<indexterm>
<primary>mail</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mail</secondary>
</indexterm>
<para>Send or read e-mail messages.</para>
<para>This stripped-down command-line mail client
works fine as a command embedded in a script.</para>
<example id="selfmailer">
<title>A script that mails itself</title>
<programlisting>&selfmailer;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mailtoref"/><command>mailto</command></term>
<listitem>
<indexterm>
<primary>mailto</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>MIME mail</secondary>
</indexterm>
<para>Similar to the <command>mail</command> command,
<command>mailto</command> sends e-mail messages
from the command-line or in a script. However,
<command>mailto</command> also permits sending MIME
(multimedia) messages.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mailstatsref"/><command>mailstats</command></term>
<listitem>
<indexterm>
<primary>mailstats</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>statistics</secondary>
</indexterm>
<para>Show <firstterm>mail statistics</firstterm>. This command
may be invoked only by <firstterm>root</firstterm>.</para>
<para>
<screen><prompt>root# </prompt><userinput>mailstats</userinput>
<computeroutput>Statistics from Tue Jan 1 20:32:08 2008
M msgsfr bytes_from msgsto bytes_to msgsrej msgsdis msgsqur Mailer
4 1682 24118K 0 0K 0 0 0 esmtp
9 212 640K 1894 25131K 0 0 0 local
=====================================================================
T 1894 24758K 1894 25131K 0 0 0
C 414 0</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="vacationref"/><command>vacation</command></term>
<listitem>
<indexterm>
<primary>vacation</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mail</secondary>
</indexterm>
<para>This utility automatically replies to e-mails that
the intended recipient is on vacation and temporarily
unavailable. It runs on a network, in conjunction with
<command>sendmail</command>, and is not applicable to a
dial-up POPmail account.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Communications Commands -->
<sect1 id="terminalccmds">
<title>Terminal Control Commands</title>
<variablelist id="termcommandlisting">
<title><anchor id="termcommandlisting1"/>Command affecting the console
or terminal</title>
<varlistentry>
<term><anchor id="tputref"/><command>tput</command></term>
<listitem>
<indexterm>
<primary>tput</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>terminal</secondary>
</indexterm>
<para>Initialize terminal and/or fetch information about it from
<database>terminfo</database> data. Various options permit
certain terminal operations: <command>tput clear</command>
is the equivalent of <link linkend="clearref">clear</link>;
<command>tput reset</command> is the equivalent
of <link linkend="resetref">reset</link>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>tput longname</userinput>
<computeroutput>xterm terminal emulator (X Window System)</computeroutput>
</screen>
</para>
<para>Issuing a <command>tput cup X Y</command> moves
the cursor to the (X,Y) coordinates in the current
terminal. A <command>clear</command> to erase the terminal
screen would normally precede this.</para>
<para>
Some interesting options to <firstterm>tput</firstterm> are:
<itemizedlist>
<listitem><para><option>bold</option>, for high-intensity
text</para></listitem>
<listitem><para><option>smul</option>, to underline text
in the terminal</para></listitem>
<listitem><para><option>smso</option>, to render text in
reverse</para></listitem>
<listitem><para><option>sgr0</option>, to reset the terminal
parameters (to normal), without clearing the
screen</para></listitem>
</itemizedlist>
</para>
<para>Example scripts using <firstterm>tput</firstterm>:
<orderedlist id="tputexamples">
<listitem><para><xref linkend="colorecho"/></para></listitem>
<listitem><para><xref linkend="ex30a"/></para></listitem>
<listitem><para><xref linkend="homework"/></para></listitem>
<listitem><para><xref linkend="nim"/></para></listitem>
<listitem><para><xref linkend="poem"/></para></listitem>
</orderedlist>
</para>
<para>Note that <link linkend="sttyref">stty</link> offers
a more powerful command set for controlling a terminal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="infocmpref"/><command>infocmp</command></term>
<listitem>
<indexterm>
<primary>infocmp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>terminal</secondary>
</indexterm>
<para>This command prints out extensive information about the
current terminal. It references the
<firstterm>terminfo</firstterm> database.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>infocmp</userinput>
<computeroutput># Reconstructed via infocmp from file:
/usr/share/terminfo/r/rxvt
rxvt|rxvt terminal emulator (X Window System),
am, bce, eo, km, mir, msgr, xenl, xon,
colors#8, cols#80, it#8, lines#24, pairs#64,
acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
bel=^G, blink=\E[5m, bold=\E[1m,
civis=\E[?25l,
clear=\E[H\E[2J, cnorm=\E[?25h, cr=^M,
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="resetref"/><command>reset</command></term>
<listitem>
<indexterm>
<primary>reset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>reset</secondary>
</indexterm>
<para>Reset terminal parameters and clear text screen. As with
<command>clear</command>, the cursor and prompt reappear in the
upper lefthand corner of the terminal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="clearref"/><command>clear</command></term>
<listitem>
<indexterm>
<primary>clear</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>clear</secondary>
</indexterm>
<para>The <command>clear</command> command simply clears
the text screen at the console or in an
<firstterm>xterm</firstterm>. 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><anchor id="resizeref"/><command>resize</command></term>
<listitem>
<indexterm>
<primary>resize</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>resize</secondary>
</indexterm>
<para>Echoes commands necessary to set <varname>$TERM</varname>
and <varname>$TERMCAP</varname> to duplicate the
<firstterm>size</firstterm> (dimensions) of the current
terminal.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>resize</userinput>
<computeroutput>set noglob;
setenv COLUMNS '80';
setenv LINES '24';
unset noglob;</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="scriptref"/><command>script</command></term>
<listitem>
<indexterm>
<primary>script</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>script</secondary>
</indexterm>
<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,
creates a record of a session.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Terminal Control Commands -->
<sect1 id="mathc">
<title>Math Commands</title>
<variablelist id="mathcommandlisting">
<title><anchor id="mathcommandlisting1"/><quote>Doing the
numbers</quote></title>
<varlistentry>
<term><anchor id="factorref"/><command>factor</command></term>
<listitem>
<indexterm>
<primary>factor</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>factor</secondary>
</indexterm>
<para>Decompose an integer into prime factors.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>factor 27417</userinput>
<computeroutput>27417: 3 13 19 37</computeroutput>
</screen>
</para>
<example id="primes2">
<title>Generating prime numbers</title>
<programlisting>&primes2;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bcref"/><command>bc</command></term>
<listitem>
<indexterm>
<primary>bc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bc</secondary>
</indexterm>
<para>Bash can't handle floating point calculations, and
it lacks operators for certain important mathematical
functions. Fortunately, <command>bc</command> gallops to
the rescue.</para>
<para>Not just a versatile, arbitrary precision calculation
utility, <command>bc</command> offers many of the facilities of
a programming language. It has a syntax vaguely resembling
<command>C</command>.</para>
<para>Since it is a fairly well-behaved UNIX utility, and may
therefore be used in a <link linkend="piperef">pipe</link>,
<command>bc</command> comes in handy in scripts.</para>
<para><anchor id="bctemplate"/></para>
<para>Here is a simple template for using
<command>bc</command> to calculate a script
variable. This uses <link linkend="commandsubref">command
substitution</link>.</para>
<para>
<screen>
<userinput>variable=$(echo "OPTIONS; OPERATIONS" | bc)</userinput>
</screen>
</para>
<para><anchor id="monthlypmt0"/></para>
<example id="monthlypmt">
<title>Monthly Payment on a Mortgage</title>
<programlisting>&monthlypmt;</programlisting>
</example>
<para><anchor id="base0"/></para>
<example id="base">
<title>Base Conversion</title>
<programlisting>&base;</programlisting>
</example>
<para><anchor id="bcheredoc"/></para>
<para>An alternate method of invoking <command>bc</command>
involves using a <link linkend="heredocref">here
document</link> embedded within a <link
linkend="commandsubref">command substitution</link>
block. This is especially appropriate when a script
needs to pass a list of options and commands to
<command>bc</command>.</para>
<para>
<programlisting>variable=`bc &lt;&lt; LIMIT_STRING
options
statements
operations
LIMIT_STRING
`
...or...
variable=$(bc &lt;&lt; LIMIT_STRING
options
statements
operations
LIMIT_STRING
)</programlisting>
</para>
<example id="altbc">
<title>Invoking <firstterm>bc</firstterm> using a <firstterm>here
document</firstterm></title>
<programlisting>&altbc;</programlisting>
</example>
<para><anchor id="cannonref"/></para>
<example id="cannon">
<title>Calculating PI</title>
<programlisting>&cannon;</programlisting>
</example>
<para>See also <xref linkend="stddev"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="dcref"/><command>dc</command></term>
<listitem>
<indexterm>
<primary>dc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dc</secondary>
</indexterm>
<para>The <command>dc</command> (<command>d</command>esk
<command>c</command>alculator) utility is <link
linkend="stackdefref">stack-oriented</link>
and uses RPN (<firstterm>Reverse Polish Notation</firstterm>).
Like <command>bc</command>, it has much of the power of
a programming language.</para>
<para>Similar to the procedure with <command>bc</command>,
<link linkend="echoref">echo</link> a command-string
to <command>dc</command>.</para>
<para>
<programlisting>echo "[Printing a string ... ]P" | dc
# The P command prints the string between the preceding brackets.
# And now for some simple arithmetic.
echo "7 8 * p" | dc # 56
# Pushes 7, then 8 onto the stack,
#+ multiplies ("*" operator), then prints the result ("p" operator).</programlisting>
</para>
<para>Most persons avoid <command>dc</command>, because
of its non-intuitive input and rather cryptic
operators. Yet, it has its uses.</para>
<example id="hexconvert">
<title>Converting a decimal number to hexadecimal</title>
<programlisting>&hexconvert;</programlisting>
</example>
<para>Studying the <link linkend="inforef">info</link> page for
<command>dc</command> is a painful path to understanding its
intricacies. There seems to be a small, select group of
<emphasis>dc wizards</emphasis> who delight in showing off
their mastery of this powerful, but arcane utility.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>echo "16i[q]sa[ln0=aln100%Pln100/snlbx]sbA0D68736142snlbxq" | dc</userinput>
<computeroutput>Bash</computeroutput>
</screen>
</para>
<para><anchor id="goldenratio"/>
<programlisting>dc &lt;&lt;&lt; 10k5v1+2/p # 1.6180339887
# ^^^ Feed operations to dc using a Here String.
# ^^^ Pushes 10 and sets that as the precision (10k).
# ^^ Pushes 5 and takes its square root
# (5v, v = square root).
# ^^ Pushes 1 and adds it to the running total (1+).
# ^^ Pushes 2 and divides the running total by that (2/).
# ^ Pops and prints the result (p)
# The result is 1.6180339887 ...
# ... which happens to be the Pythagorean Golden Ratio, to 10 places.</programlisting>
</para>
<example id="factr">
<title>Factoring</title>
<programlisting>&factr;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="awkmath"/><command>awk</command></term>
<listitem>
<indexterm>
<primary>awk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>math</secondary>
</indexterm>
<para>Yet another way of doing floating point math in
a script is using <link linkend="awkref">awk's</link>
built-in math functions in a <link linkend="shwrapper">shell
wrapper</link>.</para>
<example id="hypot">
<title>Calculating the hypotenuse of a triangle</title>
<programlisting>&hypot;</programlisting>
</example>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Math Commands -->
<sect1 id="extmisc">
<title>Miscellaneous Commands</title>
<variablelist id="misccommandlisting">
<title><anchor id="misccommandlisting1"/>Command that fit in no
special category</title>
<varlistentry>
<term><anchor id="jotref"/><command>jot</command></term>
<term><anchor id="seqref"/><command>seq</command></term>
<listitem>
<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>
<para>These utilities emit a sequence of integers, with a
user-selectable increment.</para>
<para>The default separator character between each integer is a
newline, but this can be changed with the <option>-s</option>
option.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>seq 5</userinput>
<computeroutput>1
2
3
4
5</computeroutput>
<prompt>bash$ </prompt><userinput>seq -s : 5</userinput>
<computeroutput>1:2:3:4:5</computeroutput>
</screen>
</para>
<para>Both <command>jot</command> and <command>seq</command>
come in handy in a <link linkend="forloopref1">for
loop</link>.</para>
<example id="ex53">
<title>Using <firstterm>seq</firstterm> to generate loop
arguments</title>
<programlisting>&ex53;</programlisting>
</example>
<para>A simpler example:</para>
<para><programlisting># Create a set of 10 files,
#+ named file.1, file.2 . . . file.10.
COUNT=10
PREFIX=file
for filename in `seq $COUNT`
do
touch $PREFIX.$filename
# Or, can do other operations,
#+ such as rm, grep, etc.
done</programlisting></para>
<example id="lettercount">
<title>Letter Count"</title>
<programlisting>&lettercount;</programlisting>
</example>
<note>
<para>Somewhat more capable than <firstterm>seq</firstterm>,
<command>jot</command> is a classic UNIX
utility that is not normally included in a standard Linux
distro. However, the source <firstterm>rpm</firstterm>
is available for download from the <ulink
url="http://www.mit.edu/afs/athena/system/rhlinux/athena-9.0/free/SRPMS/athena-jot-9.0-3.src.rpm">
MIT repository</ulink>.</para>
<para><anchor id="jotrandom"/></para>
<para>Unlike <firstterm>seq</firstterm>, <command>jot</command> can
generate a sequence of random numbers, using the <option>-r</option>
option.</para>
<para><screen><prompt>bash$ </prompt><userinput>jot -r 3 999</userinput>
<computeroutput>1069
1272
1428</computeroutput></screen></para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="getopty"/><command>getopt</command></term>
<listitem>
<indexterm>
<primary>getopt</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>option</secondary>
</indexterm>
<para>The <command>getopt</command> command
parses command-line options preceded by a <link
linkend="dashref">dash</link>. This external command
corresponds to the <link linkend="getoptsx">getopts</link>
Bash builtin. Using <command>getopt</command> permits
handling long options by means of the <option>-l</option>
flag, and this also allows parameter reshuffling.</para>
<example id="ex33a">
<title>Using <firstterm>getopt</firstterm> to parse command-line
options</title>
<programlisting>&ex33a;</programlisting>
</example>
<note>
<para>As <emphasis>Peggy Russell</emphasis> points out:</para>
<para>It is often necessary to include an <link
linkend="evalref">eval</link> to correctly process
<link linkend="whitespaceref">whitespace</link> and
<firstterm>quotes</firstterm>.
<programlisting>args=$(getopt -o a:bc:d -- "$@")
eval set -- "$args"</programlisting></para>
</note>
<para>See <xref linkend="getoptsimple"/> for a simplified emulation
of <command>getopt</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="runpartsref"/><command>run-parts</command></term>
<listitem>
<indexterm>
<primary>run-parts</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>run-parts</secondary>
</indexterm>
<para>The <command>run-parts</command> command
<footnote><para>This is actually a script adapted from
the Debian Linux distribution.</para></footnote>
executes all the scripts in a target directory, sequentially
in ASCII-sorted filename order. Of course, the scripts
need to have execute permission.</para>
<para>The <link linkend="cronref">cron</link> <link
linkend="daemonref">daemon</link> invokes
<command>run-parts</command> to run the scripts in
the <filename class="directory">/etc/cron.*</filename>
directories.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="yesref"/><command>yes</command></term>
<listitem>
<indexterm>
<primary>yes</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>yes</secondary>
</indexterm>
<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>.</para>
<para>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 <firstterm>expect</firstterm>.</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>Caution advised when piping
<firstterm>yes</firstterm> to a potentially dangerous
system command, such as <link linkend="fsckref">fsck</link>
or <link linkend="fdiskref">fdisk</link>. It might have
unintended consequences.</para></warning>
<note>
<para>The <firstterm>yes</firstterm> command parses variables,
or more accurately, it echoes parsed variables.
For example:</para>
<para>
<screen><prompt>bash$ </prompt><userinput>yes $BASH_VERSION</userinput>
<computeroutput>3.1.17(1)-release
3.1.17(1)-release
3.1.17(1)-release
3.1.17(1)-release
3.1.17(1)-release
. . .</computeroutput>
</screen>
</para>
<para>
This particular <quote>feature</quote> may be used
to create a <emphasis>very large</emphasis> ASCII file on the fly:
<screen><prompt>bash$ </prompt><userinput>yes $PATH > huge_file.txt</userinput>
<userinput>Ctl-C</userinput>
</screen>
Hit <userinput>Ctl-C</userinput> <emphasis>very
quickly</emphasis>, or you just might get more than you
bargained for. . . .
</para>
</note>
<para><anchor id="yesemu"/>The <firstterm>yes</firstterm>
command may be emulated in a very simple script <link
linkend="functionref">function</link>.</para>
<para><programlisting>yes ()
{ # Trivial emulation of "yes" ...
local DEFAULT_TEXT="y"
while [ true ] # Endless loop.
do
if [ -z "$1" ]
then
echo "$DEFAULT_TEXT"
else # If argument ...
echo "$1" # ... expand and echo it.
fi
done # The only things missing are the
} #+ --help and --version options.</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="bannerref"/><command>banner</command></term>
<listitem>
<indexterm>
<primary>banner</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>banner</secondary>
</indexterm>
<para>Prints arguments as a large vertical banner to
<filename>stdout</filename>, using an <link
linkend="asciidef">ASCII</link> character (default
'#'). This may be redirected to a printer for
hardcopy.</para>
<para>Note that <firstterm>banner</firstterm> has been
dropped from many Linux distros, presumably because it
is no longer considered useful.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="printenvref"/><command>printenv</command></term>
<listitem>
<indexterm>
<primary>printenv</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>environment</secondary>
</indexterm>
<para>Show all the <link linkend="envref">environmental
variables</link> 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><anchor id="lpref"/><command>lp</command></term>
<listitem>
<indexterm>
<primary>lp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lpr</secondary>
</indexterm>
<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 <firstterm>print queue</firstterm> 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.
<footnote><para>Large mechanical <firstterm>line
printers</firstterm> printed a single
line of type at a time onto joined
sheets of <firstterm>greenbar</firstterm>
paper, to the accompaniment of <ulink
url="http://www.columbia.edu/cu/computinghistory/1403.html">a
great deal of noise</ulink>. The hardcopy
thusly printed was referred to as a
<firstterm>printout</firstterm>.</para></footnote>
</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 <link
linkend="groffref">groff</link> and
<firstterm>Ghostscript</firstterm> 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><anchor id="teeref"/><command>tee</command></term>
<listitem>
<indexterm>
<primary>tee</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tee</secondary>
</indexterm>
<para>[UNIX borrows an idea from the plumbing trade.]</para>
<para>This is a redirection operator, but with a difference. Like the
plumber's <firstterm>tee,</firstterm> it permits <quote>siphoning
off</quote> <emphasis>to a file </emphasis>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>
(redirection)
|----> to file
|
==========================|====================
command ---> command ---> |tee ---> command ---> ---> output of pipe
===============================================
</screen>
<para><programlisting>cat listfile* | sort | tee check.file | uniq > result.file
# ^^^^^^^^^^^^^^ ^^^^
# The file "check.file" contains the concatenated sorted "listfiles,"
#+ before the duplicate lines are removed by 'uniq.'</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mkfiforef"/><command>mkfifo</command></term>
<listitem>
<indexterm>
<primary>mkfifo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkfifo</secondary>
</indexterm>
<para><anchor id="namedpiperef"/>This obscure command
creates a <firstterm>named pipe</firstterm>, a temporary
<firstterm>first-in-first-out buffer</firstterm> 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"><citetitle
pubwork="journal">Linux
Journal</citetitle></ulink>.</para></footnote>
Typically, one process writes to the FIFO, and the other
reads from it. See <xref linkend="fifo"/>.</para>
<para>
<programlisting>#!/bin/bash
# This short script by Omair Eshkenazi.
# Used in ABS Guide with permission (thanks!).
mkfifo pipe1 # Yes, pipes can be given names.
mkfifo pipe2 # Hence the designation "named pipe."
(cut -d' ' -f1 | tr "a-z" "A-Z") >pipe2 &lt;pipe1 &amp;
ls -l | tr -s ' ' | cut -d' ' -f3,9- | tee pipe1 |
cut -d' ' -f2 | paste - pipe2
rm -f pipe1
rm -f pipe2
# No need to kill background processes when script terminates (why not?).
exit $?
Now, invoke the script and explain the output:
sh mkfifo-example.sh
4830.tar.gz BOZO
pipe1 BOZO
pipe2 BOZO
mkfifo-example.sh BOZO
Mixed.msg BOZO</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pathchkref"/><command>pathchk</command></term>
<listitem>
<indexterm>
<primary>pathchk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pathchk</secondary>
</indexterm>
<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.</para>
<para>Unfortunately, <command>pathchk</command> does
not return a recognizable error code, and it is therefore
pretty much useless in a script. Consider instead the
<link linkend="rtif">file test operators</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ddref"/><command>dd</command></term>
<listitem>
<indexterm>
<primary>dd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dd</secondary>
</indexterm>
<para>Though this somewhat obscure and much feared
<command>d</command>ata <command>d</command>uplicator
command originated as a utility for exchanging
data on magnetic tapes between UNIX minicomputers
and IBM mainframes, it still has its uses.
The <command>dd</command> command simply copies a
file (or <filename>stdin/stdout</filename>), but with
conversions. <anchor id="ddconversions"/>Possible conversions
include ASCII/EBCDIC,
<footnote><para><acronym>EBCDIC</acronym> (pronounced
<quote>ebb-sid-ick</quote>) is an acronym for Extended
Binary Coded Decimal Interchange Code, an obsolete
IBM data format. A bizarre application of
the <option>conv=ebcdic</option> option of
<command>dd</command> is as a quick 'n easy, but not
very secure text file encoder.
<programlisting>cat $file | dd conv=swab,ebcdic > $file_encrypted
# Encode (looks like gibberish).
# Might as well switch bytes (swab), too, for a little extra obscurity.
cat $file_encrypted | dd conv=swab,ascii > $file_plaintext
# Decode.</programlisting>
</para></footnote>
upper/lower case, swapping of byte pairs between input
and output, and skipping and/or truncating the head or
tail of the input file.</para>
<para>
<programlisting># Converting a file to all uppercase:
dd if=$filename conv=ucase > $filename.uppercase
# lcase # For lower case conversion</programlisting>
</para>
<para><anchor id="ddoptions"/></para>
<para>Some basic options to <command>dd</command> are:
<itemizedlist>
<listitem>
<para>if=INFILE</para>
<para>INFILE is the <firstterm>source</firstterm>
file.</para>
</listitem>
<listitem>
<para>of=OUTFILE</para>
<para>OUTFILE is the <firstterm>target</firstterm>
file, the file that will have the data written to it.</para>
</listitem>
<listitem>
<para>bs=BLOCKSIZE</para>
<para>This is the size of each block of data being read
and written, usually a power of 2.</para>
</listitem>
<listitem>
<para>skip=BLOCKS</para>
<para>How many blocks of data to skip in INFILE before
starting to copy. This is useful when the INFILE has
<quote>garbage</quote> or garbled data in its
header or when it is desirable to copy only a portion
of the INFILE.</para>
</listitem>
<listitem>
<para>seek=BLOCKS</para>
<para>How many blocks of data to skip in OUTFILE before
starting to copy, leaving blank data at beginning
of OUTFILE.</para>
</listitem>
<listitem>
<para>count=BLOCKS</para>
<para>Copy only this many blocks of data, rather than the
entire INFILE.</para>
</listitem>
<listitem>
<para>conv=CONVERSION</para>
<para>Type of conversion to be applied to INFILE data
before copying operation.</para>
</listitem>
</itemizedlist>
</para>
<para>A <userinput>dd --help</userinput> lists all the
options this powerful utility takes.</para>
<example id="selfcopy">
<title>A script that copies itself</title>
<programlisting>&selfcopy;</programlisting>
</example>
<example id="exercisingdd">
<title>Exercising <firstterm>dd</firstterm></title>
<programlisting>&exercisingdd;</programlisting>
</example>
<para><anchor id="ddkeystrokes"/></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><anchor id="ddrandom"/></para>
<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><anchor id="ddcopy"/></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.</para>
<para>
<userinput>dd if=kernel-image of=/dev/fd0H1440</userinput>
</para>
<para>Similarly, <command>dd</command> can copy the entire
contents of a floppy, even one formatted with a
<quote>foreign</quote> OS, to the hard drive as an
image file.</para>
<para>
<userinput>dd if=/dev/fd0 of=/home/bozo/projects/floppy.img</userinput>
</para>
<para><anchor id="bfs"/>Likewise, <command>dd</command>
can create bootable flash drives and SD cards.</para>
<para><userinput>dd if=image.iso of=/dev/sdb</userinput></para>
<para><anchor id="rpsdcard01"/></para>
<example id="rpsdcard">
<title>Preparing a bootable SD card for the
<emphasis>Raspberry Pi</emphasis></title>
<programlisting>&rpsdcard;</programlisting>
</example>
<para><anchor id="ddswap"/></para>
<para>
Other applications of <command>dd</command> include
initializing temporary swap files (<xref linkend="ex73"/>)
and ramdisks (<xref linkend="ramdisk"/>). It can even do a
low-level copy of an entire hard drive partition, although
this is not necessarily recommended.</para>
<para>People (with presumably nothing better to do with
their time) are constantly thinking of interesting
applications of <command>dd</command>.</para>
<para><anchor id="ddfdel"/></para>
<example id="blotout">
<title>Securely deleting a file</title>
<programlisting>&blotout;</programlisting>
</example>
<para>See also the <link linkend="ddlink">dd
thread</link> entry in the <link
linkend="biblioref">bibliography</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="odref"/><command>od</command></term>
<listitem>
<indexterm>
<primary>od</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>od</secondary>
</indexterm>
<para>The <command>od</command>, or <firstterm>octal
dump</firstterm> filter 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
<link linkend="devfileref">device files</link>, such as
<filename>/dev/urandom</filename>, and as a filter for
binary data.</para>
<para>
<programlisting>head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p'
# Sample output: 1324725719, 3918166450, 2989231420, etc.
# From rnd.sh example script, by St&eacute;phane Chazelas</programlisting>
</para>
<para>See also <xref linkend="seedingrandom"/> and <xref
linkend="insertionsort"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hexdumpref"/><command>hexdump</command></term>
<listitem>
<indexterm>
<primary>hexdump</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hexadecimal</secondary>
</indexterm>
<para>Performs a hexadecimal, octal, decimal, or ASCII
dump of a binary file. This command is the rough equivalent
of <command>od</command>, above, but not nearly as
useful. May be used to view the contents of a binary file,
in combination with <link linkend="ddref">dd</link> and <link
linkend="lessref">less</link>.</para>
<para>
<programlisting>dd if=/bin/ls | hexdump -C | less
# The -C option nicely formats the output in tabular form.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="objdumpref"/><command>objdump</command></term>
<listitem>
<indexterm>
<primary>objdump</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>object binary dump</secondary>
</indexterm>
<para>Displays information about an object file or binary
executable in either hexadecimal form or as a disassembled
listing (with the <option>-d</option> option).</para>
<para>
<screen><prompt>bash$ </prompt><userinput>objdump -d /bin/ls</userinput>
<computeroutput>/bin/ls: file format elf32-i386
Disassembly of section .init:
080490bc &lt;.init&gt;:
80490bc: 55 push %ebp
80490bd: 89 e5 mov %esp,%ebp
. . .</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mcookieref"/><command>mcookie</command></term>
<listitem>
<indexterm>
<primary>magic</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>cookie</secondary>
</indexterm>
<para>This command generates a <quote>magic cookie,</quote> a
128-bit (32-character) pseudorandom hexadecimal number,
normally used as an authorization <quote>signature</quote>
by the X server. This also available for use in a script
as a <quote>quick 'n dirty</quote> random number.</para>
<para><programlisting>random000=$(mcookie)</programlisting></para>
<para>Of course, a script could use <link
linkend="md5sumref">md5sum</link> for the same purpose.</para>
<para><programlisting># Generate md5 checksum on the script itself.
random001=`md5sum $0 | awk '{print $1}'`
# Uses 'awk' to strip off the filename.</programlisting></para>
<para>The <command>mcookie</command> command gives yet another way
to generate a <quote>unique</quote> filename.</para>
<example id="tempfilename">
<title>Filename generator</title>
<programlisting>&tempfilename;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="unitsref"/><command>units</command></term>
<listitem>
<indexterm>
<primary>units</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>conversion</secondary>
</indexterm>
<para>This utility converts between different <firstterm>units
of measure</firstterm>. While normally invoked in interactive
mode, <command>units</command> may find use in a
script.</para>
<example id="unitconversion">
<title>Converting meters to miles</title>
<programlisting>&unitconversion;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="m4ref"/><command>m4</command></term>
<listitem>
<indexterm>
<primary>m4</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>macro</secondary>
</indexterm>
<para>A hidden treasure, <command>m4</command> is a
powerful macro
<footnote><para>A <firstterm>macro</firstterm> is a
symbolic constant that expands into a command string
or a set of operations on parameters. Simply put,
it's a shortcut or abbreviation.</para></footnote>
processing filter, virtually a complete language.
Although originally written as a pre-processor
for <firstterm>RatFor</firstterm>, <command>m4</command>
turned out to be useful as a stand-alone utility. In
fact, <command>m4</command> combines some of the
functionality of <link linkend="evalref">eval</link>,
<link linkend="trref">tr</link>, and <link
linkend="awkref">awk</link>, in addition to its extensive
macro expansion facilities.</para>
<para>The April, 2002 issue of <ulink
url="http://www.linuxjournal.com"><citetitle
pubwork="journal">Linux Journal</citetitle></ulink>
has a very nice article on <command>m4</command> and
its uses.</para>
<example id="m4">
<title>Using <firstterm>m4</firstterm></title>
<programlisting>&m4;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xmessageref"/><command>xmessage</command></term>
<listitem>
<indexterm>
<primary>xmessage</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>macro</secondary>
</indexterm>
<para>This X-based variant of
<link linkend="echoref">echo</link> pops up a message/query
window on the desktop.</para>
<para>
<programlisting>xmessage Left click to continue -button okay</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="zenityref"/><command>zenity</command></term>
<listitem>
<indexterm>
<primary>zenity</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>macro</secondary>
</indexterm>
<para>The
<ulink url="http://freshmeat.net/projects/zenity">zenity</ulink>
utility is adept at displaying
<firstterm>GTK+</firstterm> dialog <link
linkend="widgetref">widgets</link> and <link
linkend="zenityref2">very suitable for scripting
purposes</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="doexecref"/><command>doexec</command></term>
<listitem>
<indexterm>
<primary>doexec</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>executable arg list</secondary>
</indexterm>
<para>The <command>doexec</command> command enables passing
an arbitrary list of arguments to a <firstterm>binary
executable</firstterm>. In particular, passing
<parameter>argv[0]</parameter> (which corresponds to <link
linkend="posparamref1">$0</link> in a script) lets the
executable be invoked by various names, and it can then
carry out different sets of actions, according to the name
by which it was called. What this amounts to is roundabout
way of passing options to an executable.</para>
<para>For example, the <filename
class="directory">/usr/local/bin</filename> directory might
contain a binary called <quote>aaa</quote>. Invoking
<command>doexec /usr/local/bin/aaa list</command>
would <emphasis>list</emphasis> all those files
in the current working directory beginning with an
<quote>a</quote>, while invoking (the same executable
with) <command>doexec /usr/local/bin/aaa delete </command>
would <emphasis>delete</emphasis> those files.</para>
<note><para>The various behaviors of the executable
must be defined within the code of the executable itself,
analogous to something like the following in a shell script:
<programlisting>case `basename $0` in
"name1" ) do_something;;
"name2" ) do_something_else;;
"name3" ) do_yet_another_thing;;
* ) bail_out;;
esac</programlisting></para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><command>dialog</command></term>
<listitem>
<indexterm>
<primary>dialog</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dialog</secondary>
</indexterm>
<para>The <link linkend="dialogref">dialog</link> family of tools
provide a method of calling interactive
<quote>dialog</quote> boxes from a script. The more
elaborate variations of <command>dialog</command> --
<command>gdialog</command>, <command>Xdialog</command>,
and <command>kdialog</command> -- actually invoke X-Windows
<link linkend="widgetref">widgets</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="soxref"/><command>sox</command></term>
<listitem>
<indexterm>
<primary>sox</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sound</secondary>
</indexterm>
<para>The <command>sox</command>, or
<quote><command>so</command>und
e<command>x</command>change</quote> command plays and
performs transformations on sound files. In fact,
the <filename>/usr/bin/play</filename> executable
(now deprecated) is nothing but a shell wrapper for
<firstterm>sox</firstterm>.</para>
<para>For example, <command>sox soundfile.wav
soundfile.au</command> changes a WAV sound file into a
(Sun audio format) AU sound file.</para>
<para>Shell scripts are ideally suited for batch-processing
<command>sox</command> operations on
sound files. For examples, see the <ulink
url="http://osl.iu.edu/~tveldhui/radio/"> Linux Radio
Timeshift HOWTO</ulink> and the <ulink
url="http://savannah.nongnu.org/projects/audiodo">MP3do
Project</ulink>.</para>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- End Miscellaneous Commands -->
</chapter> <!-- External Filters, Programs and Commands -->
<chapter id="system">
<title>System and Administrative Commands</title>
<para>The startup and shutdown scripts in
<filename class="directory">/etc/rc.d</filename> illustrate the uses
(and usefulness) of many of these comands. These are usually
invoked by <firstterm>root</firstterm> and used for system
maintenance or emergency filesystem repairs. Use with caution, as
some of these commands may damage your system if misused.</para>
<variablelist id="usersgroups">
<title><anchor id="usersgroups1"/>Users and Groups</title>
<varlistentry>
<term><anchor id="usersref"/><command>users</command></term>
<listitem>
<indexterm>
<primary>users</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>users</secondary>
</indexterm>
<para>Show all logged on users. This is the approximate
equivalent of <command>who -q</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="groupscmdref"/><command>groups</command></term>
<listitem>
<indexterm>
<primary>groups</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>groups</secondary>
</indexterm>
<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><anchor id="chownref"/><command>chown</command></term>
<term><anchor id="chgrpref"/><command>chgrp</command></term>
<listitem>
<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>
<para>The <command>chown</command> command changes the
ownership of a file or files. This command is a useful
method that <firstterm>root</firstterm> 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 <firstterm>root</firstterm>)
to use this operation.
<programlisting>chgrp --recursive dunderheads *.data
# The "dunderheads" group will now own all the "*.data" files
#+ all the way down the $PWD directory tree (that's what "recursive" means).
</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="useraddref"/><command>useradd</command></term>
<term><command>userdel</command></term>
<listitem>
<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>
<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="usermodref"/><command>usermod</command></term>
<listitem>
<indexterm>
<primary>usermod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>usermod</secondary>
</indexterm>
<para>Modify a user account. Changes may be made to the password,
group membership, expiration date, and other attributes of
a given user's account. With this command, a user's password
may be locked, which has the effect of disabling the
account.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="groupmodref"/><command>groupmod</command></term>
<listitem>
<indexterm>
<primary>groupmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>group</secondary>
</indexterm>
<para>Modify a given group. The group name and/or ID number may be
changed using this command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="idref"/><command>id</command></term>
<listitem>
<indexterm>
<primary>id</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>id</secondary>
</indexterm>
<para>The <command>id</command> command lists the real and
effective user IDs and the group IDs of the user
associated with the current process. 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>
<note><para>The <command>id</command> command shows the
<emphasis>effective</emphasis> IDs only when they differ
from the <emphasis>real</emphasis> ones.</para></note>
<para>Also see <xref linkend="amiroot"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lidref"/><command>lid</command></term>
<listitem>
<indexterm>
<primary>lid</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>group</secondary>
</indexterm>
<para>The <firstterm>lid</firstterm> (list ID) command
shows the group(s) that a given user belongs to, or alternately,
the users belonging to a given group. May be invoked only by
root.</para>
<para>
<screen><prompt>root# </prompt><userinput>lid bozo</userinput>
<computeroutput> bozo(gid=500)</computeroutput>
<prompt>root# </prompt><userinput>lid daemon</userinput>
<computeroutput> bin(gid=1)
daemon(gid=2)
adm(gid=4)
lp(gid=7)</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="whoref"/><command>who</command></term>
<listitem>
<indexterm>
<primary>who</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>whoami</secondary>
</indexterm>
<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><anchor id="wref"/><command>w</command></term>
<listitem>
<indexterm>
<primary>w</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>w</secondary>
</indexterm>
<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 <link linkend="grepref">grep</link> 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><anchor id="lognameref"/><command>logname</command></term>
<listitem>
<indexterm>
<primary>logname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logname</secondary>
</indexterm>
<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>
<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>
</para>
<note><para>While <command>logname</command> prints the name
of the logged in user, <command>whoami</command> gives the
name of the user attached to the current process. As we have
just seen, sometimes these are not the same.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="suref"/><command>su</command></term>
<listitem>
<indexterm>
<primary>su</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>su</secondary>
</indexterm>
<para>Runs a program or script as a
<command>s</command>ubstitute <command>u</command>ser.
<command>su rjones</command> starts a shell as user
<emphasis>rjones</emphasis>. A naked <command>su</command>
defaults to <firstterm>root</firstterm>. See <xref
linkend="fifo"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sudoref"/><command>sudo</command></term>
<listitem>
<indexterm>
<primary>sudo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sudo</secondary>
</indexterm>
<para>Runs a command as <firstterm>root</firstterm> (or
another user). This may be used in a script, thus permitting
a <firstterm>regular user</firstterm> to run the script.</para>
<para><programlisting>#!/bin/bash
# Some commands.
sudo cp /root/secretfile /home/bozo/secret
# Some more commands.</programlisting></para>
<para>The file <filename>/etc/sudoers</filename> holds
the names of users permitted to invoke
<command>sudo</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="passwdref"/><command>passwd</command></term>
<listitem>
<indexterm>
<primary>passwd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>password</secondary>
</indexterm>
<para>Sets, changes, or manages a user's password.</para>
<para>The <command>passwd</command> command can be used in
a script, but probably <emphasis>should not</emphasis> be.</para>
<example id="setnewpw">
<title>Setting a new password</title>
<programlisting>&setnewpw;</programlisting>
</example>
<para>The <command>passwd</command> command's <option>-l</option>,
<option>-u</option>, and <option>-d</option> options permit
locking, unlocking, and deleting a user's password. Only
<firstterm>root</firstterm> may use these options.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="acref"/><command>ac</command></term>
<listitem>
<indexterm>
<primary>ac</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>accounting</secondary>
</indexterm>
<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><anchor id="lastref"/><command>last</command></term>
<listitem>
<indexterm>
<primary>last</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logged in</secondary>
</indexterm>
<para>List <emphasis>last</emphasis> logged in users, as read from
<filename>/var/log/wtmp</filename>. This command can also
show remote logins.</para>
<para>For example, to show the last few times the system
rebooted:</para>
<screen><prompt>bash$ </prompt><userinput>last reboot</userinput>
<computeroutput>reboot system boot 2.6.9-1.667 Fri Feb 4 18:18 (00:02)
reboot system boot 2.6.9-1.667 Fri Feb 4 15:20 (01:27)
reboot system boot 2.6.9-1.667 Fri Feb 4 12:56 (00:49)
reboot system boot 2.6.9-1.667 Thu Feb 3 21:08 (02:17)
. . .
wtmp begins Tue Feb 1 12:50:09 2005</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="newgrpref"/><command>newgrp</command></term>
<listitem>
<indexterm>
<primary>newgrp</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>group</secondary>
</indexterm>
<para>Change user's <firstterm>group ID</firstterm> without
logging out. This permits access to the new group's
files. Since users may be members of multiple groups
simultaneously, this command finds only limited use.</para>
<note><para>Kurt Glaesemann points out that the
<firstterm>newgrp</firstterm> command could prove helpful
in setting the default group permissions for files a user
writes. However, the <link linkend="chgrpref">chgrp</link>
command might be more convenient for this purpose.</para></note>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="terminalssys">
<title><anchor id="terminalssys1"/>Terminals</title>
<varlistentry>
<term><anchor id="ttyref"/><command>tty</command></term>
<listitem>
<indexterm>
<primary>tty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tty</secondary>
</indexterm>
<para>Echoes the name (filename) of the current user's terminal.
Note that each separate <firstterm>xterm</firstterm>
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>
<listitem>
<indexterm>
<primary>stty</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>stty</secondary>
</indexterm>
<para>Shows and/or changes terminal settings. This complex
command, used in a script, can control terminal behavior
and the way output displays. See the info page, and study
it carefully.</para>
<example id="erase">
<title>Setting an <firstterm>erase</firstterm> character</title>
<programlisting>&erase;</programlisting>
</example>
<example id="secretpw">
<title><firstterm>secret password</firstterm>:
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"/> and <xref
linkend="stopwatch"/>.</para>
<para><anchor id="terminalsref"/></para>
<sidebar><title>terminals and modes</title>
<para>Normally, a terminal works in the
<firstterm>canonical</firstterm> 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>wc -c &lt; filexxx</userinput>
<computeroutput>12</computeroutput>
</screen>
The process controlling the terminal receives only 12
characters (11 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>St&eacute;phane Chazelas</emphasis></para>
</sidebar>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="settermref"/><command>setterm</command></term>
<listitem>
<indexterm>
<primary>setterm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>terminal</secondary>
</indexterm>
<para>Set certain terminal attributes. This command writes
to its terminal's <filename>stdout</filename> a string that
changes the behavior of that terminal.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>setterm -cursor off</userinput>
<computeroutput>bash$</computeroutput>
</screen>
</para>
<para>The <command>setterm</command> command can be used within a
script to change the appearance of text written to
<filename>stdout</filename>, although there are certainly
<link linkend="colorizingref">better tools</link> available
for this purpose.</para>
<para><programlisting>setterm -bold on
echo bold hello
setterm -bold off
echo normal hello</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tsetref"/><command>tset</command></term>
<listitem>
<indexterm>
<primary>tset</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tset</secondary>
</indexterm>
<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><anchor id="setserialref"/><command>setserial</command></term>
<listitem>
<indexterm>
<primary>setserial</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>serial</secondary>
</indexterm>
<para>Set or display serial port parameters. This command must be
run by <firstterm>root</firstterm> and is usually found in a
system setup script.</para>
<para><programlisting># From /etc/pcmcia/serial script:
IRQ=`setserial /dev/$DEVICE | sed -e 's/.*IRQ: //'`
setserial /dev/$DEVICE irq 0 ; setserial /dev/$DEVICE irq $IRQ</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gettyref"/><command>getty</command></term>
<term><anchor id="agettyref"/><command>agetty</command></term>
<listitem>
<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>
<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>
<listitem>
<indexterm>
<primary>mesg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mesg</secondary>
</indexterm>
<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 quite 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><anchor id="wallref"/><command>wall</command></term>
<listitem>
<indexterm>
<primary>wall</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wall</secondary>
</indexterm>
<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
that terminal.</para></note>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="statisticssys">
<title><anchor id="statisticssys1"/>Information and Statistics</title>
<varlistentry>
<term><anchor id="unameref"/><command>uname</command></term>
<listitem>
<indexterm>
<primary>uname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uname</secondary>
</indexterm>
<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"/>). The <option>-s</option>
option shows only the OS type.</para>
<screen><prompt>bash$ </prompt><userinput>uname</userinput>
<computeroutput>Linux</computeroutput>
<prompt>bash$ </prompt><userinput>uname -s</userinput>
<computeroutput>Linux</computeroutput>
<prompt>bash$ </prompt><userinput>uname -a</userinput>
<computeroutput>Linux iron.bozo 2.6.15-1.2054_FC5 #1 Tue Mar 14 15:48:33 EST 2006
i686 i686 i386 GNU/Linux</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="archref"/><command>arch</command></term>
<listitem>
<indexterm>
<primary>arch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>arch</secondary>
</indexterm>
<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="lastcommref"/><command>lastcomm</command></term>
<listitem>
<indexterm>
<primary>lastcomm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>last</secondary>
</indexterm>
<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><anchor id="lastlogref"/><command>lastlog</command></term>
<listitem>
<indexterm>
<primary>lastlog</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>last</secondary>
</indexterm>
<para>List the last login time of all system users. This
references the <filename>/var/log/lastlog</filename>
file.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lastlog</userinput>
<computeroutput>root tty1 Fri Dec 7 18:43:21 -0700 2001
bin **Never logged in**
daemon **Never logged in**
...
bozo tty1 Sat Dec 8 21:14:29 -0700 2001</computeroutput>
<prompt>bash$ </prompt><userinput>lastlog | grep root</userinput>
<computeroutput>root tty1 Fri Dec 7 18:43:21 -0700 2001</computeroutput>
</screen>
</para>
<caution><para>This command will fail if the user invoking
it does not have read permission for the
<filename>/var/log/lastlog</filename> file.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lsofref"/><command>lsof</command></term>
<listitem>
<indexterm>
<primary>lsof</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lsof</secondary>
</indexterm>
<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>
<para>The <command>lsof</command> command is a useful,
if complex administrative tool. If you are unable to
dismount a filesystem and get an error message that it is
still in use, then running <firstterm>lsof</firstterm> helps
determine which files are still open on that filesystem. The
<option>-i</option> option lists open network socket files,
and this can help trace intrusion or hack attempts.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lsof -an -i tcp</userinput>
<computeroutput>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
firefox 2330 bozo 32u IPv4 9956 TCP 66.0.118.137:57596->67.112.7.104:http ...
firefox 2330 bozo 38u IPv4 10535 TCP 66.0.118.137:57708->216.79.48.24:http ...</computeroutput>
</screen>
</para>
<para>See <xref linkend="ipaddresses"/> for an effective use
of <command>lsof</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="straceref"/><command>strace</command></term>
<listitem>
<indexterm>
<primary>strace</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>trace</secondary>
</indexterm>
<para><command>S</command>ystem <command>trace</command>:
diagnostic and debugging tool for tracing <firstterm>system
calls</firstterm> and signals. This command and
<command>ltrace</command>, following, are useful for
diagnosing why a given program or package fails to
run . . . perhaps due to missing libraries or related
causes.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>strace df</userinput>
<computeroutput>execve("/bin/df", ["df"], [/* 45 vars */]) = 0
uname({sys="Linux", node="bozo.localdomain", ...}) = 0
brk(0) = 0x804f5e4
...</computeroutput>
</screen>
</para>
<para>This is the Linux equivalent of
the Solaris <command>truss</command> command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ltraceref"/><command>ltrace</command></term>
<listitem>
<indexterm>
<primary>ltrace</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>trace</secondary>
</indexterm>
<para><command>L</command>ibrary <command>trace</command>:
diagnostic and debugging tool that traces <firstterm>library calls</firstterm>
invoked by a given command.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ltrace df</userinput>
<computeroutput>__libc_start_main(0x804a910, 1, 0xbfb589a4, 0x804fb70, 0x804fb68 &lt;unfinished ...&gt;
setlocale(6, "") = "en_US.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale") = "/usr/share/locale"
textdomain("coreutils") = "coreutils"
__cxa_atexit(0x804b650, 0, 0, 0x8052bf0, 0xbfb58908) = 0
getenv("DF_BLOCK_SIZE") = NULL
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ncref"/><command>nc</command></term>
<listitem>
<indexterm>
<primary>nc</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>nc</secondary>
</indexterm>
<para>The <command>nc</command> (<firstterm>netcat</firstterm>)
utility is a complete toolkit for connecting to and
listening to TCP and UDP ports. It is useful as a diagnostic
and testing tool and as a component in simple script-based HTTP
clients and servers.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>nc localhost.localdomain 25</userinput>
<computeroutput>220 localhost.localdomain ESMTP Sendmail 8.13.1/8.13.1;
Thu, 31 Mar 2005 15:41:35 -0700</computeroutput></screen>
</para>
<para>A real-life <link linkend="netcatexample">usage
example</link>.</para>
<example id="iscan">
<title>Checking a remote server for
<firstterm>identd</firstterm></title>
<programlisting>&iscan;</programlisting>
</example>
<para>
And, of course, there's Dr. Andrew Tridgell's notorious
one-line script in the BitKeeper Affair:
<programlisting>echo clone | nc thunk.org 5000 > e2fsprogs.dat</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="freeref"/><command>free</command></term>
<listitem>
<indexterm>
<primary>free</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>free</secondary>
</indexterm>
<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>
<listitem>
<indexterm>
<primary>procinfo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>procinfo</secondary>
</indexterm>
<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><anchor id="lsdevref"/><command>lsdev</command></term>
<listitem>
<indexterm>
<primary>lsdev</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>device</secondary>
</indexterm>
<para>List devices, that is, show installed hardware.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lsdev</userinput>
<computeroutput>Device DMA IRQ I/O Ports
------------------------------------------------
cascade 4 2
dma 0080-008f
dma1 0000-001f
dma2 00c0-00df
fpu 00f0-00ff
ide0 14 01f0-01f7 03f6-03f6
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="duref"/><command>du</command></term>
<listitem>
<indexterm>
<primary>du</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>du</secondary>
</indexterm>
<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><anchor id="dfref"/><command>df</command></term>
<listitem>
<indexterm>
<primary>df</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>df</secondary>
</indexterm>
<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><anchor id="dmesgref"/><command>dmesg</command></term>
<listitem>
<indexterm>
<primary>dmesg</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dmesg</secondary>
</indexterm>
<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>
<para>
<screen><prompt>bash$ </prompt><userinput>dmesg | grep hda</userinput>
<computeroutput>Kernel command line: ro root=/dev/hda2
hda: IBM-DLGA-23080, ATA DISK drive
hda: 6015744 sectors (3080 MB) w/96KiB Cache, CHS=746/128/63
hda: hda1 hda2 hda3 &lt; hda5 hda6 hda7 > hda4</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="statref"/><command>stat</command></term>
<listitem>
<indexterm>
<primary>stat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>stat</secondary>
</indexterm>
<para>Gives detailed and verbose <emphasis>stat</emphasis>istics
on a given file (even a directory or device file) or set
of files.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>stat test.cru</userinput>
<computeroutput> File: "test.cru"
Size: 49970 Allocated Blocks: 100 Filetype: Regular File
Mode: (0664/-rw-rw-r--) Uid: ( 501/ bozo) Gid: ( 501/ bozo)
Device: 3,8 Inode: 18185 Links: 1
Access: Sat Jun 2 16:40:24 2001
Modify: Sat Jun 2 16:40:24 2001
Change: Sat Jun 2 16:40:24 2001</computeroutput>
</screen>
</para>
<para>If the target file does not exist, <command>stat</command>
returns an error message.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>stat nonexistent-file</userinput>
<computeroutput>nonexistent-file: No such file or directory</computeroutput>
</screen>
</para>
<para>In a script, you can use <command>stat</command> to extract
information about files (and filesystems) and set variables
accordingly.</para>
<para>
<programlisting>#!/bin/bash
# fileinfo2.sh
# Per suggestion of Jo&euml;l Bourquard and . . .
# http://www.linuxquestions.org/questions/showthread.php?t=410766
FILENAME=testfile.txt
file_name=$(stat -c%n "$FILENAME") # Same as "$FILENAME" of course.
file_owner=$(stat -c%U "$FILENAME")
file_size=$(stat -c%s "$FILENAME")
# Certainly easier than using "ls -l $FILENAME"
#+ and then parsing with sed.
file_inode=$(stat -c%i "$FILENAME")
file_type=$(stat -c%F "$FILENAME")
file_access_rights=$(stat -c%A "$FILENAME")
echo "File name: $file_name"
echo "File owner: $file_owner"
echo "File size: $file_size"
echo "File inode: $file_inode"
echo "File type: $file_type"
echo "File access rights: $file_access_rights"
exit 0
sh fileinfo2.sh
File name: testfile.txt
File owner: bozo
File size: 418
File inode: 1730378
File type: regular file
File access rights: -rw-rw-r--</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="vmstatref"/><command>vmstat</command></term>
<listitem>
<indexterm>
<primary>vmstat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>virtual memory</secondary>
</indexterm>
<para>Display virtual memory statistics.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>vmstat</userinput>
<computeroutput> procs memory swap io system cpu
r b w swpd free buff cache si so bi bo in cs us sy id
0 0 0 0 11040 2636 38952 0 0 33 7 271 88 8 3 89</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="uptimeref"/><command>uptime</command></term>
<listitem>
<indexterm>
<primary>uptime</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>uptime</secondary>
</indexterm>
<para>Shows how long the system has been running, along with
associated statistics.</para>
<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>
</para>
<note><para>A <firstterm>load average</firstterm> of 1 or less
indicates that the system handles processes immediately. A load
average greater than 1 means that processes are being queued. When
the load average gets above 3 (on a single-core processor),
then system performance is significantly degraded.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hnameref"/><command>hostname</command></term>
<listitem>
<indexterm>
<primary>hostname</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hostname</secondary>
</indexterm>
<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>
<para>Similar to the <command>hostname</command> command are the
<command>domainname</command>,
<command>dnsdomainname</command>,
<command>nisdomainname</command>, and
<command>ypdomainname</command> commands. Use these to
display or set the system DNS or NIS/YP domain name. Various
options to <command>hostname</command> also perform these
functions.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hostidref"/><command>hostid</command></term>
<listitem>
<indexterm>
<primary>hostid</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>host id</secondary>
</indexterm>
<para>Echo a 32-bit hexadecimal numerical identifier for the
host machine.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>hostid</userinput>
<computeroutput>7f0100</computeroutput></screen>
</para>
<note>
<para>This command allegedly fetches a <quote>unique</quote>
serial number for a particular system. Certain
product registration procedures use this number
to brand a particular user license. Unfortunately,
<command>hostid</command> only returns the machine
network address in hexadecimal, with pairs of bytes
transposed.</para>
<para>The network address of a typical non-networked Linux
machine, is found in <filename>/etc/hosts</filename>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>cat /etc/hosts</userinput>
<computeroutput>127.0.0.1 localhost.localdomain localhost</computeroutput>
</screen>
</para>
<para>As it happens, transposing the bytes of
<userinput>127.0.0.1</userinput>, we get
<userinput>0.127.1.0</userinput>, which translates in
hex to <userinput>007f0100</userinput>, the exact equivalent
of what <command>hostid</command> returns, above. There
exist only a few million other Linux machines with this
identical <firstterm>hostid</firstterm>.</para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sarref"/><command>sar</command></term>
<listitem>
<indexterm>
<primary>sar</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>system activity report</secondary>
</indexterm>
<para>Invoking <command>sar</command> (System Activity Reporter)
gives a very detailed rundown on system statistics. The
Santa Cruz Operation (<quote>Old</quote> SCO) released
<command>sar</command> as Open Source in June, 1999.</para>
<para>This command is not part of the base Linux distribution,
but may be obtained as part of the<ulink
url="http://perso.wanadoo.fr/sebastien.godard/">
sysstat utilities</ulink> package, written by <ulink
url="mailto:sebastien.godard@wanadoo.fr">Sebastien
Godard</ulink>.</para>
<screen>
<prompt>bash$ </prompt><userinput>sar</userinput>
<computeroutput>Linux 2.4.9 (brooks.seringas.fr) 09/26/03
10:30:00 CPU %user %nice %system %iowait %idle
10:40:00 all 2.21 10.90 65.48 0.00 21.41
10:50:00 all 3.36 0.00 72.36 0.00 24.28
11:00:00 all 1.12 0.00 80.77 0.00 18.11
Average: all 2.23 3.63 72.87 0.00 21.27
14:32:30 LINUX RESTART
15:00:00 CPU %user %nice %system %iowait %idle
15:10:00 all 8.59 2.40 17.47 0.00 71.54
15:20:00 all 4.07 1.00 11.95 0.00 82.98
15:30:00 all 0.79 2.94 7.56 0.00 88.71
Average: all 6.33 1.70 14.71 0.00 77.26</computeroutput>
</screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="readelfref"/><command>readelf</command></term>
<listitem>
<indexterm>
<primary>elf</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>statistics</secondary>
</indexterm>
<para>Show information and statistics about a designated
<firstterm>elf</firstterm> binary. This is part of the
<firstterm>binutils</firstterm> package.</para>
<screen><prompt>bash$ </prompt><userinput>readelf -h /bin/bash</userinput>
<computeroutput>ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
. . .</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="sizeref"/><command>size</command></term>
<listitem>
<indexterm>
<primary>size</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>segment</secondary>
</indexterm>
<para>The <command>size [/path/to/binary]</command> command
gives the segment sizes of a binary executable or archive file.
This is mainly of use to programmers.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>size /bin/bash</userinput>
<computeroutput> text data bss dec hex filename
495971 22496 17392 535859 82d33 /bin/bash</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="syslog">
<title><anchor id="syslog1"/>System Logs</title>
<varlistentry>
<term><anchor id="loggerref"/><command>logger</command></term>
<listitem>
<indexterm>
<primary>logger</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logger</secondary>
</indexterm>
<para>Appends a user-generated message to the system log
(<filename>/var/log/messages</filename>). You do not have
to be <firstterm>root</firstterm> to invoke
<command>logger</command>.</para>
<para>
<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>.</para>
<para><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><anchor id="logrotateref"/><command>logrotate</command></term>
<listitem>
<indexterm>
<primary>logrotate</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>logrotate</secondary>
</indexterm>
<para>This utility manages the system log files, rotating,
compressing, deleting, and/or e-mailing them, as appropriate.
This keeps the <filename class="directory">/var/log</filename>
from getting cluttered with old log files.
Usually <link linkend="cronref">cron</link> 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>
<note><para>Stefano Falsetto has created <ulink
url="http://www.gnu.org/software/rottlog/">rottlog</ulink>,
which he considers to be an improved version of
<command>logrotate</command>.</para></note>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="jobcontrolsys">
<title><anchor id="jobcontrolsys1"/>Job Control</title>
<varlistentry>
<term><anchor id="ppssref"/><command>ps</command></term>
<listitem>
<indexterm>
<primary>ps</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ps</secondary>
</indexterm>
<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> or
<option>aux</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>
<para>To display system processes in graphical <quote>tree</quote>
format: <command>ps afjx</command> or
<command>ps ax --forest</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pgrepref"/><command>pgrep</command></term>
<term><anchor id="pkillref"/><command>pkill</command></term>
<listitem>
<indexterm>
<primary>pgrep</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>process grep</secondary>
</indexterm>
<indexterm>
<primary>pkill</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>process kill</secondary>
</indexterm>
<para>Combining the <command>ps</command> command
with <link linkend="grepref">grep</link> or
<link linkend="killref">kill</link>.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ps a | grep mingetty</userinput>
<computeroutput>2212 tty2 Ss+ 0:00 /sbin/mingetty tty2
2213 tty3 Ss+ 0:00 /sbin/mingetty tty3
2214 tty4 Ss+ 0:00 /sbin/mingetty tty4
2215 tty5 Ss+ 0:00 /sbin/mingetty tty5
2216 tty6 Ss+ 0:00 /sbin/mingetty tty6
4849 pts/2 S+ 0:00 grep mingetty</computeroutput>
<prompt>bash$ </prompt><userinput>pgrep mingetty</userinput>
<computeroutput>2212 mingetty
2213 mingetty
2214 mingetty
2215 mingetty
2216 mingetty</computeroutput>
</screen>
</para>
<para>Compare the action of <command>pkill</command> with <link
linkend="killallref">killall</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pstreeref"/><command>pstree</command></term>
<listitem>
<indexterm>
<primary>pstree</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pstree</secondary>
</indexterm>
<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="topref"/><command>top</command></term>
<listitem>
<indexterm>
<primary>top</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>processes</secondary>
</indexterm>
<para>Continuously updated display of most cpu-intensive
processes. The <option>-b</option> option displays in text
mode, so that the output may be parsed or accessed from
a script.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>top -b</userinput>
<computeroutput> 8:30pm up 3 min, 3 users, load average: 0.49, 0.32, 0.13
45 processes: 44 sleeping, 1 running, 0 zombie, 0 stopped
CPU states: 13.6% user, 7.3% system, 0.0% nice, 78.9% idle
Mem: 78396K av, 65468K used, 12928K free, 0K shrd, 2352K buff
Swap: 157208K av, 0K used, 157208K free 37244K cached
PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND
848 bozo 17 0 996 996 800 R 5.6 1.2 0:00 top
1 root 8 0 512 512 444 S 0.0 0.6 0:04 init
2 root 9 0 0 0 0 SW 0.0 0.0 0:00 keventd
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="niceref"/><command>nice</command></term>
<listitem>
<indexterm>
<primary>nice</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>nice</secondary>
</indexterm>
<para><anchor id="nice2ref"/></para>
<para>Run a background job with an altered
priority. Priorities run from 19 (lowest) to -20
(highest). Only <firstterm>root</firstterm> may set the
negative (higher) priorities. Related commands are
<command>renice</command> and <command>snice</command>,
which change the priority of a running process or
processes, and <command>skill</command>, which sends a
<link linkend="killref">kill</link> signal to a process
or processes.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="nohupref"/><command>nohup</command></term>
<listitem>
<indexterm>
<primary>nohup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>nohup</secondary>
</indexterm>
<para>Keeps a command running even after user logs off.
The command will run as a foreground process unless followed
by <token>&amp;</token>. If you use <command>nohup</command>
within a script, consider coupling it with a <link
linkend="waitref">wait</link> to avoid creating an
<firstterm>orphan</firstterm> or
<link linkend="zombieref">zombie</link> process.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="pidofref"/><command>pidof</command></term>
<listitem>
<indexterm>
<primary>pidof</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>process ID</secondary>
</indexterm>
<para>Identifies <firstterm>process ID (PID)</firstterm> of a
running job. Since job control commands, such as <link
linkend="killref">kill</link> and <link
linkend="nice2ref">renice</link> act on the
<firstterm>PID</firstterm> of a process (not its
name), it is sometimes necessary to identify that
<firstterm>PID</firstterm>. 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><firstterm>pidof</firstterm> helps kill a process</title>
<programlisting>&killprocess;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fuserref"/><command>fuser</command></term>
<listitem>
<indexterm>
<primary>fuser</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fuser</secondary>
</indexterm>
<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>
<para>
<screen><prompt>bash$ </prompt><userinput>fuser -u /usr/bin/vim</userinput>
<computeroutput>/usr/bin/vim: 3207e(bozo)</computeroutput>
<prompt>bash$ </prompt><userinput>fuser -u /dev/null</userinput>
<computeroutput>/dev/null: 3009(bozo) 3010(bozo) 3197(bozo) 3199(bozo)</computeroutput>
</screen>
</para>
<para>One important application for <command>fuser</command> is
when physically inserting or removing storage media, such
as CD ROM disks or USB flash drives. Sometimes trying
a <link linkend="umountref">umount</link> fails with a
<errorname>device is busy</errorname> error message. This
means that some user(s) and/or process(es) are accessing
the device. An <command>fuser -um /dev/device_name</command>
will clear up the mystery, so you can kill any relevant
processes.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>umount /mnt/usbdrive</userinput>
<computeroutput>umount: /mnt/usbdrive: device is busy</computeroutput>
<prompt>bash$ </prompt><userinput>fuser -um /dev/usbdrive</userinput>
<computeroutput>/mnt/usbdrive: 1772c(bozo)</computeroutput>
<prompt>bash$ </prompt><userinput>kill -9 1772</userinput>
<prompt>bash$ </prompt><userinput>umount /mnt/usbdrive</userinput>
</screen>
</para>
<para> The <command>fuser</command> command, invoked with the
<option>-n</option> option identifies the processes
accessing a <firstterm>port</firstterm>. This
is especially useful in combination with <link
linkend="nmapref">nmap</link>.</para>
<para>
<screen><prompt>root# </prompt><userinput>nmap localhost.localdomain</userinput>
<computeroutput>PORT STATE SERVICE
25/tcp open smtp</computeroutput>
<prompt>root# </prompt><userinput>fuser -un tcp 25</userinput>
<computeroutput>25/tcp: 2095(root)</computeroutput>
<prompt>root# </prompt><userinput>ps ax | grep 2095 | grep -v grep</userinput>
<computeroutput>2095 ? Ss 0:00 sendmail: accepting connections</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cronref"/><command>cron</command></term>
<listitem>
<indexterm>
<primary>cron</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>crond</secondary>
</indexterm>
<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 <firstterm>superuser</firstterm> version of <link
linkend="atref">at</link> (although each user may have
their own <filename>crontab</filename> file which can be
changed with the <command>crontab</command> command).
It runs as a <link linkend="daemonref">daemon</link>
and executes scheduled entries from
<filename>/etc/crontab</filename>.</para>
<note><para>Some flavors of Linux run
<command>crond</command>, Matthew Dillon's version of
<command>cron</command>.</para></note>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="runcontrolsys">
<title><anchor id="runcontrolsys1"/>Process Control and Booting</title>
<varlistentry>
<term><anchor id="initref"/><command>init</command></term>
<listitem>
<indexterm>
<primary>init</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>init</secondary>
</indexterm>
<para><anchor id="inittabref"/></para>
<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
<firstterm>root</firstterm> only.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="telinitref"/><command>telinit</command></term>
<listitem>
<indexterm>
<primary>telinit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>telinit</secondary>
</indexterm>
<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 <firstterm>root</firstterm>. This
command can be dangerous -- be certain you understand it
well before using!</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="runlevelref"/><command>runlevel</command></term>
<listitem>
<indexterm>
<primary>runlevel</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>runlevel</secondary>
</indexterm>
<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><anchor id="haltref"/><command>halt</command></term>
<term><anchor id="shutdownref"/><command>shutdown</command></term>
<term><anchor id="rebootref"/><command>reboot</command></term>
<listitem>
<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>
<para>Command set to shut the system down, usually just prior to a power down.</para>
<warning><para>On some Linux distros, the <command>halt</command> command
has 755 permissions, so it can be invoked by a non-root user.
A careless <firstterm>halt</firstterm> in a terminal or a script
may shut down the system!</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="serviceref"/><command>service</command></term>
<listitem>
<indexterm>
<primary>service</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>service</secondary>
</indexterm>
<para>Starts or stops a system <firstterm>service</firstterm>.
The startup scripts in <filename class="directory">/etc/init.d</filename>
and <filename class="directory">/etc/rc.d</filename> use this
command to start services at bootup.</para>
<para><anchor id="iptables01"/></para>
<para>
<screen><prompt>root# </prompt><userinput>/sbin/service iptables stop</userinput>
<computeroutput>Flushing firewall rules: [ OK ]
Setting chains to policy ACCEPT: filter [ OK ]
Unloading iptables modules: [ OK ]</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="networksys">
<title><anchor id="networksys1"/>Network</title>
<varlistentry>
<term><anchor id="nmapref"/><command>nmap</command></term>
<listitem>
<indexterm>
<primary>nmap</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>port scan</secondary>
</indexterm>
<para><command>N</command>etwork <command>map</command>per
and port scanner. This command scans a server to
locate open ports and the services associated with those
ports. It can also report information about packet filters and
firewalls. This is an important security tool for locking down
a network against hacking attempts.</para>
<para><programlisting>#!/bin/bash
SERVER=$HOST # localhost.localdomain (127.0.0.1).
PORT_NUMBER=25 # SMTP port.
nmap $SERVER | grep -w "$PORT_NUMBER" # Is that particular port open?
# grep -w matches whole words only,
#+ so this wouldn't match port 1025, for example.
exit 0
# 25/tcp open smtp</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ifconfigref"/><command>ifconfig</command></term>
<listitem>
<indexterm>
<primary>ifconfig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ifconfig</secondary>
</indexterm>
<para>Network <firstterm>interface configuration</firstterm>
and tuning utility.</para>
<screen><prompt>bash$ </prompt><userinput>ifconfig -a</userinput>
<computeroutput>lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:10 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:700 (700.0 b) TX bytes:700 (700.0 b)</computeroutput></screen>
<para>The <command>ifconfig</command> command is most often used
at bootup to set up the interfaces, or to shut them down
when rebooting.</para>
<para><programlisting># Code snippets from /etc/rc.d/init.d/network
# ...
# Check that networking is up.
[ ${NETWORKING} = "no" ] &amp;&amp; exit 0
[ -x /sbin/ifconfig ] || exit 0
# ...
for i in $interfaces ; do
if ifconfig $i 2>/dev/null | grep -q "UP" >/dev/null 2>&amp;1 ; then
action "Shutting down interface $i: " ./ifdown $i boot
fi
# The GNU-specific "-q" option 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></para>
<para>See also <xref linkend="online"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="netstatref"/><command>netstat</command></term>
<listitem>
<indexterm>
<primary>netstat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>netstat</secondary>
</indexterm>
<para>Show current network statistics and information,
such as routing tables and active connections. This utility
accesses information in <filename>/proc/net</filename>
(<xref linkend="devproc"/>). See <xref
linkend="constat"/>.</para>
<para><command>netstat -r</command> is equivalent to <link
linkend="routeref">route</link>.</para>
<screen><prompt>bash$ </prompt><userinput>netstat</userinput>
<computeroutput>Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
unix 11 [ ] DGRAM 906 /dev/log
unix 3 [ ] STREAM CONNECTED 4514 /tmp/.X11-unix/X0
unix 3 [ ] STREAM CONNECTED 4513
. . .</computeroutput></screen>
<note><para>A <command>netstat -lptu</command> shows <link
linkend="socketref">sockets</link> that are listening
to ports, and the associated processes. This can be useful
for determining whether a computer has been hacked or
compromised.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="iwconfigref"/><command>iwconfig</command></term>
<listitem>
<indexterm>
<primary>iwconfig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>wireless</secondary>
</indexterm>
<para>This is the command set for configuring a wireless network.
It is the wireless equivalent of <command>ifconfig</command>,
above.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="ipref"/><command>ip</command></term>
<listitem>
<indexterm>
<primary>ip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>routing</secondary>
</indexterm>
<para>General purpose utility for setting up, changing, and
analyzing <firstterm>IP</firstterm> (Internet Protocol)
networks and attached devices. This command is part of
the <firstterm>iproute2</firstterm> package.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ip link show</userinput>
<computeroutput>1: lo: &lt;LOOPBACK,UP&gt; mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:d0:59:ce:af:da brd ff:ff:ff:ff:ff:ff
3: sit0: &lt;NOARP&gt; mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0</computeroutput>
<prompt>bash$ </prompt><userinput>ip route list</userinput>
<computeroutput>169.254.0.0/16 dev lo scope link</computeroutput>
</screen>
</para>
<para>Or, in a script:</para>
<para><anchor id="ipscript0"/>
<programlisting>&ipscript;</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="routeref"/><command>route</command></term>
<listitem>
<indexterm>
<primary>route</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>route</secondary>
</indexterm>
<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 MSS Window irtt Iface
pm3-67.bozosisp * 255.255.255.255 UH 40 0 0 ppp0
127.0.0.0 * 255.0.0.0 U 40 0 0 lo
default pm3-67.bozosisp 0.0.0.0 UG 40 0 0 ppp0</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor
id="iptablesref"/><command>iptables</command></term>
<listitem>
<indexterm>
<primary>iptables</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>firewall</secondary>
</indexterm>
<para> The <command>iptables</command> command set is
a packet filtering tool used mainly for such security
purposes as setting up network firewalls. This
is a complex tool, and a detailed explanation of
its use is beyond the scope of this document. <ulink
url="http://www.frozentux.net/iptables-tutorial/iptables-tutorial.html">Oskar
Andreasson's tutorial</ulink> is a reasonable starting
point.</para>
<para>See also <link linkend="iptables01">shutting down
<firstterm>iptables</firstterm></link> and <xref
linkend="ipaddresses"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="chkconfigref"/><command>chkconfig</command></term>
<listitem>
<indexterm>
<primary>chkconfig</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>network configuration</secondary>
</indexterm>
<para>Check network and system configuration. This command
lists and
manages the network and system services started at bootup in
the <filename class="directory">/etc/rc?.d</filename>
directory.</para>
<para>Originally a port from IRIX to Red Hat Linux,
<command>chkconfig</command> may not be part of the core
installation of some Linux flavors.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>chkconfig --list</userinput>
<computeroutput>atd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
rwhod 0:off 1:off 2:off 3:off 4:off 5:off 6:off
...</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tcpdumpref"/><command>tcpdump</command></term>
<listitem>
<indexterm>
<primary>tcpdump</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tcp</secondary>
</indexterm>
<para>Network packet <quote>sniffer.</quote> This is a tool for
analyzing and troubleshooting traffic on a network by dumping
packet headers that match specified criteria.</para>
<para>Dump ip packet traffic between hosts
<emphasis>bozoville</emphasis> and
<emphasis>caduceus</emphasis>:</para>
<para>
<screen><prompt>bash$ </prompt><userinput>tcpdump ip host bozoville and caduceus</userinput>
</screen>
</para>
<para>Of course, the output of <command>tcpdump</command> can be
parsed with certain of the previously discussed <link
linkend="tpcommandlisting1">text processing
utilities</link>.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="filesystemsys">
<title><anchor id="filesystemsys1"/>Filesystem</title>
<varlistentry>
<term><anchor id="mountref"/><command>mount</command></term>
<listitem>
<indexterm>
<primary>mount</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mount</secondary>
</indexterm>
<para>Mount a filesystem, usually on an external device,
such as a floppy or CDROM. <anchor id="fstabref"/>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 CD ROM. ISO 9660 is a standard CD ROM filesystem.
mount /mnt/cdrom
# Shortcut, if /mnt/cdrom listed in /etc/fstab</programlisting>
</para>
<para><anchor id="isomountref0"/></para>
<para>The versatile <firstterm>mount</firstterm> command can even
mount an ordinary file on a block device, and the file will
act as if it were a filesystem. <firstterm>Mount</firstterm>
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 filesystem 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"><citetitle
pubwork="journal">Linux
Journal</citetitle></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><anchor id="umountref"/><command>umount</command></term>
<listitem>
<indexterm>
<primary>umount</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>umount</secondary>
</indexterm>
<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
<quote>multispindle</quote> laptops with swappable
floppy and optical drives, this can cause problems,
however.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="gnomemountref"/><command>gnome-mount</command></term>
<listitem>
<indexterm>
<primary>gnome-mount</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mount</secondary>
</indexterm>
<para>The newer Linux distros have deprecated
<command>mount</command> and <command>umount</command>.
The successor, for command-line mounting of removable storage
devices, is <command>gnome-mount</command>. It can take the
<option>-d</option> option to mount a <link
linkend="devfileref">device file</link> by its listing in
<filename class="directory">/dev</filename>.</para>
<para>For example, to mount a USB flash drive:</para>
<para>
<screen><prompt>bash$ </prompt><userinput>gnome-mount -d /dev/sda1</userinput>
<computeroutput>gnome-mount 0.4</computeroutput>
<prompt>bash$ </prompt><userinput>df</userinput>
<computeroutput>. . .
/dev/sda1 63584 12034 51550 19% /media/disk</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="syncref"/><command>sync</command></term>
<listitem>
<indexterm>
<primary>sync</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>sync</secondary>
</indexterm>
<para>Forces an immediate write of all updated data from
buffers to hard drive (synchronize drive
with buffers). While not strictly necessary, a
<command>sync</command> assures the sys admin or
user that the data just changed will survive a sudden
power failure. In the olden days, a <userinput>sync;
sync</userinput> (twice, just to make absolutely sure) was a
useful precautionary measure before a system reboot.</para>
<para>At times, you may wish to force an immediate buffer
flush, as when securely deleting a file (see <xref
linkend="blotout"/>) or when the lights begin to
flicker.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="losetupref"/><command>losetup</command></term>
<listitem>
<indexterm>
<primary>losetup</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>losetup</secondary>
</indexterm>
<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 &lt; /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><anchor id="mkswapref"/><command>mkswap</command></term>
<listitem>
<indexterm>
<primary>mkswap</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkswap</secondary>
</indexterm>
<para>Creates a swap partition or file. The swap area must
subsequently be enabled with
<command>swapon</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="swaponref"/><command>swapon</command></term>
<term><anchor id="swapoffref"/><command>swapoff</command></term>
<listitem>
<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>
<para>Enable / disable swap partitition or file.
These commands usually take effect at bootup and
shutdown.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mke2fsref"/><command>mke2fs</command></term>
<listitem>
<indexterm>
<primary>mke2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mke2fs</secondary>
</indexterm>
<para>Create a Linux <firstterm>ext2</firstterm>
filesystem. This command must be invoked as
<firstterm>root</firstterm>.</para>
<example id="adddrv">
<title>Adding a new hard drive</title>
<programlisting>&adddrv;</programlisting>
</example>
<para>See also <xref linkend="createfs"/> and <xref
linkend="ramdisk"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mkdosfsref"/><command>mkdosfs</command></term>
<listitem>
<indexterm>
<primary>mkdosfs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mkdosfs</secondary>
</indexterm>
<para>Create a DOS <firstterm>FAT</firstterm>
filesystem.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tune2fsref"/><command>tune2fs</command></term>
<listitem>
<indexterm>
<primary>tune2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tune2fs</secondary>
</indexterm>
<para>Tune <firstterm>ext2</firstterm> filesystem. May be
used to change filesystem parameters, such as maximum
mount count. This must be invoked as
<firstterm>root</firstterm>.</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="dumpe2fsref"/><command>dumpe2fs</command></term>
<listitem>
<indexterm>
<primary>dumpe2fs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>dumpe2fs</secondary>
</indexterm>
<para>Dump (list to <filename>stdout</filename>) very verbose
filesystem info. This must be invoked as
<firstterm>root</firstterm>.</para>
<screen><prompt>root# </prompt><command>dumpe2fs /dev/hda7 | grep 'ount count'</command>
<computeroutput>dumpe2fs 1.19, 13-Jul-2000 for EXT2 FS 0.5b, 95/08/09
Mount count: 6
Maximum mount count: 20</computeroutput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="hdparmref"/><command>hdparm</command></term>
<listitem>
<indexterm>
<primary>hdparm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>hard disk parameters</secondary>
</indexterm>
<para>List or change hard disk parameters. This command must be
invoked as <firstterm>root</firstterm>, and it may be
dangerous if misused.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fdiskref"/><command>fdisk</command></term>
<listitem>
<indexterm>
<primary>fdisk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>fdisk</secondary>
</indexterm>
<para>Create or change a partition table on a storage device,
usually a hard drive. This command must be invoked as
<firstterm>root</firstterm>.</para>
<warning><para>Use this command with extreme caution. If something
goes wrong, you may destroy an existing
filesystem.</para></warning>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="fsckref"/><command>fsck</command></term>
<term><anchor id="e2fsckref"/><command>e2fsck</command></term>
<term><anchor id="debugfsref"/><command>debugfs</command></term>
<listitem>
<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>
<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
<firstterm>ext2</firstterm>.</para>
<para><command>e2fsck</command>: ext2 filesystem checker.</para>
<para><command>debugfs</command>: ext2 filesystem debugger.
One of the uses of this versatile, but dangerous command
is to (attempt to) recover deleted files. For advanced users
only!</para>
<caution><para>All of these should be invoked as
<firstterm>root</firstterm>, and they can damage or destroy
a filesystem if misused.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="badblocksref"/><command>badblocks</command></term>
<listitem>
<indexterm>
<primary>badblocks</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>badblocks</secondary>
</indexterm>
<para>Checks for bad blocks (physical media flaws) on a
storage device. This command finds use when formatting
a newly installed hard drive or testing the integrity
of backup media.
<footnote><para>The <option>-c</option> option to <link
linkend="mke2fsref">mke2fs</link> also invokes a check for bad
blocks.</para></footnote>
As an example, <command>badblocks /dev/fd0</command>
tests a floppy disk.</para>
<para>The <command>badblocks</command> command
may be invoked destructively (overwrite all data) or
in non-destructive read-only mode. If <firstterm>root
user</firstterm> owns the device to be tested, as is
generally the case, then <firstterm>root</firstterm>
must invoke this command.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lsusbref"/><command>lsusb</command></term>
<term><command>usbmodules</command></term>
<listitem>
<indexterm>
<primary>lsusb</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>usb</secondary>
</indexterm>
<indexterm>
<primary>usbmodules</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>usb</secondary>
</indexterm>
<para>The <command>lsusb</command> command lists all USB
(Universal Serial Bus) buses and the devices hooked up to
them.</para>
<para>The <command>usbmodules</command> command outputs
information about the driver modules for connected USB
devices.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lsusb</userinput>
<computeroutput>Bus 001 Device 001: ID 0000:0000
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.00
bDeviceClass 9 Hub
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x0000
idProduct 0x0000
. . .</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lspciref"/><command>lspci</command></term>
<listitem>
<indexterm>
<primary>lspci</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>pci</secondary>
</indexterm>
<para>Lists <firstterm>pci</firstterm> busses present.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>lspci</userinput>
<computeroutput>00:00.0 Host bridge: Intel Corporation 82845 845
(Brookdale) Chipset Host Bridge (rev 04)
00:01.0 PCI bridge: Intel Corporation 82845 845
(Brookdale) Chipset AGP Bridge (rev 04)
00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)
00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)
00:1d.2 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #3) (rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)
. . .</computeroutput>
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mkbootdiskref"/><command>mkbootdisk</command></term>
<listitem>
<indexterm>
<primary>mkbootdisk</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>bootdisk</secondary>
</indexterm>
<para>Creates a boot floppy which can be used to bring up the
system if, for example, the MBR (master boot record) becomes
corrupted. Of special interest is the <option>--iso</option>
option, which uses <command>mkisofs</command> to create a
bootable <firstterm>ISO9660</firstterm> filesystem image
suitable for burning a bootable CDR.</para>
<para>The <command>mkbootdisk</command> command is actually
a Bash script, written by Erik Troan, in the <filename
class="directory">/sbin</filename> directory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mkisofsref"/><command>mkisofs</command></term>
<listitem>
<indexterm>
<primary>mkisofs</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ISO9660</secondary>
</indexterm>
<para>Creates an <firstterm>ISO9660</firstterm> filesystem
suitable for a CDR image.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="chrootref"/><command>chroot</command></term>
<listitem>
<indexterm>
<primary>chroot</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>chroot</secondary>
</indexterm>
<indexterm>
<primary>directory</primary>
<secondary>root</secondary>
<tertiary>change</tertiary>
</indexterm>
<para>CHange ROOT directory. Normally commands are fetched
from <link linkend="pathref">$PATH</link>, relative to
<filename class="directory">/</filename>, the default
<firstterm>root
directory</firstterm>. This changes the
<firstterm>root</firstterm> directory to a different one
(and also changes the working directory to there). This is
useful for security purposes, for instance when the system
administrator wishes to restrict certain users, such as
those <link linkend="telnetref">telnetting</link> in,
to a secured portion of the filesystem (this is sometimes
referred to as confining a guest user to a <quote>chroot
jail</quote>). Note that after a <command>chroot</command>,
the execution path for system binaries is no longer
valid.</para>
<para>A <userinput>chroot /opt</userinput> would cause
references to <filename
class="directory">/usr/bin</filename>
to be translated to <filename
class="directory">/opt/usr/bin</filename>. Likewise,
<userinput>chroot /aaa/bbb /bin/ls</userinput> would
redirect future instances of <command>ls</command>
to <filename>/aaa/bbb</filename> as the base directory,
rather than <filename class="directory">/</filename> as is
normally the case. An <command>alias XX 'chroot /aaa/bbb
ls'</command> in a user's <link
linkend="sample-bashrc"><filename>~/.bashrc</filename></link>
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 <link linkend="rpmref">rpm</link>
option) or running a readonly filesystem from a CD ROM.
Invoke only as <firstterm>root</firstterm>, and use with
care.</para>
<caution><para>It might be necessary to copy certain system
files to a <firstterm>chrooted</firstterm> directory,
since the normal <varname>$PATH</varname> can no longer
be relied upon.</para></caution>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lockfileref"/><command>lockfile</command></term>
<listitem>
<indexterm>
<primary>lockfile</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lockfile</secondary>
</indexterm>
<para>This utility is part of the <command>procmail</command>
package (<ulink url="http://www.procmail.org">www.procmail.org</ulink>).
It creates a <firstterm>lock file</firstterm>, a
<firstterm>semaphore</firstterm> that controls access to
a file, device, or resource.</para>
<sidebar><para><anchor id="semaphoreref"/>
<userinput>Definition:</userinput>
A <firstterm>semaphore</firstterm> is a flag or
signal. (The usage originated in railroading, where a
colored flag, lantern, or striped movable arm
<firstterm>semaphore</firstterm> indicated whether a
particular track was in use and therefore unavailable
for another train.) A UNIX process can check the
appropriate semaphore to determine whether a particular
resource is available/accessible.</para></sidebar>
<para>The lock file serves as a flag that this particular
file, device, or resource is in use by a process (and
is therefore <quote>busy</quote>). The presence of a
lock file permits only restricted access (or no access)
to other processes.</para>
<para><programlisting>lockfile /home/bozo/lockfiles/$0.lock
# Creates a write-protected lockfile prefixed with the name of the script.
lockfile /home/bozo/lockfiles/${0##*/}.lock
# A safer version of the above, as pointed out by E. Choroba.</programlisting></para>
<para>Lock files are used in such applications as protecting
system mail folders from simultaneously being changed
by multiple users, indicating that a modem port
is being accessed, and showing that an instance of
<application>Firefox</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 to create a lock file that
already exists, the script will likely hang.</para>
<para>Normally, applications create and check for lock files
in the <filename class="directory">/var/lock</filename>
directory.
<footnote><para>Since only <firstterm>root</firstterm>
has write permission in the <filename
class="directory">/var/lock</filename> directory,
a user script cannot set a lock file there.</para></footnote>
A script can test for the presence of a lock file by
something like the following.
<programlisting>appname=xyzip
# Application "xyzip" created lock file "/var/lock/xyzip.lock".
if [ -e "/var/lock/$appname.lock" ]
then #+ Prevent other programs &amp; scripts
# from accessing files/resources used by xyzip.
...</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="flockref"/><command>flock</command></term>
<listitem>
<indexterm>
<primary>flock</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>lock file</secondary>
</indexterm>
<para>Much less useful than the <command>lockfile</command>
command is <command>flock</command>. It sets an
<quote>advisory</quote> lock on a file and then executes
a command while the lock is on. This is to prevent
any other process from setting a lock on that file until
completion of the specified command.</para>
<para><programlisting>flock $0 cat $0 > lockfile__$0
# Set a lock on the script the above line appears in,
#+ while listing the script to stdout.</programlisting></para>
<note><para>Unlike <command>lockfile</command>,
<command>flock</command> does <emphasis>not</emphasis>
automatically create a lock file.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="mknodref"/><command>mknod</command></term>
<listitem>
<indexterm>
<primary>mknod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>mknod</secondary>
</indexterm>
<para>Creates block or character <link
linkend="devfileref">device files</link> (may be
necessary when installing new hardware on the system). The
<command>MAKEDEV</command> utility has virtually
all of the functionality of <command>mknod</command>,
and is easier to use.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="MAKEDEVref"/><command>MAKEDEV</command></term>
<listitem>
<indexterm>
<primary>MAKEDEV</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>make device file</secondary>
</indexterm>
<para>Utility for creating device files. It must be run as
<firstterm>root</firstterm>, and in the <filename
class="directory">/dev</filename> directory. It is a sort
of advanced version of <command>mknod</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="tmpwatchref"/><command>tmpwatch</command></term>
<listitem>
<indexterm>
<primary>tmpwatch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>tmpwatch</secondary>
</indexterm>
<para>Automatically deletes files which have not been accessed
within a specified period of time. Usually invoked by
<link linkend="cronref">cron</link> to remove stale log
files.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="periphsys">
<title><anchor id="periphsys1"/>Backup</title>
<varlistentry>
<term><anchor id="dumpref"/><command>dump</command></term>
<term><anchor id="restoreref"/><command>restore</command></term>
<listitem>
<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>
<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><anchor id="fdformatref"/><command>fdformat</command></term>
<listitem>
<indexterm>
<primary>fdformat</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>floppy</secondary>
</indexterm>
<para>Perform a low-level format on a floppy disk
(<filename>/dev/fd0*</filename>).</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="sysresources">
<title><anchor id="sysresources1"/>System Resources</title>
<varlistentry>
<term><anchor id="ulimitref"/><command>ulimit</command></term>
<listitem>
<indexterm>
<primary>ulimit</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ulimit</secondary>
</indexterm>
<para>Sets an <firstterm>upper limit</firstterm> on use
of 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).
<footnote><para>As of the <link linkend="bash4ref">version
4 update</link> of Bash, the <option>-f</option>
and <option>-c</option> options take a block size
of 512 when in <link linkend="posix2ref">POSIX</link>
mode. Additionally, there are two new options:
<option>-b</option> for <link
linkend="socketref">socket</link> buffer size, and
<option>-T</option> for the limit on the number of
<firstterm>threads</firstterm>.</para></footnote>
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>
<important>
<para>Judicious use of <command>ulimit</command> can
protect a system against the dreaded <firstterm>fork
bomb</firstterm>.</para>
<para>
<programlisting>#!/bin/bash
# This script is for illustrative purposes only.
# Run it at your own peril -- it WILL freeze your system.
while true # Endless loop.
do
$0 &amp; # This script invokes itself . . .
#+ forks an infinite number of times . . .
#+ until the system freezes up because all resources exhausted.
done # This is the notorious <quote>sorcerer's appentice</quote> scenario.
exit 0 # Will not exit here, because this script will never terminate.</programlisting>
</para>
<para>A <command>ulimit -Hu XX</command> (where
<emphasis>XX</emphasis> is the user process limit) in
<filename>/etc/profile</filename> would abort
this script when it exceeded the preset limit.
</para>
</important>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="quotaref"/><command>quota</command></term>
<listitem>
<indexterm>
<primary>quota</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>quota</secondary>
</indexterm>
<para>Display user or group disk quotas.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="setquotaref"/><command>setquota</command></term>
<listitem>
<indexterm>
<primary>setquota</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>quota</secondary>
</indexterm>
<para>Set user or group disk quotas from the command-line.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="umaskref"/><command>umask</command></term>
<listitem>
<indexterm>
<primary>umask</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>umask</secondary>
</indexterm>
<para>User file creation permissions
<firstterm>mask</firstterm>. 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 file permissions
<firstterm>disabled</firstterm>. For example, <command>umask
022</command> ensures that new files will have at most
755 permissions (777 NAND 022).
<footnote><para>NAND is the logical
<firstterm>not-and</firstterm> operator. Its effect
is somewhat similar to subtraction.</para></footnote>
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>
<example id="rot13a">
<title>Using <firstterm>umask</firstterm> to hide an output file
from prying eyes</title>
<programlisting>&rot13a;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rdevref"/><command>rdev</command></term>
<listitem>
<indexterm>
<primary>rdev</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rdev</secondary>
</indexterm>
<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 a dangerous command, if misused.
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="modulessys">
<title><anchor id="modulessys1"/>Modules</title>
<varlistentry>
<term><anchor id="lsmodref"/><command>lsmod</command></term>
<listitem>
<indexterm>
<primary>lsmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<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>
<note><para>Doing a <command>cat /proc/modules</command> gives the
same information.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="insmodref"/><command>insmod</command></term>
<listitem>
<indexterm>
<primary>insmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<para>Force installation of a kernel module (use
<command>modprobe</command> instead, when possible). Must
be invoked as <firstterm>root</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rmmodref"/><command>rmmod</command></term>
<listitem>
<indexterm>
<primary>rmmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<para>Force unloading of a kernel module. Must be invoked
as <firstterm>root</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="modproberef"/><command>modprobe</command></term>
<listitem>
<indexterm>
<primary>modprobe</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<para>Module loader that is normally invoked automatically
in a startup script. Must be invoked as
<firstterm>root</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="depmodref"/><command>depmod</command></term>
<listitem>
<indexterm>
<primary>depmod</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<para>Creates module dependency file. Usually invoked from a
startup script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="modinforef"/><command>modinfo</command></term>
<listitem>
<indexterm>
<primary>modinfo</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>loadable modules</secondary>
</indexterm>
<para>Output information about a loadable module.</para>
<screen><prompt>bash$ </prompt><userinput>modinfo hid</userinput>
<computeroutput>filename: /lib/modules/2.4.20-6/kernel/drivers/usb/hid.o
description: "USB HID support drivers"
author: "Andreas Gal, Vojtech Pavlik &lt;vojtech@suse.cz&gt;"
license: "GPL"</computeroutput>
</screen>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="miscsys">
<title><anchor id="miscsys1"/>Miscellaneous</title>
<varlistentry>
<term><anchor id="envvref"/><command>env</command></term>
<listitem>
<indexterm>
<primary>env</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>env</secondary>
</indexterm>
<para>
Runs a program or script with certain <link
linkend="envref">environmental variables</link>
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.
<footnote><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></footnote>
</para>
<note>
<para><anchor id="envv2ref"/>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.</para>
<para><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>
<para>Or even ... </para>
<para><programlisting>#!/bin/env bash
# Queries the $PATH enviromental variable for the location of bash.
# Therefore ...
# This script will run where Bash is not in its usual place, in /bin.
...</programlisting></para>
</note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="lddref"/><command>ldd</command></term>
<listitem>
<indexterm>
<primary>ldd</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>ldd</secondary>
</indexterm>
<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><anchor id="watchref"/><command>watch</command></term>
<listitem>
<indexterm>
<primary>watch</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>periodic</secondary>
</indexterm>
<para>Run a command repeatedly, at specified time intervals.</para>
<para>The default is two-second intervals, but this may be changed
with the <option>-n</option> option.</para>
<para><programlisting>watch -n 5 tail /var/log/messages
# Shows tail end of system log, /var/log/messages, every five seconds.</programlisting></para>
<note><para>Unfortunately, <link linkend="piperef">piping</link>
the output of <command>watch command</command> to <link
linkend="grepref">grep</link> does not work.</para></note>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="stripref"/><command>strip</command></term>
<listitem>
<indexterm>
<primary>strip</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>symbol</secondary>
</indexterm>
<para>Remove the debugging symbolic references from an executable
binary. This decreases its size, but makes debugging it
impossible.</para>
<para>This command often occurs in a <link
linkend="makefileref">Makefile</link>,
but rarely in a shell script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="nmref"/><command>nm</command></term>
<listitem>
<indexterm>
<primary>nm</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>symbol</secondary>
</indexterm>
<para>List symbols in an unstripped compiled binary.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="xrandrref"/><command>xrandr</command></term>
<listitem>
<indexterm>
<primary>xrandr</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>xrandr</secondary>
</indexterm>
<para>Command-line tool for manipulating the root window
of the screen.</para>
<example id="backlight">
<title><firstterm>Backlight</firstterm>: changes
the brightness of the (laptop) screen backlight</title>
<programlisting>&backlight;</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="rdistref"/><command>rdist</command></term>
<listitem>
<indexterm>
<primary>rdist</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>rdist</secondary>
</indexterm>
<para>Remote distribution client: synchronizes, clones,
or backs up a file system on a remote server.</para>
</listitem>
</varlistentry>
</variablelist>
<sect1 id="sysscripts">
<title>Analyzing a System Script</title>
<para><anchor id="killall2ref"/></para>
<para>Using our knowledge of administrative commands, let us examine a system
script. One of the shortest and simplest to understand scripts is
<quote>killall,</quote>
<footnote><para>The <firstterm>killall</firstterm> system
script should not be confused with the <link
linkend="killallref">killall</link> command in <filename
class="directory">/usr/bin</filename>.</para></footnote>
used to suspend running processes at system shutdown.</para>
<example id="ex55">
<title><firstterm>killall</firstterm>, 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 1</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
<firstterm>root</firstterm>). Do a simulated run
with the <option>-vn</option> flags (<userinput>sh
-vn scriptname</userinput>). Add extensive
comments. Change the commands to <link
linkend="echoref">echos</link>.</para></formalpara>
<formalpara><title>Exercise 2</title>
<para>Look at some of the more complex scripts in
<filename class="directory">/etc/rc.d/init.d</filename>.
Try to understand at least portions 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/share/doc/initscripts-?.??</filename>,
which is part of the <quote>initscripts</quote>
documentation.</para></formalpara>
</sect1> <!-- Analyzing a System Script -->
</chapter> <!-- System and Administrative Commands -->
</part> <!-- Part 4 (Beyond the Basics) -->
<part label="Part 5" id="part5">
<title>Advanced Topics</title>
<partintro>
<para>At this point, we are ready to delve into certain of the
difficult and unusual aspects of scripting. Along the way, we
will attempt to <quote>push the envelope</quote> in various
ways and examine <firstterm>boundary conditions</firstterm>
(what happens when we move into uncharted territory?).</para>
</partintro>
<chapter id="regexp">
<title>Regular Expressions</title>
<epigraph>
<para>. . . the intellectual activity associated with software
development is largely one of gaining insight.</para>
<para>--Stowe Boyd</para>
</epigraph>
<para><anchor id="regexref"/></para>
<para>To fully utilize the power of shell scripting, you need to
master Regular Expressions. Certain commands
and utilities commonly used in scripts, such
as <link linkend="grepref">grep</link>, <link
linkend="exprref">expr</link>, <link linkend="sedref">sed</link>
and <link linkend="awkref">awk</link>, interpret and use REs. As of
<link linkend="bash3ref">version 3</link>, Bash has acquired its
own <link linkend="regexmatchref">RE-match operator</link>:
<command>=~</command>.</para>
<sect1><title>A Brief Introduction to Regular Expressions</title>
<para>An expression is a string of characters. Those characters
having an interpretation above and beyond their literal
meaning are called <firstterm>metacharacters</firstterm>.
A quote symbol, for example, may denote speech by a person,
<firstterm>ditto</firstterm>, or a meta-meaning
<footnote><para><anchor id="metameaningref"/>A
<firstterm>meta-meaning</firstterm> is the meaning of a
term or expression on a higher level of abstraction. For
example, the <firstterm>literal</firstterm> meaning
of <firstterm>regular expression</firstterm> is an
ordinary expression that conforms to accepted usage. The
<firstterm>meta-meaning</firstterm> is drastically different,
as discussed at length in this chapter.</para></footnote>
for the symbols that follow. Regular Expressions are sets
of characters and/or metacharacters that match (or specify)
patterns.</para>
<para>A Regular Expression contains one or more of the
following:</para>
<itemizedlist>
<listitem>
<para><firstterm>A character set</firstterm>. These are the
characters retaining their literal meaning. The
simplest type of Regular Expression consists
<emphasis>only</emphasis> of a character set, with no
metacharacters.</para>
</listitem>
<listitem>
<para><anchor id="anchorref"/></para>
<para><firstterm>An anchor</firstterm>. These designate
(<firstterm>anchor</firstterm>) the position in the line of
text that the RE is to match. For example, <token>^</token>,
and <token>$</token> are anchors.</para>
</listitem>
<listitem>
<para><firstterm>Modifiers</firstterm>. These expand or narrow
(<firstterm>modify</firstterm>) the range of text the RE is
to match. Modifiers include the asterisk, brackets, and
the backslash.</para>
</listitem>
</itemizedlist>
<para>The main uses for Regular Expressions
(<firstterm>RE</firstterm>s) are text searches and string
manipulation. An RE <firstterm>matches</firstterm> a single
character or a set of characters -- a string or a part of
a string.</para>
<itemizedlist>
<listitem>
<indexterm>
<primary>*</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>*</secondary>
</indexterm>
<para><anchor id="asteriskreg"/>The asterisk --
<token>*</token> -- matches any number of
repeats of the character string or RE preceding it,
including <emphasis>zero</emphasis> instances.</para>
<para><quote>1133*</quote> matches <replaceable>11 +
one or more 3's</replaceable>:
<replaceable>113</replaceable>, <replaceable>1133</replaceable>,
<replaceable>1133333</replaceable>, and so forth.</para>
</listitem>
<listitem>
<indexterm>
<primary>.</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>.</secondary>
</indexterm>
<para><anchor id="regexdot"/>The <firstterm>dot</firstterm>
-- <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/.*/[&amp;]/' &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>
<para>See <xref linkend="cwsolver"/> for a demonstration
of <firstterm>dot single-character</firstterm>
matching.</para>
</listitem>
<listitem>
<indexterm>
<primary>^</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>^</secondary>
</indexterm>
<para><anchor id="caretref"/>The caret -- <token>^</token>
-- matches the beginning of a line, but sometimes, depending
on context, negates the meaning of a set of characters in
an RE.</para>
</listitem>
<listitem>
<indexterm>
<primary>$</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>$</secondary>
</indexterm>
<para><anchor id="dollarsignref"/></para>
<para>The dollar sign -- <token>$</token> -- at the end of an
RE matches the end of a line.</para>
<para><quote>XXX$</quote> matches <token>XXX</token> at 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><anchor id="bracketsref"/></para>
<para>Brackets -- <token>[...]</token> -- enclose a set of characters
to match in a single RE.</para>
<para><quote>[xyz]</quote> matches any one of the characters
<replaceable>x</replaceable>, <replaceable>y</replaceable>,
or <replaceable>z</replaceable>.</para>
<para><quote>[c-n]</quote> matches any one of the
characters in the range <replaceable>c</replaceable>
to <replaceable>n</replaceable>.</para>
<para><quote>[B-Pk-y]</quote> matches any one 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 single lowercase
letter or any digit.</para>
<para><quote>[^b-d]</quote> matches any character
<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><anchor id="regexbs"/></para>
<para>The backslash -- <token>\</token> -- <link
linkend="escp">escapes</link> a special character, which
means that character gets interpreted literally (and is
therefore no longer <firstterm>special</firstterm>).</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>
<listitem>
<indexterm>
<primary>\&lt; \&gt;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>\&lt; \&gt;</secondary>
</indexterm>
<para><anchor id="anglebrac"/></para>
<para><link linkend="escp">Escaped</link> <quote>angle
brackets</quote> -- <token>\&lt;...\&gt;</token> -- mark word
boundaries.</para>
<para>The angle brackets must be escaped, since otherwise
they have only their literal character meaning.</para>
<para><quote>\&lt;the\&gt;</quote> matches the word
<quote>the,</quote> but not the words <quote>them,</quote>
<quote>there,</quote> <quote>other,</quote> etc.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>cat textfile</userinput>
<computeroutput>This is line 1, of which there is only one instance.
This is the only instance of line 2.
This is line 3, another line.
This is line 4.</computeroutput>
<prompt>bash$ </prompt><userinput>grep 'the' textfile</userinput>
<computeroutput>This is line 1, of which there is only one instance.
This is the only instance of line 2.
This is line 3, another line.</computeroutput>
<prompt>bash$ </prompt><userinput>grep '\&lt;the\&gt;' textfile</userinput>
<computeroutput>This is the only instance of line 2.</computeroutput>
</screen>
</para>
</listitem>
</itemizedlist>
<sidebar>
<para>The only way to be certain that a particular RE works is to
test it.</para>
<para><programlisting>TEST FILE: tstfile # No match.
# No match.
Run grep "1133*" on this file. # Match.
# No match.
# No match.
This line contains the number 113. # Match.
This line contains the number 13. # No match.
This line contains the number 133. # No match.
This line contains the number 1133. # Match.
This line contains the number 113312. # Match.
This line contains the number 1112. # No match.
This line contains the number 113312312. # Match.
This line contains no numbers at all. # No match.</programlisting></para>
<screen><prompt>bash$ </prompt><userinput>grep "1133*" tstfile</userinput>
<computeroutput>Run grep "1133*" on this file. # Match.
This line contains the number 113. # Match.
This line contains the number 1133. # Match.
This line contains the number 113312. # Match.
This line contains the number 113312312. # Match.</computeroutput>
</screen>
</sidebar>
<itemizedlist>
<listitem override="square">
<formalpara>
<title><anchor id="extregex"/>Extended REs</title>
<para>Additional metacharacters added to the basic set. Used
in <link linkend="egrepref">egrep</link>,
<link linkend="awkref">awk</link>, and <link
linkend="perlref">Perl</link>.</para>
</formalpara>
</listitem>
<listitem>
<indexterm>
<primary>?</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>?</secondary>
</indexterm>
<para><anchor id="quexregex"/></para>
<para>The question mark -- <token>?</token> -- matches zero or
one of the previous RE. It is generally used for matching
single characters.</para>
</listitem>
<listitem>
<indexterm>
<primary>+</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>+</secondary>
</indexterm>
<para><anchor id="plusref"/></para>
<para>The plus -- <token>+</token> -- matches one or more of the
previous RE. It serves a role similar to the <token>*</token>, but
does <emphasis>not</emphasis> match zero occurrences.</para>
<para><programlisting># GNU versions of sed and awk can use "+",
# but it needs to be escaped.
echo a111b | sed -ne '/a1\+b/p'
echo a111b | grep 'a1\+b'
echo a111b | gawk '/a1+b/'
# All of above are equivalent.
# Thanks, S.C.</programlisting></para>
<para><anchor id="escpcb"/></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>
<note>
<para>Curly brackets are not available as an RE in the
<quote>classic</quote> (non-POSIX compliant) version
of <link linkend="awkref">awk</link>.
<anchor id="gnugawk"/>However, the GNU extended version
of <firstterm>awk</firstterm>, <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>
<para><command>Perl</command> and some
<command>egrep</command> versions do not require escaping
the curly brackets.</para>
</note>
</listitem>
<listitem>
<indexterm>
<primary>()</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>()</secondary>
</indexterm>
<para><anchor id="parengrps"/></para>
<para>Parentheses -- <command>( )</command> -- enclose a group of
REs. They are useful with the following
<quote><token>|</token></quote> operator and in <link
linkend="exprparen">substring extraction</link> using <link
linkend="exprref">expr</link>.</para>
</listitem>
<listitem>
<indexterm>
<primary>|</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>|</secondary>
</indexterm>
<para>The -- <command>|</command> -- <quote>or</quote> RE operator
matches any of a set of alternate characters.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>egrep 're(a|e)d' misc.txt</userinput>
<computeroutput>People who read seem to be better informed than those who do not.
The clarinet produces sound by the vibration of its reed.</computeroutput>
</screen>
</para>
</listitem>
</itemizedlist>
<note><para>Some versions of <command>sed</command>,
<command>ed</command>, and <command>ex</command> support
escaped versions of the extended Regular Expressions
described above, as do the GNU utilities.</para></note>
<itemizedlist>
<listitem override="square">
<formalpara><title><anchor id="posixref"/>POSIX Character Classes</title>
<para><userinput>[:class:]</userinput></para></formalpara>
<indexterm>
<primary>[:</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>:]</secondary>
</indexterm>
<para>This is an alternate method of specifying a range of
characters to match.</para>
</listitem>
<listitem>
<indexterm>
<primary>alnum</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>alphabetic numeric</secondary>
</indexterm>
<para><userinput>[:alnum:]</userinput> matches alphabetic or
numeric characters. This is equivalent to
<userinput>A-Za-z0-9</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>alpha</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>alphabetic</secondary>
</indexterm>
<para><userinput>[:alpha:]</userinput> matches alphabetic
characters. This is equivalent to
<userinput>A-Za-z</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>blank</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>space tab</secondary>
</indexterm>
<para><userinput>[:blank:]</userinput> matches a space or a
tab.</para>
</listitem>
<listitem>
<indexterm>
<primary>cntrl</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>control</secondary>
</indexterm>
<para><userinput>[:cntrl:]</userinput> matches control
characters.</para>
</listitem>
<listitem>
<indexterm>
<primary>digit</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>decimal digit</secondary>
</indexterm>
<para><userinput>[:digit:]</userinput> matches (decimal)
digits. This is equivalent to
<userinput>0-9</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>graph</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>graph</secondary>
</indexterm>
<para><userinput>[:graph:]</userinput> (graphic printable
characters). Matches characters in the range of <link
linkend="asciidef">ASCII</link> 33 - 126. This is
the same as <userinput>[:print:]</userinput>, below,
but excluding the space character.</para>
</listitem>
<listitem>
<indexterm>
<primary>lower</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>lowercase</secondary>
</indexterm>
<para><userinput>[:lower:]</userinput> matches lowercase
alphabetic characters. This is equivalent to
<userinput>a-z</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>print</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>printable</secondary>
</indexterm>
<para><userinput>[:print:]</userinput> (printable
characters). Matches characters in the range of ASCII 32 -
126. This is the same as <userinput>[:graph:]</userinput>,
above, but adding the space character.</para>
</listitem>
<listitem>
<indexterm>
<primary>space</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>whitespace</secondary>
</indexterm>
<para><anchor id="wsposix"/><userinput>[:space:]</userinput>
matches whitespace characters (space and horizontal
tab).</para>
</listitem>
<listitem>
<indexterm>
<primary>upper</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>uppercase</secondary>
</indexterm>
<para><userinput>[:upper:]</userinput> matches uppercase
alphabetic characters. This is equivalent to
<userinput>A-Z</userinput>.</para>
</listitem>
<listitem>
<indexterm>
<primary>xdigit</primary>
</indexterm>
<indexterm>
<primary>character range</primary>
<secondary>hexadecimal</secondary>
</indexterm>
<para><userinput>[:xdigit:]</userinput> matches hexadecimal
digits. This is equivalent to
<userinput>0-9A-Fa-f</userinput>.</para>
<important>
<para>POSIX character classes generally require quoting
or <link linkend="dblbrackets">double brackets</link>
([[ ]]).</para>
</important>
<para>
<screen><prompt>bash$ </prompt><userinput>grep [[:digit:]] test.file</userinput>
<computeroutput>abc=723</computeroutput>
</screen>
</para>
<para><programlisting># ...
if [[ $arow =~ [[:digit:]] ]] # Numerical input?
then # POSIX char class
if [[ $acol =~ [[:alpha:]] ]] # Number followed by a letter? Illegal!
# ...
# From ktour.sh example script.</programlisting>
</para>
<para>These character classes may even be used with <link
linkend="globbingref">globbing</link>, to a limited
extent.</para>
<para>
<screen><prompt>bash$ </prompt><userinput>ls -l ?[[:digit:]][[:digit:]]?</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 21 14:47 a33b</computeroutput>
</screen>
</para>
<para>POSIX character classes are used in
<xref linkend="ex49"/> and <xref linkend="lowercase"/>.</para>
</listitem>
</itemizedlist>
<para><link linkend="sedref">Sed</link>, <link
linkend="awkref">awk</link>, and <link
linkend="perlref">Perl</link>, used as filters in scripts, take
REs as arguments when "sifting" or transforming files or I/O
streams. See <xref linkend="behead"/> and <xref linkend="tree"/>
for illustrations of this.</para>
<para>The standard reference on this complex topic is Friedl's
<citetitle pubwork="book">Mastering Regular
Expressions</citetitle>. <citetitle pubwork="book">Sed &amp;
Awk</citetitle>, by Dougherty and Robbins, also gives a very
lucid treatment of REs. See the <xref linkend="biblio"/> for
more information on these books.</para>
</sect1> <!-- A Brief Introduction to Regular Expressions -->
<sect1 id="globbingref">
<title>Globbing</title>
<para><anchor id="globbingref2"/></para>
<para>Bash itself cannot recognize Regular Expressions. Inside
scripts, it is commands and utilities -- such as
<link linkend="sedref">sed</link> and <link
linkend="awkref">awk</link> -- that interpret RE's.</para>
<para>Bash <emphasis>does</emphasis> carry out <firstterm>filename
expansion</firstterm>
<footnote><para><firstterm>Filename expansion</firstterm>
means expanding filename patterns or templates
containing special characters. For example,
<filename>example.???</filename> might expand
to <filename>example.001</filename> and/or
<filename>example.txt</filename>.</para></footnote>
-- a process known as <firstterm>globbing</firstterm> -- but
this does <emphasis>not</emphasis> use the standard RE set.
Instead, globbing recognizes and expands <firstterm>wild
cards</firstterm>. Globbing interprets the standard wild
card characters
<footnote><para><anchor id="wildcarddef"/>A <firstterm>wild
card</firstterm> character, analogous to a wild card in poker,
can represent (almost) any other character.</para></footnote>
-- <link linkend="asteriskref">*</link> and
<link linkend="wildcardqu">?</link>, character lists in
square brackets, and certain other special characters (such
as <token>^</token> for negating the sense of a match).
<anchor id="wdotfilewc"/>There are important limitations on wild
card characters in globbing, however. Strings containing
<replaceable>*</replaceable> will not match filenames that
start with a dot, as, for example, <link
linkend="sample-bashrc"><filename>.bashrc</filename></link>.
<footnote>
<para>
Filename expansion <emphasis>can</emphasis>
match dotfiles, but only if the pattern explicitly includes the dot
as a literal character.
<programlisting>~/[.]bashrc # Will not expand to ~/.bashrc
~/?bashrc # Neither will this.
# Wild cards and metacharacters will NOT
#+ expand to a dot in globbing.
~/.[b]ashrc # Will expand to ~/.bashrc
~/.ba?hrc # Likewise.
~/.bashr* # Likewise.
# Setting the "dotglob" option turns this off.
# Thanks, S.C.</programlisting>
</para>
</footnote>
Likewise, the <replaceable>?</replaceable> has a different
meaning in globbing than as part of an RE.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>ls -l</userinput>
<computeroutput>total 2
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l t?.sh</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [ab]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [a-c]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l [^ab]*</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 466 Aug 6 17:48 t2.sh
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>ls -l {b*,c*,*est*}</userinput>
<computeroutput>-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 bozo bozo 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 bozo bozo 758 Jul 30 09:02 test1.txt</computeroutput>
</screen>
</para>
<para>Bash performs filename expansion on unquoted command-line
arguments. The <link linkend="echoref">echo</link> command
demonstrates this.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo *</userinput>
<computeroutput>a.1 b.1 c.1 t2.sh test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>echo t*</userinput>
<computeroutput>t2.sh test1.txt</computeroutput>
<prompt>bash$ </prompt><userinput>echo t?.sh</userinput>
<computeroutput>t2.sh</computeroutput>
</screen>
</para>
<note><para>It is possible to modify the way Bash interprets
special characters in globbing. A <command>set -f</command>
command disables globbing, and the
<option>nocaseglob</option> and <option>nullglob</option>
options to <link linkend="shoptref">shopt</link> change
globbing behavior.</para></note>
<para>See also <xref linkend="listglob"/>.</para>
<caution><para><anchor id="handlingfnames"/>Filenames with
embedded <link linkend="whitespaceref">whitespace</link>
can cause <firstterm>globbing</firstterm> to choke.
<ulink
url="http://www.dwheeler.com/essays/filenames-in-shell.html">David
Wheeler</ulink> shows how to avoid many such pitfalls.</para>
<para><programlisting>IFS="$(printf '\n\t')" # Remove space.
# Correct glob use:
# Always use for-loop, prefix glob, check if exists file.
for file in ./* ; do # Use ./* ... NEVER bare *
if [ -e "$file" ] ; then # Check whether file exists.
COMMAND ... "$file" ...
fi
done
# This example taken from David Wheeler's site, with permission.</programlisting></para>
</caution>
</sect1> <!-- Globbing -->
</chapter> <!-- Regular Expressions -->
<chapter id="here-docs">
<title>Here Documents</title>
<epigraph>
<para>Here and now, boys.</para>
<para>--Aldous Huxley, <firstterm>Island</firstterm></para>
</epigraph>
<para><anchor id="heredocref"/></para>
<indexterm>
<primary>&lt;&lt;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&lt;&lt;</secondary>
</indexterm>
<para>A <firstterm>here document</firstterm> is a special-purpose
code block. It uses a form of <link linkend="ioredirref">I/O
redirection</link> to feed a command list to
an interactive program or a command, such as <link
linkend="ftpref">ftp</link>, <link linkend="catref">cat</link>,
or the <firstterm>ex</firstterm> text editor.</para>
<para><programlisting>COMMAND &lt;&lt;InputComesFromHERE
...
...
...
InputComesFromHERE</programlisting></para>
<para><anchor id="limitstringref"/></para>
<para>A <firstterm>limit string</firstterm> delineates (frames)
the command list. The special symbol <token>&lt;&lt;</token> precedes
the limit string. This has the effect of redirecting the output
of a command block into the <filename>stdin</filename> of the program
or command. It is similar to <userinput>interactive-program &lt;
command-file</userinput>, where <filename>command-file</filename>
contains
<programlisting>command #1
command #2
...</programlisting></para>
<para>The <firstterm>here document</firstterm> equivalent looks
like this:
<programlisting>interactive-program &lt;&lt;LimitString
command #1
command #2
...
LimitString</programlisting></para>
<para>Choose a <firstterm>limit string</firstterm> sufficiently
unusual that it will not occur anywhere in the command list and
confuse matters.</para>
<para>Note that <firstterm>here documents</firstterm> may sometimes
be used to good effect with non-interactive utilities and commands,
such as, for example, <link linkend="wallref">wall</link>.</para>
<example id="ex70">
<title><firstterm>broadcast</firstterm>: Sends message to everyone
logged in</title>
<programlisting>&ex70;</programlisting>
</example>
<para><anchor id="vihere"/></para>
<para>Even such unlikely candidates as the <firstterm>vi</firstterm>
text editor lend themselves to <firstterm>here
documents</firstterm>.</para>
<example id="ex69">
<title><firstterm>dummyfile</firstterm>: 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>. <anchor id="exscriptref"/><firstterm>Here
documents</firstterm> containing a list of <command>ex</command>
commands are common enough to form their own category, known as
<firstterm>ex scripts</firstterm>.
<programlisting>#!/bin/bash
# Replace all instances of "Smith" with "Jones"
#+ in files with a ".txt" filename suffix.
ORIGINAL=Smith
REPLACEMENT=Jones
for word in $(fgrep -l $ORIGINAL *.txt)
do
# -------------------------------------
ex $word &lt;&lt;EOF
:%s/$ORIGINAL/$REPLACEMENT/g
:wq
EOF
# :%s is the "ex" substitution command.
# :wq is write-and-quit.
# -------------------------------------
done</programlisting>
</para>
<para><anchor id="catscriptref"/></para>
<para>Analogous to <quote>ex scripts</quote> are <firstterm>cat
scripts</firstterm>.</para>
<example id="ex71">
<title>Multi-line message using <firstterm>cat</firstterm></title>
<programlisting>&ex71;</programlisting>
</example>
<para><anchor id="limitstrdash"/></para>
<para>The <option>-</option> option to mark a here document limit string
(<userinput>&lt;&lt;-LimitString</userinput>) suppresses leading
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><anchor id="herepassp"/></para>
<para>A <firstterm>here document</firstterm> 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 replaceable parameters</title>
<programlisting>&ex71b;</programlisting>
</example>
<para><anchor id="hereparamsub"/></para>
<para>This is a useful script containing a <firstterm>here
document</firstterm> with parameter substitution.</para>
<example id="ex72">
<title>Upload a file pair to <firstterm>Sunsite</firstterm> incoming
directory</title>
<programlisting>&ex72;</programlisting>
</example>
<para><anchor id="hereesc"/></para>
<para>Quoting or escaping the <quote>limit string</quote> at the
head of a here document disables parameter substitution within its
body. The reason for this is that <firstterm>quoting/escaping the
limit string</firstterm> effectively <link
linkend="escp">escapes</link> the <token>$</token>,
<token>`</token>, and <token>\</token> <link
linkend="scharlist">special characters</link>, and causes them to
be interpreted literally. (Thank you, Allen Halsey, for pointing
this out.)</para>
<example id="ex71c">
<title>Parameter substitution turned off</title>
<programlisting>&ex71c;</programlisting>
</example>
<para><anchor id="herelit"/></para>
<para>Disabling parameter substitution permits outputting literal text.
Generating scripts or even program code is one use for this.</para>
<example id="generatescript">
<title>A script that generates another script</title>
<programlisting>&generatescript;</programlisting>
</example>
<para><anchor id="herecs"/></para>
<para>
It is possible to set a variable from the output of a here document.
This is actually a devious form of <link
linkend="commandsubref">command substitution</link>.
<programlisting>variable=$(cat &lt;&lt;SETVAR
This variable
runs over multiple lines.
SETVAR
)
echo "$variable"</programlisting>
</para>
<para><anchor id="herefunc"/></para>
<para>A here document can supply input to a function in the same
script.</para>
<example id="hf">
<title>Here documents and functions</title>
<programlisting>&hf;</programlisting>
</example>
<para><anchor id="anonheredoc0"/></para>
<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 $?</programlisting>
</example>
<para><anchor id="cblock1"/></para>
<tip><para>A variation of the above technique permits <quote>commenting
out</quote> blocks of code.</para></tip>
<example id="commentblock">
<title>Commenting out a block of code</title>
<programlisting>&commentblock;</programlisting>
</example>
<para><anchor id="hselfdoc"/></para>
<tip><para>Yet another twist of this nifty trick makes
<quote>self-documenting</quote> scripts possible.</para></tip>
<example id="selfdocument">
<title>A self-documenting script</title>
<programlisting>&selfdocument;</programlisting>
</example>
<para>Using a <link linkend="catscriptref">cat script</link> is an
alternate way of accomplishing this.</para>
<para>
<programlisting>DOC_REQUEST=70
if [ "$1" = "-h" -o "$1" = "--help" ] # Request help.
then # Use a "cat script" . . .
cat &lt;&lt;DOCUMENTATIONXX
List the statistics of a specified directory in tabular format.
---------------------------------------------------------------
The command-line parameter gives the directory to be listed.
If no directory specified or directory specified cannot be read,
then list the current working directory.
DOCUMENTATIONXX
exit $DOC_REQUEST
fi</programlisting>
</para>
<para>See also <xref linkend="isspammer2"/>, <xref linkend="petals"/>,
<xref linkend="qky"/>, and <xref linkend="nim"/> for more examples
of self-documenting scripts.</para>
<para><anchor id="heretemp"/></para>
<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' &lt;&lt; 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 inside a
<firstterm>here document</firstterm>.</para></caution>
<para><anchor id="indentedls"/></para>
<warning>
<para>The closing <firstterm>limit string</firstterm>,
on the final line of a here document, must start in the
<emphasis>first</emphasis> character position. There can
be <emphasis>no leading whitespace</emphasis>. Trailing
whitespace after the limit string likewise causes unexpected
behavior. The whitespace prevents the limit string from being
recognized.
<footnote><para>Except, as Dennis Benzinger points out,
if <link linkend="limitstrdash">using
<command>&lt;&lt;-</command> to suppress
tabs</link>.</para></footnote>
</para>
<para>
<programlisting>#!/bin/bash
echo "----------------------------------------------------------------------"
cat &lt;&lt;LimitString
echo "This is line 1 of the message inside the here document."
echo "This is line 2 of the message inside the here document."
echo "This is the final line of the message inside the here document."
LimitString
#^^^^Indented limit string. Error! This script will not behave as expected.
echo "----------------------------------------------------------------------"
# These comments are outside the 'here document',
#+ and should not echo.
echo "Outside the here document."
exit 0
echo "This line had better not echo." # Follows an 'exit' command.</programlisting>
</para>
</warning>
<caution>
<para><anchor id="exclls"/>Some people very cleverly use a
single <token>!</token> as a limit string. But, that's not
necessarily a good idea.</para>
<para><programlisting># This works.
cat &lt;&lt;!
Hello!
! Three more exclamations !!!
!
# But . . .
cat &lt;&lt;!
Hello!
Single exclamation point follows!
!
!
# Crashes with an error message.
# However, the following will work.
cat &lt;&lt;EOF
Hello!
Single exclamation point follows!
!
EOF
# It's safer to use a multi-character limit string.</programlisting></para>
</caution>
<para>For those tasks too complex for a <firstterm>here
document</firstterm>, consider using the
<replaceable>expect</replaceable> scripting language, which
was specifically designed for feeding input into interactive
programs.</para>
<sect1><title>Here Strings</title>
<para><anchor id="herestringsref"/></para>
<blockquote>
<literallayout>
A <firstterm>here string</firstterm> can be considered as a stripped-down form of a <firstterm>here document</firstterm>.
It consists of nothing more than <command>COMMAND &lt;&lt;&lt; $WORD</command>,
where <varname>$WORD</varname> is expanded and fed to the <filename>stdin</filename> of <command>COMMAND</command>.
</literallayout>
</blockquote>
<para>As a simple example, consider this alternative to the <link
linkend="echogrepref">echo-grep</link> construction.</para>
<para>
<programlisting># Instead of:
if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]]
# etc.
# Try:
if grep -q "txt" &lt;&lt;&lt; "$VAR"
then # ^^^
echo "$VAR contains the substring sequence \"txt\""
fi
# Thank you, Sebastian Kaminski, for the suggestion.</programlisting>
</para>
<para><anchor id="hsread"/></para>
<para>Or, in combination with <link linkend="readref">read</link>:</para>
<para>
<programlisting>String="This is a string of words."
read -r -a Words &lt;&lt;&lt; "$String"
# The -a option to "read"
#+ assigns the resulting values to successive members of an array.
echo "First word in String is: ${Words[0]}" # This
echo "Second word in String is: ${Words[1]}" # is
echo "Third word in String is: ${Words[2]}" # a
echo "Fourth word in String is: ${Words[3]}" # string
echo "Fifth word in String is: ${Words[4]}" # of
echo "Sixth word in String is: ${Words[5]}" # words.
echo "Seventh word in String is: ${Words[6]}" # (null)
# Past end of $String.
# Thank you, Francisco Lobo, for the suggestion.</programlisting>
</para>
<para><anchor id="hsloop"/>It is, of course, possible to feed
the output of a <firstterm>here string</firstterm>
into the <filename>stdin</filename> of a <link
linkend="loopref00">loop</link>.</para>
<para><programlisting># As Seamus points out . . .
ArrayVar=( element0 element1 element2 {A..D} )
while read element ; do
echo "$element" 1>&amp;2
done &lt;&lt;&lt; $(echo ${ArrayVar[*]})
# element0 element1 element2 A B C D</programlisting></para>
<para><anchor id="hspre"/></para>
<example id="prependex">
<title>Prepending a line to a file</title>
<programlisting>&prependex;</programlisting>
</example>
<example id="mailboxgrep">
<title>Parsing a mailbox</title>
<programlisting>&mailboxgrep;</programlisting>
</example>
<para>Exercise: Find other uses for <firstterm>here
strings</firstterm>, such as, for example, <link
linkend="goldenratio">feeding input to
<firstterm>dc</firstterm></link>.</para>
</sect1><!-- Here Strings -->
</chapter> <!-- Here Documents -->
<chapter id="io-redirection">
<title>I/O Redirection</title>
<para><anchor id="ioredirref"/></para>
<para>There are always three default <firstterm>files</firstterm>
<footnote><para>By convention in UNIX and Linux, data streams
and peripherals (<link linkend="devfileref">device files</link>)
are treated as files, in a fashion analogous to ordinary
files.</para></footnote>
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><anchor id="fdref1"/>A <firstterm>file
descriptor</firstterm> is simply a number that
the operating system assigns to an open file
to keep track of it. Consider it a simplified
type of file pointer. It is analogous
to a <firstterm>file handle</firstterm> in
<command>C</command>.</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://groups.google.com/group/gnu.bash.bug/browse_thread/thread/13955daafded3b5c/18c17050087f9f37">
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> COMMAND_OUTPUT >
# 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.
# If file not present, creates zero-length file (same effect as 'touch').
# The : serves as a dummy placeholder, producing no output.
> filename
# The > truncates file "filename" to zero length.
# If file not present, creates zero-length file (same effect as 'touch').
# (Same result as ": >", above, but this does not work with some shells.)
COMMAND_OUTPUT >>
# Redirect stdout to a file.
# Creates the file if not present, otherwise appends to it.
# Single-line redirection commands (affect only the line they are on):
# --------------------------------------------------------------------
1>filename
# Redirect stdout to file "filename."
1>>filename
# Redirect and append stdout to file "filename."
2>filename
# Redirect stderr to file "filename."
2>>filename
# Redirect and append stderr to file "filename."
&amp;>filename
# Redirect both stdout and stderr to file "filename."
# This operator is now functional, as of Bash 4, final release.
M>N
# "M" is a file descriptor, which defaults to 1, if not explicitly set.
# "N" is a filename.
# File descriptor "M" is redirect to file "N."
M&gt;&amp;N
# "M" is a file descriptor, which defaults to 1, if not set.
# "N" is another file descriptor.
#==============================================================================
# Redirecting stdout, one line at a time.
LOGFILE=script.log
echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE
echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
# These redirection commands automatically "reset" after each line.
# Redirecting stderr, one line at a time.
ERRORFILE=script.errors
bad_command1 2>$ERRORFILE # Error message sent to $ERRORFILE.
bad_command2 2>>$ERRORFILE # Error message appended to $ERRORFILE.
bad_command3 # Error message echoed to stderr,
#+ and does not appear in $ERRORFILE.
# These redirection commands also automatically "reset" after each line.
#=======================================================================</programlisting>
<para><anchor id="ioredirectionref1"/></para>
<programlisting>
2>&amp;1
# Redirects stderr to stdout.
# Error messages get sent to same place as standard output.
>>filename 2>&amp;1
bad_command >>filename 2>&amp;1
# Appends both stdout and stderr to the file "filename" ...
2>&amp;1 | [command(s)]
bad_command 2>&amp;1 | awk '{print $5}' # found
# Sends stderr through a pipe.
# |&amp; was added to Bash 4 as an abbreviation for 2>&amp;1 |.
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>.</programlisting>
<para><anchor id="ioredirectionref2"/></para>
<programlisting>
0&lt; FILENAME
&lt; FILENAME
# 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 &lt;&amp;3 # Read only 4 characters.
echo -n . >&amp;3 # Write a decimal point there.
exec 3>&amp;- # 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
# Or the equivalent:
&lt; input-file command &gt; output-file # Although this is non-standard.
command1 | command2 | command3 > output-file</programlisting>
See <xref linkend="derpm"/> and <xref linkend="fifo"/>.</para>
<para>Multiple output streams may be redirected to one file.
<programlisting>ls -yz >> command.log 2>&amp;1
# Capture result of illegal options "yz" in file "command.log."
# Because stderr is redirected to the file,
#+ any error messages will also be there.
# Note, however, that the following does *not* give the same result.
ls -yz 2>&amp;1 >> command.log
# Outputs an error message, but does not write to file.
# More precisely, the command output (in this case, null)
#+ writes to the file, but the error message goes only to stdout.
# If redirecting both stdout and stderr,
#+ the order of the commands makes a difference.
</programlisting></para>
<variablelist id="closingfiledescriptors">
<title><anchor id="cfd"/>Closing File Descriptors</title>
<varlistentry>
<term><token>n&lt;&amp;-</token></term>
<listitem>
<para>Close input file descriptor
<replaceable>n</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>0&lt;&amp;-</token></term>
<term><token>&lt;&amp;-</token></term>
<listitem>
<para>Close <filename>stdin</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>n>&amp;-</token></term>
<listitem>
<para>Close output file descriptor <replaceable>n</replaceable>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><token>1>&amp;-</token></term>
<term><token>>&amp;-</token></term>
<listitem>
<para>Close <filename>stdout</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
<para><anchor id="fdref2"/></para>
<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>&amp;1 # Save current "value" of stdout.
ls -l 2>&amp;1 >&amp;3 3>&amp;- | grep bad 3>&amp;- # Close fd 3 for 'grep' (but not 'ls').
# ^^^^ ^^^^
exec 3>&amp;- # Now close it for the remainder of the script.
# Thanks, S.C.</programlisting>
</para>
<para>For a more detailed introduction to I/O redirection see
<xref linkend="ioredirintro"/>.</para>
<sect1><title>Using <firstterm>exec</firstterm></title>
<para><anchor id="usingexecref"/></para>
<para>An <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
<firstterm>exec</firstterm></title>
<programlisting>&redir1;</programlisting>
</example>
<para>Similarly, an <command>exec &gt;filename</command>
command redirects <filename>stdout</filename> to a designated
file. This sends all command output that would normally go
to <filename>stdout</filename> to that file.</para>
<important>
<para>
<command>exec N > filename</command> affects the entire
script or <firstterm>current shell</firstterm>. Redirection in
the <link linkend="processidref">PID</link> of the script or shell
from that point on has changed. However . . .
</para>
<para>
<command>N > filename</command> affects only the newly-forked process,
not the entire script or shell.
</para>
<para>Thank you, Ahmed Darwish, for pointing this out.</para>
</important>
<example id="reassignstdout">
<title>Redirecting <filename>stdout</filename> using
<firstterm>exec</firstterm></title>
<programlisting>&reassignstdout;</programlisting>
</example>
<example id="upperconv">
<title>Redirecting both <filename>stdin</filename> and
<filename>stdout</filename> in the same script with
<firstterm>exec</firstterm></title>
<programlisting>&upperconv;</programlisting>
</example>
<para>I/O redirection is a clever way of avoiding the dreaded <link
linkend="parvis">inaccessible variables within a subshell</link>
problem.</para>
<example id="avoidsubshell">
<title>Avoiding a subshell</title>
<programlisting>&avoidsubshell;</programlisting>
</example>
</sect1><!-- Using exec For Redirection -->
<sect1 id="redircb"><title>Redirecting Code Blocks</title>
<para><anchor id="redirref"/>Blocks of code, such as <link
linkend="whileloopref">while</link>, <link
linkend="untilloopref">until</link>, and <link
linkend="forloopref1">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 end of the code block
accomplishes this.</para>
<example id="redir2">
<title>Redirected <firstterm>while</firstterm> loop</title>
<programlisting>&redir2;</programlisting>
</example>
<example id="redir2a">
<title>Alternate form of redirected <firstterm>while</firstterm> loop</title>
<programlisting>&redir2a;</programlisting>
</example>
<example id="redir3">
<title>Redirected <firstterm>until</firstterm> loop</title>
<programlisting>&redir3;</programlisting>
</example>
<example id="redir4">
<title>Redirected <firstterm>for</firstterm> loop</title>
<programlisting>&redir4;</programlisting>
</example>
<para>We can modify the previous example to also redirect the output of
the loop.</para>
<example id="redir4a">
<title>Redirected <firstterm>for</firstterm> loop (both
<filename>stdin</filename> and <filename>stdout</filename>
redirected)</title>
<programlisting>&redir4a;</programlisting>
</example>
<example id="redir5">
<title>Redirected <firstterm>if/then</firstterm> test</title>
<programlisting>&redir5;</programlisting>
</example>
<example id="namesdata">
<title>Data file <firstterm>names.data</firstterm> for above
examples</title>
<programlisting>&namesdata;</programlisting>
</example>
<para>Redirecting the <filename>stdout</filename> of a code
block has the effect of saving its output to a file. See <xref
linkend="rpmcheck"/>.</para>
<para><link linkend="heredocref">Here documents</link>
are a special case of redirected code blocks. That being the case,
it should be possible to feed the output of a <firstterm>here
document</firstterm> into the <filename>stdin</filename> for a
<firstterm>while loop</firstterm>.</para>
<para>
<programlisting># This example by Albert Siersema
# Used with permission (thanks!).
function doesOutput()
# Could be an external command too, of course.
# Here we show you can use a function as well.
{
ls -al *.jpg | awk '{print $5,$9}'
}
nr=0 # We want the while loop to be able to manipulate these and
totalSize=0 #+ to be able to see the changes after the 'while' finished.
while read fileSize fileName ; do
echo "$fileName is $fileSize bytes"
let nr++
totalSize=$((totalSize+fileSize)) # Or: "let totalSize+=fileSize"
done&lt;&lt;EOF
$(doesOutput)
EOF
echo "$nr files totaling $totalSize bytes"</programlisting>
</para>
</sect1><!-- Redirecting Code Blocks -->
<sect1 id="redirapps"><title>Applications</title>
<para>Clever use of I/O redirection permits parsing and stitching
together snippets of command output (see <xref
linkend="readredir"/>). This permits
generating report and log files.</para>
<example id="logevents">
<title>Logging events</title>
<programlisting>&logevents;</programlisting>
</example>
</sect1><!-- Applications -->
</chapter> <!-- I/O Redirection -->
<chapter id="subshells">
<title>Subshells</title>
<para><anchor id="subshellsref"/></para>
<para>Running a shell script launches a new process, a
<firstterm>subshell</firstterm>.</para>
<sidebar>
<para><userinput>Definition:</userinput>
A <firstterm>subshell</firstterm> is a
<link linkend="childref2">child process</link> launched by a
shell (or <firstterm>shell script</firstterm>).</para>
</sidebar>
<para>A subshell is a separate instance of the command processor
-- the <firstterm>shell</firstterm> that gives you the prompt at
the console or in an <firstterm>xterm</firstterm> window. Just
as your commands are interpreted at the command-line prompt,
similarly does a script <link
linkend="batchprocref">batch-process</link> a list of
commands. Each shell script running is, in effect, a subprocess
(<firstterm>child process</firstterm>) of the <link
linkend="forkref">parent</link> shell.</para>
<para>A shell script can itself launch subprocesses. These
<firstterm>subshells</firstterm> let the script do
parallel processing, in effect executing multiple subtasks
simultaneously.</para>
<para>
<programlisting>#!/bin/bash
# subshell-test.sh
(
# Inside parentheses, and therefore a subshell . . .
while [ 1 ] # Endless loop.
do
echo "Subshell running . . ."
done
)
# Script will run forever,
#+ or at least until terminated by a Ctl-C.
exit $? # End of script (but will never get here).
Now, run the script:
sh subshell-test.sh
And, while the script is running, from a different xterm:
ps -ef | grep subshell-test.sh
UID PID PPID C STIME TTY TIME CMD
500 2698 2502 0 14:26 pts/4 00:00:00 sh subshell-test.sh
500 2699 2698 21 14:26 pts/4 00:00:24 sh subshell-test.sh
^^^^
Analysis:
PID 2698, the script, launched PID 2699, the subshell.
Note: The "UID ..." line would be filtered out by the "grep" command,
but is shown here for illustrative purposes.</programlisting>
</para>
<para>In general, an <link linkend="externalref">external
command</link> in a script <link linkend="forkref">forks
off</link> a subprocess,
<footnote><para>An external command invoked with an <link
linkend="execref">exec</link> does <emphasis>not</emphasis>
(usually) fork off a subprocess / subshell.</para></footnote>
whereas a Bash <link linkend="builtinref">builtin</link>
does not. For this reason, builtins execute more quickly
and use fewer system resources than their external command
equivalents.</para>
<variablelist id="subshellparens">
<title><anchor id="subshellparens1"/>Command List within
Parentheses</title>
<varlistentry>
<term>( command1; command2; command3; ... )</term>
<listitem>
<para>A command list embedded between
<replaceable>parentheses</replaceable> runs as a
subshell.</para>
</listitem>
</varlistentry>
</variablelist>
<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,
variables <link linkend="localref">local</link> to the
<firstterm>child process</firstterm>.</para>
<example id="subshell">
<title>Variable scope in a subshell</title>
<programlisting>&subshell;</programlisting>
</example>
<para>See also <link linkend="bashpidref">$BASHPID</link> and
<xref linkend="subpit"/>.</para>
<sidebar>
<para><anchor id="scoperef"/></para>
<para><userinput>Definition:</userinput> The
<firstterm>scope</firstterm> of a variable is the
context in which it has meaning, in which it has a
<firstterm>value</firstterm> that can be referenced. For
example, the scope of a <link linkend="localref1">local
variable</link> lies only within the function,
block of code, or subshell within which it is defined,
while the scope of a <firstterm>global</firstterm> variable
is the entire script in which it appears.</para>
</sidebar>
<para><anchor id="subshnlevref"/></para>
<note>
<para>While the <link linkend="bashsubshellref">$BASH_SUBSHELL</link>
internal variable indicates the nesting level of a
subshell, the <link linkend="shlvlref">$SHLVL</link>
variable <emphasis>shows no change</emphasis> within
a subshell.</para>
<para>
<programlisting>echo " \$BASH_SUBSHELL outside subshell = $BASH_SUBSHELL" # 0
( echo " \$BASH_SUBSHELL inside subshell = $BASH_SUBSHELL" ) # 1
( ( echo " \$BASH_SUBSHELL inside nested subshell = $BASH_SUBSHELL" ) ) # 2
# ^ ^ *** nested *** ^ ^
echo
echo " \$SHLVL outside subshell = $SHLVL" # 3
( echo " \$SHLVL inside subshell = $SHLVL" ) # 3 (No change!)</programlisting>
</para>
</note>
<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>
As seen here, the <link linkend="exitref">exit</link>
command only terminates the subshell in which it is running,
<emphasis>not</emphasis> the parent shell or script.</para>
<para>One application of such a <quote>dedicated environment</quote>
is testing whether a variable is defined.
<programlisting>if (set -u; : $variable) 2&gt; /dev/null
then
echo "Variable is set."
fi # Variable has been set in current script,
#+ or is an internal Bash variable,
#+ or is present in environment (has been exported).
# Could also be written [[ ${variable-x} != x || ${variable-y} != y ]]
# or [[ ${variable-x} != x$variable ]]
# or [[ ${variable+x} = x ]]
# or [[ ${variable-x} != x ]]</programlisting></para>
<para>Another application is checking for a lock file:
<programlisting>if (set -C; : &gt; lock_file) 2&gt; /dev/null
then
: # lock_file didn't exist: no user running the script
else
echo "Another user is already running that script."
exit 65
fi
# Code snippet by St&eacute;phane Chazelas,
#+ with modifications by Paulo Marcel Coelho Aragao.</programlisting>
</para>
<para>+</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) &amp;
(cat list4 list5 list6 | sort | uniq > list456) &amp;
# Merges and sorts both sets of lists simultaneously.
# Running in background ensures parallel execution.
#
# Same effect as
# cat list1 list2 list3 | sort | uniq > list123 &amp;
# cat list4 list5 list6 | sort | uniq > list456 &amp;
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 code block between <link
linkend="codeblockref">curly brackets</link> does
<emphasis>not</emphasis> launch a subshell.</para>
<para>{ command1; command2; command3; . . . commandN; }</para>
<para><programlisting>var1=23
echo "$var1" # 23
{ var1=76; }
echo "$var1" # 76</programlisting></para>
</note>
</chapter> <!-- Subshells -->
<chapter id="restricted-sh">
<title>Restricted Shells</title>
<para><anchor id="restrictedshref"/></para>
<variablelist id="disabledcommref0">
<title><anchor id="disabledcommref"/>Disabled commands in restricted
shells</title>
<varlistentry>
<term></term>
<listitem>
<formalpara><title> </title>
<para>Running a script or portion of a script in
<firstterm>restricted mode</firstterm> disables certain commands
that would otherwise be available. This is a security measure
intended to limit the privileges of the script user and to
minimize possible damage from running the script.</para>
</formalpara>
</listitem>
</varlistentry>
</variablelist>
<para>The following commands and actions are disabled:</para>
<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> <link
linkend="envref">environmental variables</link>.</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>/</token>'s.</para>
</listitem>
<listitem>
<para>Invoking <link linkend="execref">exec</link> 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>
<example id="restricted">
<title>Running a script in restricted mode</title>
<programlisting>&restricted;</programlisting>
</example>
</chapter> <!-- Restricted Shells -->
<chapter id="process-sub">
<title>Process Substitution</title>
<para><anchor id="processsubref"/><link
linkend="piperef">Piping</link> the <filename>stdout</filename>
of a command into the <filename>stdin</filename> of another
is a powerful technique. But, what if you need to pipe the
<filename>stdout</filename> of <emphasis>multiple</emphasis>
commands? This is where <replaceable>process
substitution</replaceable> comes in.</para>
<para><firstterm>Process substitution</firstterm> feeds the
output of a <link linkend="processref">process</link> (or
processes) into the <filename>stdin</filename> of another
process.</para>
<variablelist id="commandsparens">
<title><anchor id="commandsparens1"/>Template</title>
<varlistentry>
<term>Command list enclosed within parentheses</term>
<listitem>
<para><command>&gt;(command_list)</command></para>
<para><command>&lt;(command_list)</command></para>
<para>Process substitution uses
<filename>/dev/fd/&lt;n&gt;</filename> files to send the
results of the process(es) 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>
<caution><para>There is <emphasis>no</emphasis> space between the
the <quote>&lt;</quote> or <quote>></quote> and the parentheses.
Space there would give an error message.</para></caution>
</listitem>
</varlistentry>
</variablelist>
<para>
<screen><prompt>bash$ </prompt><userinput>echo >(true)</userinput>
<computeroutput>/dev/fd/63</computeroutput>
<prompt>bash$ </prompt><userinput>echo &lt;(true)</userinput>
<computeroutput>/dev/fd/63</computeroutput>
<prompt>bash$ </prompt><userinput>echo >(true) &lt;(true)</userinput>
<computeroutput>/dev/fd/63 /dev/fd/62</computeroutput>
<prompt>bash$ </prompt><userinput>wc &lt;(cat /usr/share/dict/linux.words)</userinput>
<computeroutput> 483523 483523 4992010 /dev/fd/63</computeroutput>
<prompt>bash$ </prompt><userinput>grep script /usr/share/dict/linux.words | wc</userinput>
<computeroutput> 262 262 3601</computeroutput>
<prompt>bash$ </prompt><userinput>wc &lt;(grep script /usr/share/dict/linux.words)</userinput>
<computeroutput> 262 262 3601 /dev/fd/63</computeroutput>
</screen>
</para>
<note><para>
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></note>
<para>Process substitution can compare the output of two
different commands, or even the output of different options
to the same command.</para>
<screen><prompt>bash$ </prompt><userinput>comm &lt;(ls -l) &lt;(ls -al)</userinput>
<computeroutput>total 12
-rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0
-rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2
-rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh
total 20
drwxrwxrwx 2 bozo bozo 4096 Mar 10 18:10 .
drwx------ 72 bozo bozo 4096 Mar 10 17:58 ..
-rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0
-rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2
-rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh</computeroutput></screen>
<para><anchor id="pcc2dir"/></para>
<para>
Process substitution can compare the contents
of two directories -- to see which filenames are in one,
but not the other.</para>
<para>
<programlisting>diff &lt;(ls $first_directory) &lt;(ls $second_directory)</programlisting>
</para>
<para>Some other usages and uses of process substitution:</para>
<para><anchor id="psfdstdin"/></para>
<para><programlisting>read -a list &lt; &lt;( od -Ad -w24 -t u2 /dev/urandom )
# Read a list of random numbers from /dev/urandom,
#+ process with "od"
#+ and feed into stdin of "read" . . .
# From "insertion-sort.bash" example script.
# Courtesy of JuanJo Ciarlante.</programlisting></para>
<para><anchor id="netcatexample"/></para>
<para><programlisting>PORT=6881 # bittorrent
# Scan the port to make sure nothing nefarious is going on.
netcat -l $PORT | tee&gt;(md5sum -&gt;mydata-orig.md5) |
gzip | tee&gt;(md5sum - | sed 's/-$/mydata.lz2/'&gt;mydata-gz.md5)&gt;mydata.gz
# Check the decompression:
gzip -d&lt;mydata.gz | md5sum -c mydata-orig.md5)
# The MD5sum of the original checks stdin and detects compression issues.
# Bill Davidsen contributed this example
#+ (with light edits by the ABS Guide author).</programlisting></para>
<para><programlisting>cat &lt;(ls -l)
# Same as ls -l | cat
sort -k 9 &lt;(ls -l /bin) &lt;(ls -l /usr/bin) &lt;(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 &lt;(command1) &lt;(command2) # Gives difference in command output.
tar cf >(bzip2 -c > file.tar.bz2) $directory_name
# Calls "tar cf /dev/fd/?? $directory_name", 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 &lt; pipe > file.tar.bz2&amp;
tar cf pipe $directory_name
rm pipe
# or
exec 3>&amp;1
tar cf /dev/fd/4 $directory_name 4>&amp;1 >&amp;3 3>&amp;- | bzip2 -c > file.tar.bz2 3>&amp;-
exec 3>&amp;-
# Thanks, St&eacute;phane Chazelas</programlisting></para>
<para><anchor id="goodread0"/>Here is a method of circumventing the
problem of an <link linkend="badread0"><firstterm>echo</firstterm>
piped to a <firstterm>while-read loop</firstterm></link> running
in a subshell.</para>
<example id="wrps">
<title>Code block redirection without forking</title>
<programlisting>&wrps;</programlisting>
</example>
<para><anchor id="psubpiping"/>This is a similar example.</para>
<example id="psubp">
<title>Redirecting the output of <firstterm>process
substitution</firstterm> into a loop.</title>
<programlisting>&psubp;</programlisting>
</example>
<para>A reader 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 &lt; &lt;(route -n)
# ^ ^ First &lt; is redirection, second is process substitution.
# To test it, let's make it do something.
while read des what mask iface; do
echo $des $what $mask $iface
done &lt; &lt;(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 St&eacute;phane Chazelas 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 # This yields the same output as above.
# However, as Ulrich Gayer points out . . .
#+ this simplified equivalent uses a subshell for the while loop,
#+ and therefore the variables disappear when the pipe terminates.
# --------------------------------------------------------------#
# However, Filip Moritz comments that there is a subtle difference
#+ between the above two examples, as the following shows.
(
route -n | while read x; do ((y++)); done
echo $y # $y is still unset
while read x; do ((y++)); done &lt; &lt;(route -n)
echo $y # $y has the number of lines of output of route -n
)
More generally spoken
(
: | x=x
# seems to start a subshell like
: | ( x=x )
# while
x=x &lt; &lt;(:)
# does not
)
# This is useful, when parsing csv and the like.
# That is, in effect, what the original SuSE code fragment does.</programlisting></para>
</chapter> <!-- Process Substitution -->
<chapter id="functions">
<title>Functions</title>
<para><anchor id="functionref"/></para>
<para>Like <quote>real</quote> programming languages,
Bash has functions, though in a somewhat limited implementation.
A function is a subroutine, a <link linkend="codeblockref">code
block</link> that implements a set of operations, a <quote>black
box</quote> that performs a specified task. Wherever there is
repetitive code, when a task repeats with only slight variations in
procedure, 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 <link
linkend="portabilityissues">portable</link>).</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>
<note>
<para>A function may be <quote>compacted</quote> into a single
line.</para>
<para><programlisting>fun () { echo "This is a function"; echo; }
# ^ ^</programlisting></para>
<para>In this case, however, a <firstterm>semicolon</firstterm>
must follow the final command in the function.</para>
<para><programlisting>fun () { echo "This is a function"; echo } # Error!
# ^
fun2 () { echo "Even a single-command function? Yes!"; }
# ^</programlisting></para>
</note>
<para>Functions are called, <firstterm>triggered</firstterm>, simply by
invoking their names. <emphasis>A function call is equivalent to
a command.</emphasis></para>
<example id="ex59">
<title>Simple functions</title>
<programlisting>&ex59;</programlisting>
</example>
<para><anchor id="functdefmust"/></para>
<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.
declare -f f1 # This doesn't help either.
f1 # Still an error message.
# 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 permissible.
# Thanks, S.C.</programlisting>
</para>
<note><para><anchor id="emptyfunc"/>Functions may not be empty!
<programlisting>#!/bin/bash
# empty-function.sh
empty ()
{
}
exit 0 # Will not exit here!
# $ sh empty-function.sh
# empty-function.sh: line 6: syntax error near unexpected token `}'
# empty-function.sh: line 6: `}'
# $ echo $?
# 2
# Note that a function containing only comments is empty.
func ()
{
# Comment 1.
# Comment 2.
# This is still an empty function.
# Thank you, Mark Bova, for pointing this out.
}
# Results in same error message as above.
# However ...
not_quite_empty ()
{
illegal_command
} # A script containing this function will *not* bomb
#+ as long as the function is not called.
not_empty ()
{
:
} # Contains a : (null command), and this is okay.
# Thank you, Dominick Geyer and Thiemo Kellner.</programlisting>
</para></note>
<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.
# Even a preceding "declare -f f2" wouldn't help.
echo
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"; } # Permissible, 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 ]] &amp;&amp; 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.
# Or, similarly:
filename=file1
[ -f "$filename" ] &amp;&amp;
foo () { rm -f "$filename"; echo "File "$filename" deleted."; } ||
foo () { echo "File "$filename" not found."; touch bar; }
foo
# Thanks, S.C. and Christopher Head</programlisting>
</para>
<para><anchor id="fstrangeref"/>Function names can take strange
forms.
<programlisting> _(){ for i in {1..10}; do echo -n "$FUNCNAME"; done; echo; }
# ^^^ No space between function name and parentheses.
# This doesn't always work. Why not?
# Now, let's invoke the function.
_ # __________
# ^^^^^^^^^^ 10 underscores (10 x function name)!
# A "naked" underscore is an acceptable function name.
# In fact, a colon is likewise an acceptable function name.
:(){ echo ":"; }; :
# Of what use is this?
# It's a devious way to obfuscate the code in a script.</programlisting>
See also <xref linkend="gronsfeld"/></para>
<note><para>What happens when different versions of the same function
appear in a script?
<programlisting># As Yan Chen points out,
# when a function is defined multiple times,
# the final version is what is invoked.
# This is not, however, particularly useful.
func ()
{
echo "First version of func ()."
}
func ()
{
echo "Second version of func ()."
}
func # Second version of func ().
exit $?
# It is even possible to use functions to override
#+ or preempt system commands.
# Of course, this is *not* advisable.</programlisting></para></note>
<!-- End of intro section -->
<sect1 id="complexfunct">
<title>Complex Functions and Function Complexities</title>
<para>Functions may process arguments passed to them and return
an <link linkend="exitstatusref">exit status</link> to the script
for further processing.</para>
<programlisting>function_name $arg1 $arg2</programlisting>
<para><anchor id="passedargs"/></para>
<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>
<para><anchor id="fshiftref"/></para>
<important><para>The <link linkend="shiftref">shift</link>
command works on arguments passed to functions (see <xref
linkend="multiplication"/>).</para></important>
<para>But, what about command-line arguments passed to the script?
Does a function see them? Well, let's clear up the confusion.</para>
<example id="funccmdlinearg">
<title>Functions and command-line args passed to the script</title>
<programlisting>&funccmdlinearg;</programlisting>
</example>
<para>In contrast to certain other programming languages,
shell scripts normally pass only value parameters to
functions. Variable names (which are actually
<firstterm>pointers</firstterm>), if
passed as parameters to functions, will be treated as string
literals. <emphasis>Functions interpret their arguments
literally.</emphasis></para>
<para><anchor id="funcpointers"/></para>
<para><link linkend="ivrref">Indirect variable
references</link> (see <xref linkend="ex78"/>) provide a clumsy
sort of mechanism for passing variable pointers to
functions.</para>
<example id="indfunc">
<title>Passing an indirect reference to a function</title>
<programlisting>&indfunc;</programlisting>
</example>
<para>The next logical question is whether parameters can be
dereferenced <emphasis>after</emphasis> being passed to a
function.</para>
<example id="dereferencecl">
<title>Dereferencing a parameter passed to a function</title>
<programlisting>&dereferencecl;</programlisting>
</example>
<example id="refparams">
<title>Again, dereferencing a parameter passed to a function</title>
<programlisting>&refparams;</programlisting>
</example>
<variablelist id="exitreturn">
<title><anchor id="exitreturn1"/>Exit and Return</title>
<varlistentry>
<term><command>exit status</command></term>
<listitem>
<para>Functions return a value, called an <firstterm>exit
status</firstterm>. This is analogous to the <link
linkend="exitstatusref">exit status</link> returned by a
command. 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>
<listitem>
<indexterm>
<primary>return</primary>
</indexterm>
<indexterm>
<primary>command</primary>
<secondary>return</secondary>
</indexterm>
<para><anchor id="returnref"/></para>
<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 <firstterm>integer</firstterm>
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 ]] &amp;&amp; REPLY=$(echo $(wc -l &lt; /etc/passwd))
# If /etc/passwd is readable, set REPLY to line count.
# Returns both a parameter value and status information.
# The 'echo' seems unnecessary, but . . .
#+ it removes excess whitespace from the output.
}
if count_lines_in_etc_passwd
then
echo "There are $REPLY lines in /etc/passwd."
else
echo "Cannot count lines in /etc/passwd."
fi
# Thanks, S.C.</programlisting>
</para>
</tip>
<example id="ex61">
<title>Converting numbers to Roman numerals</title>
<programlisting>&ex61;</programlisting>
</example>
<para>See also <xref linkend="isalpha"/>.</para>
<important>
<para>The largest positive integer a function can return is
255. The <command>return</command> command is closely tied
to the concept of <link linkend="exitstatusref">exit
status</link>, which accounts for this particular
limitation. Fortunately, there are various <link
linkend="rvt">workarounds</link> for those situations
requiring a large integer return value from a
function.</para>
<example id="returntest">
<title>Testing large return values in a function</title>
<programlisting>&returntest;</programlisting>
</example>
<para>A workaround for obtaining large integer <quote>return
values</quote> is to simply assign the <quote>return
value</quote> to a global variable.
<programlisting>Return_Val= # Global variable to hold oversize return value of function.
alt_return_test ()
{
fvar=$1
Return_Val=$fvar
return # Returns 0 (success).
}
alt_return_test 1
echo $? # 0
echo "return value = $Return_Val" # 1
alt_return_test 256
echo "return value = $Return_Val" # 256
alt_return_test 257
echo "return value = $Return_Val" # 257
alt_return_test 25701
echo "return value = $Return_Val" #25701</programlisting>
</para>
<para><anchor id="captureretval"/></para>
<para>A more elegant method is to have the function
<command>echo</command> its <quote>return
value to <filename>stdout</filename>,</quote> and
then capture it by <link linkend="commandsubref">command
substitution</link>. See the <link linkend="rvt">discussion
of this</link> in <xref linkend="assortedtips"/>.</para>
<example id="max2">
<title>Comparing two large integers</title>
<programlisting>&max2;</programlisting>
</example>
<para>Here is another example of capturing a function
<quote>return value.</quote> Understanding it requires some
knowledge of <link linkend="awkref">awk</link>.
<programlisting>month_length () # Takes month number as an argument.
{ # Returns number of days in month.
monthD="31 28 31 30 31 30 31 31 30 31 30 31" # Declare as local?
echo "$monthD" | awk '{ print $'"${1}"' }' # Tricky.
# ^^^^^^^^^
# Parameter passed to function ($1 -- month number), then to awk.
# Awk sees this as "print $1 . . . print $12" (depending on month number)
# Template for passing a parameter to embedded awk script:
# $'"${script_parameter}"'
# Here's a slightly simpler awk construct:
# echo $monthD | awk -v month=$1 '{print $(month)}'
# Uses the -v awk option, which assigns a variable value
#+ prior to execution of the awk program block.
# Thank you, Rich.
# Needs error checking for correct parameter range (1-12)
#+ and for February in leap year.
}
# ----------------------------------------------
# Usage example:
month=4 # April, for example (4th month).
days_in=$(month_length $month)
echo $days_in # 30
# ----------------------------------------------</programlisting></para>
<para>See also <xref linkend="daysbetween"/>
and <xref linkend="stddev"/>.</para>
<para><userinput>Exercise:</userinput> Using what we have
just learned, extend the previous <link
linkend="ex61">Roman numerals example</link> to accept
arbitrarily large input.</para>
</important>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="redstdinfunc">
<title><anchor id="redstdinfunc1"/>Redirection</title>
<varlistentry>
<term><replaceable>Redirecting the stdin
of a function</replaceable></term>
<listitem>
<indexterm>
<primary>redirection</primary>
<secondary>stdin</secondary>
</indexterm>
<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 alternate, 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 ()
{
...
} &lt; file
# Try this:
Function ()
{
{
...
} &lt; 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>
<note><para>Emmanuel Rouat's <link
linkend="sample-bashrc">sample <filename>bashrc</filename>
file</link> contains some instructive examples of
functions.</para></note>
</listitem>
</varlistentry>
</variablelist>
</sect1> <!-- Complex Functions and Function Complexities -->
<sect1 id="localvar">
<title>Local Variables</title>
<variablelist id="localref">
<title><anchor id="localref1"/>What makes a variable
<firstterm>local</firstterm>?</title>
<varlistentry>
<term>local variables</term>
<listitem>
<indexterm>
<primary>variable</primary>
<secondary>local</secondary>
</indexterm>
<para>A variable declared as <firstterm>local</firstterm>
is one that is visible only within the <link
linkend="codeblockref">block of code</link> in which it
appears. It has local <link linkend="scoperef">scope</link>.
In a function, a <firstterm>local variable</firstterm> has
meaning only within that function block.
<footnote>
<para>However, as Thomas Braunberger points out, a local
variable declared in a function <emphasis>is also visible
to functions called by the parent
function.</emphasis></para>
<para><programlisting>#!/bin/bash
function1 ()
{
local func1var=20
echo "Within function1, \$func1var = $func1var."
function2
}
function2 ()
{
echo "Within function2, \$func1var = $func1var."
}
function1
exit 0
# Output of the script:
# Within function1, $func1var = 20.
# Within function2, $func1var = 20.</programlisting></para>
<para>This is documented in the Bash manual:</para>
<para><quote>Local can only be used within a function;
it makes the variable name have a visible scope
restricted to that function <emphasis>and its
children</emphasis>.</quote> [emphasis added]
<emphasis>The ABS Guide author considers this behavior
to be a bug.</emphasis></para>
</footnote>
</para>
<example id="ex62">
<title>Local variable visibility</title>
<programlisting>&ex62;</programlisting>
</example>
<caution>
<para>Before a function is called, <emphasis>all</emphasis>
variables declared within the function are invisible outside
the body of the function, not just those explicitly declared
as <firstterm>local</firstterm>.
<programlisting>#!/bin/bash
func ()
{
global_var=37 # Visible only within the function block
#+ before the function has been called.
} # END OF FUNCTION
echo "global_var = $global_var" # global_var =
# Function "func" has not yet been called,
#+ so $global_var is not visible here.
func
echo "global_var = $global_var" # global_var = 37
# Has been set by function call.</programlisting>
</para>
</caution>
<note>
<para><anchor id="exitvalanomaly01"/></para>
<para>As Evgeniy Ivanov points out, when declaring and
setting a local variable in a single command, apparently the
order of operations is to <emphasis>first set the variable,
and only afterwards restrict it to local scope</emphasis>.
This is reflected in the <link
linkend="exitstatusref">return value</link>.</para>
<para><programlisting>#!/bin/bash
echo "==OUTSIDE Function (global)=="
t=$(exit 1)
echo $? # 1
# As expected.
echo
function0 ()
{
echo "==INSIDE Function=="
echo "Global"
t0=$(exit 1)
echo $? # 1
# As expected.
echo
echo "Local declared &amp; assigned in same command."
local t1=$(exit 1)
echo $? # 0
# Unexpected!
# Apparently, the variable assignment takes place before
#+ the local declaration.
#+ The return value is for the latter.
echo
echo "Local declared, then assigned (separate commands)."
local t2
t2=$(exit 1)
echo $? # 1
# As expected.
}
function0</programlisting></para>
</note>
</listitem>
</varlistentry>
</variablelist>
<sect2 id="locvarrecur">
<title>Local variables and recursion.</title>
<para><anchor id="recursionref0"/></para>
<sidebar>
<para><anchor id="recursionref"/></para>
<para><firstterm>Recursion</firstterm> is an interesting
and sometimes useful form of
<firstterm>self-reference</firstterm>. <link
linkend="mayerref">Herbert Mayer</link> defines it
as <quote>. . . expressing an algorithm by using a
simpler version of that same algorithm . . .</quote></para>
<para>Consider a definition defined in terms of itself,
<footnote><para>Otherwise known as
<firstterm>redundancy</firstterm>.</para></footnote>
an expression implicit in its own expression,
<footnote><para>Otherwise known as
<firstterm>tautology</firstterm>.</para></footnote>
<emphasis>a snake swallowing its own
tail</emphasis>,
<footnote><para>Otherwise known as a
<firstterm>metaphor</firstterm>.</para></footnote>
or . . . a function that calls itself.
<footnote><para>Otherwise known as a
<firstterm>recursive function</firstterm>.</para></footnote>
</para>
<para><anchor id="recursiondemo0"/></para>
<example id="recursiondemo">
<title>Demonstration of a simple recursive function</title>
<programlisting>&recursiondemo;</programlisting>
</example>
<para><anchor id="recursiondemo02"/></para>
<example id="recursiondemo2">
<title>Another simple demonstration</title>
<programlisting>&recursiondemo2;</programlisting>
</example>
</sidebar>
<para>Local variables are a useful tool for writing recursive
code, but this practice generally involves a great deal of
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
# Warning: Running this script could possibly lock up your system!
# If you're lucky, it will segfault before using up all available memory.
recursive_function ()
{
echo "$1" # Makes the function do something, and hastens the segfault.
(( $1 &lt; $2 )) &amp;&amp; recursive_function $(( $1 + 1 )) $2;
# As long as 1st parameter is less than 2nd,
#+ increment 1st and recurse.
}
recursive_function 1 50000 # Recurse 50,000 levels!
# Most likely segfaults (depending on stack size, set by ulimit -m).
# Recursion this deep might cause even a C program to segfault,
#+ by using up all the memory allotted to the stack.
echo "This will probably not print."
exit 0 # This script will not exit normally.
# Thanks, St&eacute;phane Chazelas.</programlisting>
</para></footnote>
</para>
<para><anchor id="factorialref"/></para>
<example id="ex63">
<title>Recursion, using a local variable</title>
<programlisting>&ex63;</programlisting>
</example>
<para>Also see <xref linkend="primes"/> for an example of
recursion in a script. Be aware that recursion is
resource-intensive and executes slowly, and is therefore
generally not appropriate in a script.</para>
</sect2>
</sect1> <!-- Local Variables -->
<sect1 id="recurnolocvar">
<title>Recursion Without Local Variables</title>
<para>A function may recursively call itself even without use of
local variables.</para>
<para><anchor id="fiboref"/></para>
<example id="fibo">
<title><firstterm>The Fibonacci Sequence</firstterm></title>
<programlisting>&fibo;</programlisting>
</example>
<para><anchor id="hanoiref"/></para>
<example id="hanoi">
<title><firstterm>The Towers of Hanoi</firstterm></title>
<programlisting>&hanoi;</programlisting>
</example>
</sect1> <!-- Recursion Without Local Variables -->
</chapter> <!-- Functions -->
<chapter id="aliases">
<title>Aliases</title>
<para><anchor id="aliasref"/></para>
<indexterm>
<primary>alias</primary>
</indexterm>
<para>A Bash <firstterm>alias</firstterm> 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="sample-bashrc"><filename>~/.bashrc</filename>
file</link>, then each <userinput>lm</userinput>
<footnote><para> ... as the first word of a command string.
Obviously, an alias is only meaningful at the
<emphasis>beginning</emphasis> of a command.</para></footnote>
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 deleting
important files.</para>
<para>In a script, aliases have very limited usefulness. It would be
nice if aliases could assume some of the functionality of
the <command>C</command> preprocessor, such as macro expansion,
but unfortunately Bash does not expand arguments within the
alias body.
<footnote><para>However, aliases do seem to expand positional
parameters.</para></footnote>
Moreover, a script fails to expand an alias itself
within <quote>compound constructs,</quote> such as <link
linkend="ifthen">if/then</link> statements, loops, and
functions. An added limitation is that an alias will not expand
recursively. Almost invariably, whatever we would like an alias
to do could be accomplished much more effectively with a <link
linkend="functionref">function</link>.</para>
<example id="al">
<title>Aliases within a script</title>
<programlisting>&al;</programlisting>
</example>
<para><anchor id="unaliasref"/></para>
<para>The <command>unalias</command> command removes a previously
set <firstterm>alias</firstterm>.</para>
<example id="unal">
<title><firstterm>unalias</firstterm>: Setting and unsetting
an alias</title>
<programlisting>&unal;</programlisting>
</example>
<screen><prompt>bash$ </prompt><userinput>./unalias.sh</userinput>
<computeroutput>
total 6
drwxrwxr-x 2 bozo bozo 3072 Feb 6 14:04 .
drwxr-xr-x 40 bozo bozo 2048 Feb 6 14:04 ..
-rwxr-xr-x 1 bozo bozo 199 Feb 6 14:04 unalias.sh
./unalias.sh: llm: command not found
</computeroutput></screen>
</chapter> <!-- Aliases -->
<chapter id="list-cons">
<title>List Constructs</title>
<para><anchor id="listconsref"/></para>
<indexterm>
<primary>&amp;&amp;</primary>
</indexterm>
<indexterm>
<primary>special character</primary>
<secondary>&amp;&amp;</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 <firstterm>and list</firstterm> and <firstterm>or
list</firstterm> constructs provide a means of processing a
number of commands consecutively. These can effectively replace
complex nested <link linkend="testconstructs1">if/then</link>
or even <link linkend="caseesac1">case</link> statements.</para>
<variablelist id="lcons">
<title><anchor id="lcons1"/>Chaining together commands</title>
<varlistentry>
<term>and list</term>
<listitem>
<para><programlisting>command-1 &amp;&amp; command-2 &amp;&amp; command-3 &amp;&amp; ... command-n</programlisting>
Each command executes in turn, provided that
the previous command has given a return value of
<replaceable>true</replaceable> (zero). At the first
<replaceable>false</replaceable> (non-zero) return, the
command chain terminates (the first command returning
<replaceable>false</replaceable> is the last one to
execute).</para>
<para>An interesting use of a two-condition <firstterm>and
list</firstterm> from an early version of YongYe's <ulink
url="http://bash.deta.in/Tetris_Game.sh">Tetris
game script</ulink>:</para>
<para>
<programlisting>equation()
{ # core algorithm used for doubling and halving the coordinates
[[ ${cdx} ]] &amp;&amp; ((y=cy+(ccy-cdy)${2}2))
eval ${1}+=\"${x} ${y} \"
}</programlisting>
</para>
<example id="ex64">
<title>Using an <firstterm>and list</firstterm> to test
for command-line arguments</title>
<programlisting>&ex64;</programlisting>
</example>
<example id="andlist2">
<title>Another command-line arg test using an <firstterm>and
list</firstterm></title>
<programlisting>&andlist2;</programlisting>
</example>
<para><anchor id="anddefault"/></para>
<para>
Of course, an <firstterm>and list</firstterm> can also
<firstterm>set</firstterm> variables to a default value.
<programlisting>arg1=$@ &amp;&amp; [ -z "$arg1" ] &amp;&amp; arg1=DEFAULT
# Set $arg1 to command-line arguments, if any.
# But . . . set to DEFAULT if not specified on command-line.</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="orlistref"/>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 <firstterm>or lists</firstterm> in combination
with an <firstterm>and list</firstterm></title>
<programlisting>&ex65;</programlisting>
</example>
<caution><para>If the first command in an <firstterm>or
list</firstterm> returns <returnvalue>true</returnvalue>,
it <replaceable>will</replaceable> execute.</para></caution>
</listitem>
</varlistentry>
</variablelist>
<para><programlisting># ==> The following snippets from the /etc/rc.d/init.d/single
#+==> script by Miquel van Smoorenburg
#+==> illustrate use of "and" and "or" lists.
# ==> "Arrowed" comments added by document author.
[ -x /usr/bin/clear ] &amp;&amp; /usr/bin/clear
# ==> If /usr/bin/clear exists, then invoke it.
# ==> Checking for the existence of a command before calling it
#+==> avoids error messages and other awkward consequences.
# ==> . . .
# If they want to run something in single user mode, might as well run it...
for i in /etc/rc1.d/S[0-9][0-9]* ; do
# Check if the script is there.
[ -x "$i" ] || continue
# ==> If corresponding file in $PWD *not* found,
#+==> then "continue" by jumping to the top of the loop.
# Reject backup files and files generated by rpm.
case "$1" in
*.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
continue;;
esac
[ "$i" = "/etc/rc1.d/S00single" ] &amp;&amp; continue
# ==> Set script name, but don't execute it yet.
$i start
done
# ==> . . .</programlisting></para>
<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 <firstterm>and</firstterm> and
<firstterm>or</firstterm> lists are possible, but the logic may
easily become convoluted and require close attention to <link
linkend="opprecedence1">operator precedence rules</link>, and
possibly extensive debugging.</para>
<para><programlisting>false &amp;&amp; true || echo false # false
# Same result as
( false &amp;&amp; true ) || echo false # false
# But NOT
false &amp;&amp; ( true || echo false ) # (nothing echoed)
# Note left-to-right grouping and evaluation of statements.
# It's usually best to avoid such complexities.
# Thanks, S.C.</programlisting>
</para>
<para>See <xref linkend="daysbetween"/> and <xref
linkend="brokenlink"/> for illustrations of using <userinput>and
/ or list</userinput> constructs to test variables.</para>
</chapter> <!-- List Constructs -->
<chapter id="arrays">
<title>Arrays</title>
<para><anchor id="arrayref"/></para>
<para>Newer versions of Bash support one-dimensional arrays.
<anchor id="brackarray"/>
Array elements may be initialized with the
<userinput>variable[xx]</userinput> notation. Alternatively,
a script may introduce the entire array by an explicit
<userinput>declare -a variable</userinput> statement. To
dereference (retrieve the contents of) an array element, use
<firstterm>curly bracket</firstterm> notation, that is,
<userinput>${element[xx]}</userinput>.</para>
<para><anchor id="arraynotation"/></para>
<example id="ex66">
<title>Simple array usage</title>
<programlisting>&ex66;</programlisting>
</example>
<para><anchor id="arrayinit0"/></para>
<para>As we have seen, a convenient way of initializing an entire array
is the <varname>array=( element1 element2 ... elementN )</varname>
notation.</para>
<para>
<programlisting>base64_charset=( {A..Z} {a..z} {0..9} + / = )
# Using extended brace expansion
#+ to initialize the elements of the array.
# Excerpted from vladz's "base64.sh" script
#+ in the "Contributed Scripts" appendix.</programlisting>
</para>
<para><anchor id="arrayopsvars"/></para>
<sidebar><para>Bash permits array operations on variables, even if
the variables are not explicitly declared as arrays.</para>
<para><programlisting>string=abcABC123ABCabc
echo ${string[@]} # abcABC123ABCabc
echo ${string[*]} # abcABC123ABCabc
echo ${string[0]} # abcABC123ABCabc
echo ${string[1]} # No output!
# Why?
echo ${#string[@]} # 1
# One element in the array.
# The string itself.
# Thank you, Michael Zick, for pointing this out.</programlisting>
Once again this demonstrates that <link linkend="bvuntyped">Bash
variables are untyped</link>.
</para></sidebar>
<example id="poem">
<title>Formatting a poem</title>
<programlisting>&poem;</programlisting>
</example>
<para><anchor id="arraysyntax"/></para>
<para>Array variables have a syntax all their own, and even
standard Bash commands and operators have special options adapted
for array use.</para>
<example id="arrayops">
<title>Various array operations</title>
<programlisting>&arrayops;</programlisting>
</example>
<para><anchor id="arraystringops"/></para>
<para>Many of the standard <link linkend="stringmanip">string
operations</link> work on arrays.</para>
<example id="arraystrops">
<title>String operations on arrays</title>
<programlisting>&arraystrops;</programlisting>
</example>
<para><link linkend="commandsubref">Command substitution</link> can
construct the individual elements of an array.</para>
<example id="scriptarray">
<title>Loading the contents of a script into an array</title>
<programlisting>&scriptarray;</programlisting>
</example>
<para>In an array context, some Bash <link
linkend="builtinref">builtins</link> have a slightly
altered meaning. <anchor id="arrayunset"/>For example, <link
linkend="unsetref">unset</link> deletes array elements, or even
an entire array.</para>
<para><anchor id="arrayspecialprops"/></para>
<example id="ex67">
<title>Some special properties of arrays</title>
<programlisting>&ex67;</programlisting>
</example>
<para><anchor id="arraynumelements"/></para>
<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>
<para><anchor id="emptyarray0"/></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><anchor id="copyarray0"/></para>
<para>
<programlisting># Copying an array.
array2=( "${array1[@]}" )
# or
array2="${array1[@]}"
#
# However, this fails with "sparse" arrays,
#+ arrays with holes (missing elements) in them,
#+ as Jochen DeSmet points out.
# ------------------------------------------
array1[0]=0
# array1[1] not assigned
array1[2]=2
array2=( "${array1[@]}" ) # Copy it?
echo ${array2[0]} # 0
echo ${array2[2]} # (null), should be 2
# ------------------------------------------
# Adding an element to an array.
array=( "${array[@]}" "new element" )
# or
array[${#array[*]}]="new element"
# Thanks, S.C.</programlisting>
</para>
<para><anchor id="arrayinitcs"/></para>
<tip>
<para>The <command>array=( element1 element2 ... elementN )</command>
initialization operation, with the help of <link
linkend="commandsubref">command substitution</link>, makes it
possible to load the contents of a text file into an array.</para>
<para>
<programlisting>#!/bin/bash
filename=sample_file
# cat sample_file
#
# 1 a b c
# 2 d e fg
declare -a array1
array1=( `cat "$filename"`) # Loads contents
# List file to stdout #+ of $filename into array1.
#
# array1=( `cat "$filename" | tr '\n' ' '`)
# change linefeeds in file to spaces.
# Not necessary because Bash does word splitting,
#+ changing linefeeds to spaces.
echo ${array1[@]} # List the array.
# 1 a b c 2 d e fg
#
# Each whitespace-separated "word" in the file
#+ has been assigned to an element of the array.
element_count=${#array1[*]}
echo $element_count # 8</programlisting>
</para>
</tip>
<para>Clever scripting makes it possible to add array operations.</para>
<para><anchor id="arrayassign0"/></para>
<example id="arrayassign">
<title>Initializing arrays</title>
<programlisting>&arrayassign;</programlisting>
</example>
<note><para>Adding a superfluous <command>declare -a</command>
statement to an array declaration may speed up execution of
subsequent operations on the array.</para></note>
<para><anchor id="arrayappend0"/></para>
<example id="copyarray">
<title>Copying and concatenating arrays</title>
<programlisting>&copyarray;</programlisting>
</example>
<example id="arrayappend">
<title>More on concatenating arrays</title>
<programlisting>&arrayappend;</programlisting>
</example>
<para>--</para>
<para>Arrays permit deploying old familiar algorithms as shell scripts.
Whether this is necessarily a good idea is left for the reader to
decide.</para>
<para><anchor id="bubblesort"/></para>
<example id="bubble">
<title>The Bubble Sort</title>
<programlisting>&bubble;</programlisting>
</example>
<para>--</para>
<para><anchor id="arraynest"/></para>
<para>Is it possible to nest arrays within arrays?</para>
<para><programlisting>#!/bin/bash
# "Nested" array.
# Michael Zick provided this example,
#+ with corrections and clarifications by William Park.
AnArray=( $(ls --inode --ignore-backups --almost-all \
--directory --full-time --color=none --time=status \
--sort=time -l ${PWD} ) ) # Commands and options.
# Spaces are significant . . . and don't quote anything in the above.
SubArray=( ${AnArray[@]:11:1} ${AnArray[@]:6:5} )
# This array has six elements:
#+ SubArray=( [0]=${AnArray[11]} [1]=${AnArray[6]} [2]=${AnArray[7]}
# [3]=${AnArray[8]} [4]=${AnArray[9]} [5]=${AnArray[10]} )
#
# Arrays in Bash are (circularly) linked lists
#+ of type string (char *).
# So, this isn't actually a nested array,
#+ but it's functionally similar.
echo "Current directory and date of last status change:"
echo "${SubArray[@]}"
exit 0</programlisting></para>
<para>--</para>
<para>Embedded arrays in combination with <link
linkend="varrefnew">indirect references</link> create some fascinating
possibilities</para>
<para><anchor id="arrayindir"/></para>
<example id="embarr">
<title>Embedded arrays and indirect references</title>
<programlisting>&embarr;</programlisting>
</example>
<para>--</para>
<para><anchor id="primes0"/></para>
<para>Arrays enable implementing a shell script version of the
<firstterm>Sieve of Eratosthenes</firstterm>. 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>The Sieve of Eratosthenes</title>
<programlisting>&ex68;</programlisting>
</example>
<example id="ex68a">
<title>The Sieve of Eratosthenes, Optimized</title>
<programlisting>&ex68a;</programlisting>
</example>
<para>Compare these array-based prime number generators with
alternatives that do not use arrays, <xref linkend="primes"/>,
and <xref linkend="primes2"/>.</para>
<para>--</para>
<para>Arrays lend themselves, to some extent, to emulating data
structures for which Bash has no native support.</para>
<para><anchor id="stackex0"/></para>
<example id="stackex">
<title>Emulating a push-down stack</title>
<programlisting>&stackex;</programlisting>
</example>
<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><anchor id="arraymultidim"/></para>
<para>Bash supports only one-dimensional arrays, though 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>
<para>A two-dimensional array is essentially equivalent to a
one-dimensional one, but with additional addressing modes
for referencing and manipulating the individual elements by
<firstterm>row</firstterm> and <firstterm>column</firstterm>
position.</para>
<para>For an even more elaborate example of simulating a
two-dimensional array, see <xref linkend="lifeslow"/>.</para>
<para>--</para>
<para>For more interesting scripts using arrays, see:
<itemizedlist>
<listitem><para><xref linkend="agram2"/></para></listitem>
<listitem><para><xref linkend="primes2"/></para></listitem>
<listitem><para><xref linkend="hashex2"/></para></listitem>
<listitem><para><xref linkend="homework"/></para></listitem>
<listitem><para><xref linkend="qky"/></para></listitem>
<listitem><para><xref linkend="nim"/></para></listitem>
</itemizedlist>
</para>
</chapter> <!-- Arrays -->
<chapter id="ivr">
<title>Indirect References</title>
<para><anchor id="ivrref"/></para>
<para>We have seen that <link linkend="varsubn">referencing
a variable</link>, <varname>$var</varname>, fetches its
<firstterm>value</firstterm>. <anchor id="evalindref"/>But,
what about the <emphasis>value of a value</emphasis>? What
about <varname>$$var</varname>?</para>
<para>The actual notation is
<replaceable>\$$var</replaceable>, usually preceded by
an <link linkend="evalref">eval</link> (and sometimes an
<link linkend="echoref">echo</link>). This is called an
<firstterm>indirect reference</firstterm>.</para>
<example id="indref">
<title>Indirect Variable References</title>
<programlisting>&indref;</programlisting>
</example>
<para><anchor id="irrref"/></para>
<sidebar><para>Indirect referencing in Bash
is a multi-step process. First, take the name of a variable:
<varname>varname</varname>. Then, reference it:
<varname>$varname</varname>. Then, reference the reference:
<varname>$$varname</varname>. Then, <firstterm>escape</firstterm>
the first <token>$</token>: <varname>\$$varname</varname>.
Finally, force a reevaluation of the expression and assign it:
<command>eval newvar=\$$varname</command>.</para></sidebar>
<para>Of what practical use is indirect referencing of
variables? It gives Bash a little of the functionality
of <link linkend="pointerref">pointers</link>
in <firstterm>C</firstterm>, for instance, in <link
linkend="resistor">table lookup</link>. And, it also has some
other very interesting applications. . . .</para>
<para>Nils Radtke shows how to build <quote>dynamic</quote>
variable names and evaluate their contents. This can be useful
when <link linkend="sourceref">sourcing</link> configuration
files.</para>
<para><programlisting>#!/bin/bash
# ---------------------------------------------
# This could be "sourced" from a separate file.
isdnMyProviderRemoteNet=172.16.0.100
isdnYourProviderRemoteNet=10.0.0.10
isdnOnlineService="MyProvider"
# ---------------------------------------------
remoteNet=$(eval "echo \$$(echo isdn${isdnOnlineService}RemoteNet)")
remoteNet=$(eval "echo \$$(echo isdnMyProviderRemoteNet)")
remoteNet=$(eval "echo \$isdnMyProviderRemoteNet")
remoteNet=$(eval "echo $isdnMyProviderRemoteNet")
echo "$remoteNet" # 172.16.0.100
# ================================================================
# And, it gets even better.
# Consider the following snippet given a variable named getSparc,
#+ but no such variable getIa64:
chkMirrorArchs () {
arch="$1";
if [ "$(eval "echo \${$(echo get$(echo -ne $arch |
sed 's/^\(.\).*/\1/g' | tr 'a-z' 'A-Z'; echo $arch |
sed 's/^.\(.*\)/\1/g')):-false}")" = true ]
then
return 0;
else
return 1;
fi;
}
getSparc="true"
unset getIa64
chkMirrorArchs sparc
echo $? # 0
# True
chkMirrorArchs Ia64
echo $? # 1
# False
# Notes:
# -----
# Even the to-be-substituted variable name part is built explicitly.
# The parameters to the chkMirrorArchs calls are all lower case.
# The variable name is composed of two parts: "get" and "Sparc" . . .</programlisting>
</para>
<example id="coltotaler2">
<title>Passing an indirect reference to <firstterm>awk</firstterm></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 first
order variable must be properly dereferenced (as in the above
example). <anchor id="ivr2"/>Fortunately, the
<replaceable>${!variable}</replaceable> notation introduced
with <link linkend="bash2ref">version 2</link> of Bash
(see <xref linkend="ex78"/> and <xref linkend="hashex2"/>) makes
indirect referencing more intuitive.</para></caution>
<sidebar>
<para>Bash does not support pointer arithmetic, and this severely
limits the usefulness of indirect referencing. In fact, indirect
referencing in a scripting language is, at best, something of
an afterthought.</para>
</sidebar>
</chapter> <!-- Indirect References to Variables -->
<chapter id="devproc">
<title><filename class="directory">/dev</filename> and <filename
class="directory">/proc</filename></title>
<para><anchor id="devprocref"/></para>
<para>A Linux or UNIX filesystem typically has the
<filename class="directory">/dev</filename> and
<filename class="directory">/proc</filename> special-purpose
directories.</para>
<sect1 id="devref1">
<title><filename class="directory">/dev</filename></title>
<para><anchor id="devfileref"/>The <filename
class="directory">/dev</filename> directory contains entries for
the <firstterm>physical devices</firstterm> 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>
Appropriately enough, these are called <firstterm>device
files</firstterm>.
As an example, the hard drive partitions containing
the mounted filesystem(s) have entries in <filename
class="directory">/dev</filename>, as <link
linkend="dfref">df</link> shows.</para>
<para><screen><prompt>bash$ </prompt><userinput>df</userinput>
<computeroutput>Filesystem 1k-blocks Used Available Use%
Mounted on
/dev/hda6 495876 222748 247527 48% /
/dev/hda1 50755 3887 44248 9% /boot
/dev/hda8 367013 13262 334803 4% /home
/dev/hda5 1714416 1123624 503704 70% /usr</computeroutput>
</screen>
</para>
<para><anchor id="loopbackref"/>Among other things, the <filename
class="directory">/dev</filename> directory
contains <firstterm>loopback</firstterm> devices, such as
<filename>/dev/loop0</filename>. A loopback device is a gimmick
that allows an ordinary file to be accessed as if it were a
block device.
<footnote><para><anchor id="blockdevref"/>A <firstterm>block
device</firstterm> reads and/or writes data in chunks,
or <firstterm>blocks</firstterm>, in contrast to a <anchor
id="chardevref"/><firstterm>character device</firstterm>,
which acesses data in <firstterm>character</firstterm>
units. Examples of block devices are hard drives, CDROM
drives, and flash drives. Examples of character devices are
keyboards, modems, sound cards.</para></footnote>
This permits mounting an entire filesystem within a
single large file. See <xref linkend="createfs"/> and <xref
linkend="isomountref"/>.</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/null</filename></link>, <link
linkend="zerosref1"><filename>/dev/zero</filename></link>, <link
linkend="urandomref"><filename>/dev/urandom</filename></link>,
<filename>/dev/sda1</filename> (hard drive partition),
<filename>/dev/udp</filename> (<firstterm>User
Datagram Packet</firstterm> port), and <link
linkend="devtcp"><filename>/dev/tcp</filename></link>.</para>
<para>For instance:</para>
<para>To manually <link linkend="mountref">mount</link>
a USB flash drive, append the following line to
<link linkend="fstabref"><filename>/etc/fstab</filename></link>.
<footnote>
<para>Of course, the mount point
<filename>/mnt/flashdrive</filename> must exist. If not,
then, as <firstterm>root</firstterm>, <command>mkdir
/mnt/flashdrive</command>.</para>
<para>To actually mount the drive, use the following command:
<command>mount /mnt/flashdrive</command></para>
<para>Newer Linux distros automount flash drives in the
<filename class="directory">/media</filename>
directory without user intervention.</para>
</footnote>
<programlisting>/dev/sda1 /mnt/flashdrive auto noauto,user,noatime 0 0</programlisting>
(See also <xref linkend="usbinst"/>.)</para>
<para>Checking whether a disk is in the CD-burner
(soft-linked to <filename>/dev/hdc</filename>):
<programlisting>head -1 /dev/hdc
# head: cannot open '/dev/hdc' for reading: No medium found
# (No disc in the drive.)
# head: error reading '/dev/hdc': Input/output error
# (There is a disk in the drive, but it can't be read;
#+ possibly it's an unrecorded CDR blank.)
# Stream of characters and assorted gibberish
# (There is a pre-recorded disk in the drive,
#+ and this is raw output -- a stream of ASCII and binary data.)
# Here we see the wisdom of using 'head' to limit the output
#+ to manageable proportions, rather than 'cat' or something similar.
# Now, it's just a matter of checking/parsing the output and taking
#+ appropriate action.</programlisting></para>
<para><anchor id="socketref"/></para>
<para>When executing a command on a
<filename>/dev/tcp/$host/$port</filename> pseudo-device file, Bash
opens a TCP connection to the associated
<firstterm>socket</firstterm>.</para>
<sidebar><para>A <firstterm>socket</firstterm> is a
communications node associated with a specific I/O
port. (This is analogous to a <firstterm>hardware
socket</firstterm>, or <firstterm>receptacle</firstterm>,
for a connecting cable.) It permits data transfer between
hardware devices on the same machine, between machines
on the same network, between machines across different
networks, and, of course, between machines at different
locations on the Internet.</para></sidebar>
<para><anchor id="npref"/>The following examples assume an active Internet
connection.</para>
<para>Getting the time from <filename>nist.gov</filename>:</para>
<screen><prompt>bash$ </prompt><userinput>cat &lt;/dev/tcp/time.nist.gov/13</userinput>
<computeroutput>53082 04-03-18 04:26:54 68 0 0 502.3 UTC(NIST) *</computeroutput>
</screen>
<para>[Mark contributed this example.]</para>
<para>Generalizing the above into a script:</para>
<para><programlisting>#!/bin/bash
# This script must run with root permissions.
URL="time.nist.gov/13"
Time=$(cat &lt;/dev/tcp/"$URL")
UTC=$(echo "$Time" | awk '{print$3}') # Third field is UTC (GMT) time.
# Exercise: modify this for different time zones.
echo "UTC Time = "$UTC""</programlisting></para>
<para><anchor id="nw001"/>Downloading a URL:</para>
<screen><prompt>bash$ </prompt><userinput>exec 5&lt;&gt;/dev/tcp/www.net.cn/80</userinput>
<prompt>bash$ </prompt><userinput>echo -e "GET / HTTP/1.0\n" >&amp;5</userinput>
<prompt>bash$ </prompt><userinput>cat &lt;&amp;5</userinput>
</screen>
<para>[Thanks, Mark and Mihai Maties.]</para>
<example id="devtcp">
<title>Using <filename>/dev/tcp</filename> for
troubleshooting</title>
<programlisting>&devtcp;</programlisting>
</example>
<example id="musicscr">
<title>Playing music</title>
<programlisting>&musicscr;</programlisting>
</example>
</sect1> <!-- /dev -->
<sect1 id="procref1">
<title><filename class="directory">/proc</filename></title>
<para><anchor id="procref2"/></para>
<para>The <filename class="directory">/proc</filename> directory
is actually a pseudo-filesystem. The files in <filename
class="directory">/proc</filename> mirror currently running
system and kernel <link linkend="processref">processes</link>
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 /proc/interrupts</userinput>
<computeroutput> CPU0
0: 84505 XT-PIC timer
1: 3375 XT-PIC keyboard
2: 0 XT-PIC cascade
5: 1 XT-PIC soundblaster
8: 1 XT-PIC rtc
12: 4231 XT-PIC PS/2 Mouse
14: 109373 XT-PIC ide0
NMI: 0
ERR: 0</computeroutput>
<prompt>bash$ </prompt><userinput>cat /proc/partitions</userinput>
<computeroutput>major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
3 0 3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030
3 1 52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
3 2 1 hda2 0 0 0 0 0 0 0 0 0 0 0
3 4 165280 hda4 10 0 20 210 0 0 0 0 0 210 210
...</computeroutput>
<prompt>bash$ </prompt><userinput>cat /proc/loadavg</userinput>
<computeroutput>0.13 0.42 0.27 2/44 1119</computeroutput>
<prompt>bash$ </prompt><userinput>cat /proc/apm</userinput>
<computeroutput>1.16 1.2 0x03 0x01 0xff 0x80 -1% -1 ?</computeroutput>
<prompt>bash$ </prompt><userinput>cat /proc/acpi/battery/BAT0/info</userinput>
<computeroutput>present: yes
design capacity: 43200 mWh
last full capacity: 36640 mWh
battery technology: rechargeable
design voltage: 10800 mV
design capacity warning: 1832 mWh
design capacity low: 200 mWh
capacity granularity 1: 1 mWh
capacity granularity 2: 1 mWh
model number: IBM-02K6897
serial number: 1133
battery type: LION
OEM info: Panasonic</computeroutput>
<prompt>bash$ </prompt><userinput>fgrep Mem /proc/meminfo</userinput>
<computeroutput>MemTotal: 515216 kB
MemFree: 266248 kB</computeroutput>
</screen>
</para>
<para>Shell scripts may extract data from certain of the files in
<filename class="directory">/proc</filename>.
<footnote><para>Certain system commands, such as
<link linkend="procinforef">procinfo</link>,
<link linkend="freeref">free</link>,
<link linkend="vmstatref">vmstat</link>,
<link linkend="lsdevref">lsdev</link>,
and <link linkend="uptimeref">uptime</link>
do this as well.</para></footnote></para>
<para><programlisting>FS=iso # ISO filesystem support in kernel?
grep $FS /proc/filesystems # iso9660</programlisting></para>
<para><programlisting>kernel_version=$( awk '{ print $3 }' /proc/version )</programlisting></para>
<para><programlisting>CPU=$( awk '/model name/ {print $5}' &lt; /proc/cpuinfo )
if [ "$CPU" = "Pentium(R)" ]
then
run_some_commands
...
else
run_other_commands
...
fi
cpu_speed=$( fgrep "cpu MHz" /proc/cpuinfo | awk '{print $4}' )
# Current operating speed (in MHz) of the cpu on your machine.
# On a laptop this may vary, depending on use of battery
#+ or AC power.</programlisting></para>
<para><programlisting>#!/bin/bash
# get-commandline.sh
# Get the command-line parameters of a process.
OPTION=cmdline
# Identify PID.
pid=$( echo $(pidof "$1") | awk '{ print $1 }' )
# Get only first ^^^^^^^^^^^^^^^^^^ of multiple instances.
echo
echo "Process ID of (first instance of) "$1" = $pid"
echo -n "Command-line arguments: "
cat /proc/"$pid"/"$OPTION" | xargs -0 echo
# Formats output: ^^^^^^^^^^^^^^^
# (Thanks, Han Holl, for the fixup!)
echo; echo
# For example:
# sh get-commandline.sh xterm</programlisting></para>
<para>+</para>
<para><programlisting>devfile="/proc/bus/usb/devices"
text="Spd"
USB1="Spd=12"
USB2="Spd=480"
bus_speed=$(fgrep -m 1 "$text" $devfile | awk '{print $9}')
# ^^^^ Stop after first match.
if [ "$bus_speed" = "$USB1" ]
then
echo "USB 1.1 port found."
# Do something appropriate for USB 1.1.
fi</programlisting></para>
<note>
<para>It is even possible to control certain peripherals with commands
sent to the <filename class="directory">/proc</filename> directory.
<screen>
<prompt>root# </prompt><command>echo on > /proc/acpi/ibm/light</command>
</screen>
This turns on the <emphasis>Thinklight</emphasis> in certain models
of IBM/Lenovo Thinkpads. (May not work on all Linux distros.)</para>
<para>Of course, caution is advised when writing to <filename
class="directory">/proc</filename>.</para>
</note>
<para><anchor id="procrunning"/></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>
<para><anchor id="procwarning"/></para>
<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> <!-- /proc -->
</chapter> <!-- /dev and /proc -->
<chapter id="networkprogramming">
<title>Network Programming</title>
<epigraph>
<para>The Net's a cross between an elephant and a white
elephant sale: it never forgets, and it's always crap.</para>
<para>--Nemo</para>
</epigraph>
<para>A Linux system has quite a number of tools for accessing,
manipulating, and troubleshooting network connections. We can
incorporate some of these tools into scripts -- scripts that
expand our knowledge of networking, useful scripts that can
facilitate the administration of a network.</para>
<para><anchor id="cgiscript"/>Here is a simple CGI
script that demonstrates connecting to a remote server.</para>
<example id="testcgi">
<title>Print the server environment</title>
<programlisting>&testcgi;</programlisting>
</example>
<para>For security purposes, it may be helpful to identify the IP
addresses a computer is accessing.</para>
<para><anchor id="iptables02"/></para>
<example id="ipaddresses">
<title>IP addresses</title>
<programlisting>&ipaddresses;</programlisting>
</example>
<para>More examples of network programming:
<orderedlist>
<listitem><para><link linkend="npref">Getting the time from
<firstterm>nist.gov</firstterm></link></para></listitem>
<listitem><para><link linkend="nw001">Downloading a
URL</link></para></listitem>
<listitem><para><link linkend="ipscript0">A GRE
tunnel</link></para></listitem>
<listitem><para><link linkend="ping0">Checking
if an Internet server is up</link></para></listitem>
<listitem><para><xref linkend="isspammer"/></para></listitem>
<listitem><para><xref linkend="isspammer2"/></para></listitem>
<listitem><para><xref linkend="whx"/></para></listitem>
<listitem><para><xref linkend="devtcp"/></para></listitem>
</orderedlist>
</para>
<para>See also the <link linkend="networksys1">networking commands</link>
in the <link linkend="system">System and
Administrative Commands</link> chapter and the <link
linkend="communications">communications commands</link> in
the <link linkend="external">External Filters, Programs and
Commands</link> chapter.</para>
</chapter> <!-- Network Programing -->
<chapter id="zeros">
<title>Of Zeros and Nulls</title>
<epigraph>
<para>Faultily faultless, icily regular, splendidly null</para>
<para>Dead perfection; no more.</para>
<para>--Alfred Lord Tennyson</para>
</epigraph>
<para><anchor id="zerosref"/></para>
<variablelist id="zeronull">
<title><anchor id="zeronull1"/><filename>/dev/zero</filename>
... <filename>/dev/null</filename></title>
<varlistentry>
<term><anchor id="devnullref"/>Uses of
<filename>/dev/null</filename></term>
<listitem>
<para>Think of <filename>/dev/null</filename> as a <firstterm>black
hole</firstterm>. It is essentially the equivalent of
a write-only file. Everything written to it disappears.
Attempts to read or output from it result in nothing. All
the same, <filename>/dev/null</filename> can be quite
useful from both the command-line and in scripts.</para>
<para>Suppressing <filename>stdout</filename>.
<programlisting>cat $filename >/dev/null
# Contents of the file will not list to stdout.</programlisting>
</para>
<para>Suppressing <filename>stderr</filename>
(from <xref linkend="ex57"/>).
<programlisting>rm $badname 2>/dev/null
# So error messages [stderr] deep-sixed.</programlisting>
</para>
<para>Suppressing output from <emphasis>both</emphasis>
<filename>stdout</filename> and <filename>stderr</filename>.
<programlisting>cat $filename 2>/dev/null >/dev/null
# If "$filename" does not exist, there will be no error message output.
# If "$filename" does exist, the contents of the file will not list to stdout.
# Therefore, no output at all will result from the above line of code.
#
# This can be useful in situations where the return code from a command
#+ needs to be tested, but no output is desired.
#
# cat $filename &amp;>/dev/null
# also works, as Baris Cicek points out.</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 logfile
(especially good for dealing with those nasty
<quote>cookies</quote> sent by commercial Web sites):</para>
<example id="cookies">
<title>Hiding the cookie jar</title>
<programlisting># Obsolete Netscape browser.
# Same principle applies to newer browsers.
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><anchor id="zerosref1"/>Uses of <filename>/dev/zero</filename></term>
<listitem>
<para>Like <filename>/dev/null</filename>,
<filename>/dev/zero</filename> is a pseudo-device file, but
it actually produces a stream of nulls
(<emphasis>binary</emphasis> zeros, not the <link
linkend="asciidef">ASCII</link> kind). Output written
to <filename>/dev/zero</filename> disappears, and it is
fairly difficult to actually read the nulls emitted there,
though it can be done with <link linkend="odref">od</link>
or a hex editor. <anchor id="swapfileref"/>The chief use of
<filename>/dev/zero</filename> is creating an initialized
dummy file of predetermined length intended as a temporary
swap file.</para>
<example id="ex73">
<title>Setting up a swapfile using <filename>/dev/zero</filename></title>
<programlisting>&ex73;</programlisting>
</example>
<para>Another application of <filename>/dev/zero</filename>
is to <quote>zero out</quote> a file of a designated
size for a special purpose, such as mounting a filesystem
on a <link linkend="loopbackref">loopback device</link>
(see <xref linkend="createfs"/>) or <quote>securely</quote>
deleting a file (see <xref linkend="blotout"/>).</para>
<example id="ramdisk">
<title>Creating a ramdisk</title>
<programlisting>&ramdisk;</programlisting>
</example>
<para>In addition to all the above,
<filename>/dev/zero</filename> is needed by ELF
(<firstterm>Executable and Linking Format</firstterm>)
UNIX/Linux binaries.</para>
</listitem>
</varlistentry>
</variablelist>
</chapter> <!-- Zeros and Nulls -->
<chapter id="debugging">
<title>Debugging</title>
<epigraph>
<para>Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible,
you are, by definition, not smart enough to debug it.</para>
<para>--Brian Kernighan</para>
</epigraph>
<para>The Bash shell contains no built-in debugger, and only bare-bones
debugging-specific commands and 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>A buggy script</title>
<programlisting>&ex74;</programlisting>
</example>
<para>Output from script:
<screen><computeroutput>./ex74.sh: [37: command not found</computeroutput></screen>
What's wrong with the above script? Hint: after the
<firstterm>if</firstterm>.</para>
<example id="missingkeyword">
<title>Missing <link linkend="keywordref">keyword</link></title>
<programlisting>&missingkeyword;</programlisting>
</example>
<para>Output from script:
<screen>
<computeroutput>missing-keyword.sh: line 10: syntax error: unexpected end of file</computeroutput>
</screen>
Note that the error message does <emphasis>not</emphasis> necessarily
reference the line in which the error occurs, but the line where the
Bash interpreter finally becomes aware of the error.
</para>
<para>Error messages may disregard comment lines in a script when
reporting the line number of a syntax error.</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><firstterm>test24</firstterm>: 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 a <quote><errorname>syntax error</errorname></quote> message, 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><anchor id="debugtools"/></para>
<para>Tools for debugging non-working scripts include
<orderedlist>
<listitem>
<para>Inserting <link linkend="echoref">echo</link>
statements at critical points in the script to trace the
variables, and otherwise give a snapshot of what is going
on.</para>
<tip>
<para>Even better is an <command>echo</command> that echoes
only when <firstterm>debug</firstterm> is on.</para>
<para><programlisting>### debecho (debug-echo), by Stefano Falsetto ###
### Will echo passed parameters only if DEBUG is set to a value. ###
debecho () {
if [ ! -z "$DEBUG" ]; then
echo "$1" >&amp;2
# ^^^ to stderr
fi
}
DEBUG=on
Whatever=whatnot
debecho $Whatever # whatnot
DEBUG=
Whatever=notwhat
debecho $Whatever # (Will not echo.)</programlisting></para>
</tip>
</listitem>
<listitem>
<para>Using the <link linkend="teeref">tee</link> filter
to check processes or data flows at critical points.</para>
</listitem>
<listitem>
<para>Setting option flags <option>-n -v -x</option></para>
<para><userinput>sh -n scriptname</userinput> checks for
syntax errors without actually running the script. This is
the equivalent of inserting <userinput>set -n</userinput> or
<userinput>set -o noexec</userinput> into the script. Note
that certain types of syntax errors can slip past this
check.</para>
<para><userinput>sh -v scriptname</userinput> echoes each
command before executing it. This is the equivalent of
inserting <userinput>set -v</userinput> or <userinput>set
-o verbose</userinput> in the script.</para>
<para>The <option>-n</option> and <option>-v</option>
flags work well together. <userinput>sh -nv
scriptname</userinput> gives a verbose syntax check.</para>
<para><userinput>sh -x scriptname</userinput> echoes the result each
command, but in an abbreviated manner. This is the equivalent of
inserting <userinput>set -x</userinput> or
<userinput>set -o xtrace</userinput> in the script.</para>
<para><anchor id="undvarerr"/></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
and aborts the script.
<programlisting>set -u # Or set -o nounset
# Setting a variable to null will not trigger the error/abort.
# unset_var=
echo $unset_var # Unset (and undeclared) variable.
echo "Should not echo!"
# sh t2.sh
# t2.sh: line 6: unset_var: unbound variable</programlisting></para>
</listitem>
<listitem>
<para>Using an <quote>assert</quote> function to test a
variable or condition at critical points in a script. (This is
an idea borrowed from C.)</para>
<example id="assert">
<title>Testing a condition with an
<firstterm>assert</firstterm></title>
<programlisting>&assert;</programlisting>
</example>
</listitem>
<listitem>
<para>Using the <link linkend="linenoref">$LINENO</link>
variable and the <link linkend="callerref">caller</link>
builtin.</para>
</listitem>
<listitem>
<para><anchor id="debugtrap"/>Trapping at exit.</para>
<para>The <link linkend="exitref">exit</link> 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
<firstterm>exit</firstterm>, forcing a <quote>printout</quote>
of variables, for example. The <firstterm>trap</firstterm>
must be the first command in the script.</para>
</listitem>
</orderedlist>
</para>
<variablelist id="trapref">
<title><anchor id="trapref1"/>Trapping signals</title>
<varlistentry>
<term><command>trap</command></term>
<listitem>
<para>Specifies an action on receipt of a
signal; also useful for debugging.</para>
<para><anchor id="signald"/></para>
<sidebar>
<para>A <firstterm>signal</firstterm> is 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
<link linkend="ctlcref">Control-C</link>
sends a user interrupt, an INT signal, to a running
program.</para></sidebar>
<para><emphasis>A simple instance:</emphasis>
<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 <keycap>Control-C</keycap></title>
<programlisting>&online;</programlisting>
</example>
<example id="progressbar2">
<title>A Simple Implementation of a Progress Bar</title>
<programlisting>&progressbar2;</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>
<para>Of course, the <command>trap</command> command has other uses
aside from debugging, such as disabling certain keystrokes within a
script (see <xref linkend="stopwatch"/>).</para>
<example id="multipleproc">
<title>Running multiple processes (on an SMP box)</title>
<programlisting>&multipleproc;</programlisting>
</example>
<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>
<sidebar>
<para><link linkend="bash3ref">Version 3</link> of Bash adds the
following <link linkend="internalvariables1">internal
variables</link> for use by the debugger.
<orderedlist>
<listitem>
<para><varname>$BASH_ARGC</varname></para>
<para>Number of command-line arguments passed to script,
similar to <link
linkend="clacountref"><varname>$#</varname></link>.</para>
</listitem>
<listitem>
<para><varname>$BASH_ARGV</varname></para>
<para>Final command-line parameter passed to script, equivalent
<link
linkend="lastargref"><varname>${!#}</varname></link>.</para>
</listitem>
<listitem>
<para><varname>$BASH_COMMAND</varname></para>
<para>Command currently executing.</para>
</listitem>
<listitem>
<para><varname>$BASH_EXECUTION_STRING</varname></para>
<para>The <firstterm>option string</firstterm> following the
<option>-c</option> <link linkend="clopts">option</link>
to Bash.</para>
</listitem>
<listitem>
<para><varname>$BASH_LINENO</varname></para>
<para>In a <link linkend="functionref">function</link>,
indicates the line number of the function call.</para>
</listitem>
<listitem>
<para><varname>$BASH_REMATCH</varname></para>
<para>Array variable associated with <command>=~</command>
<link linkend="regexmatchref">conditional regex
matching</link>.</para>
</listitem>
<listitem>
<para><anchor id="bashsourceref"/></para>
<para><varname>$BASH_SOURCE</varname></para>
<para>This is the name of the script, usually the same as
<link linkend="arg0">$0</link>.</para>
</listitem>
<listitem>
<para>
<link
linkend="bashsubshellref"><varname>$BASH_SUBSHELL</varname></link></para>
</listitem>
</orderedlist></para>
</sidebar>
</chapter> <!-- Debugging -->
<chapter id="options">
<title>Options</title>
<para><anchor id="optionsref"/></para>
<para>Options are settings that change shell and/or script
behavior.</para>
<para>The <link linkend="setref">set</link> command
enables options within a script. At the point in the script
where you want the options to take effect, use <command>set
-o option-name</command> or, in short form, <command>set
-option-abbrev</command>. These two forms are equivalent.</para>
<para><programlisting>
#!/bin/bash
set -o verbose
# Echoes all commands before executing.
</programlisting></para>
<para><programlisting>
#!/bin/bash
set -v
# Exact same effect as above.
</programlisting></para>
<note><para>To <firstterm>disable</firstterm> 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><anchor id="invocationoptionsref"/></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 (preceded by a single dash)
or by complete name (preceded by a <emphasis>double</emphasis>
dash or by <option>-o</option>).</para>
<para><anchor id="optionstable"/></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><option>-B</option></entry>
<entry>brace expansion</entry>
<entry><emphasis>Enable</emphasis>
<link linkend="braceexpref">brace
expansion</link> (default setting =
<emphasis>on</emphasis>)</entry>
</row>
<row>
<entry><option>+B</option></entry>
<entry>brace expansion</entry>
<entry><emphasis>Disable</emphasis>
brace expansion</entry>
</row>
<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>checkjobs</option></entry>
<entry></entry>
<entry>Informs user of any open <link
linkend="jobsref">jobs</link> upon shell exit.
Introduced in <link linkend="bash4ref">version 4</link>
of Bash, and still <quote>experimental.</quote>
<emphasis>Usage:</emphasis> shopt -s checkjobs
(<emphasis>Caution:</emphasis> may hang!)</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 (except in <link
linkend="untilloopref">until</link> or <link
linkend="whileloopref">while loops</link>, <link
linkend="testconstructs1">if-tests</link>, <link
linkend="lcons1">list constructs</link>)</entry>
</row>
<row>
<entry><option>-f</option></entry>
<entry>noglob</entry>
<entry>Filename expansion (globbing) disabled</entry>
</row>
<row>
<entry><option>globstar</option></entry>
<entry><link
linkend="globstarref"><firstterm>globbing</firstterm>
star-match</link></entry>
<entry>Enables the <token>**</token> <link
linkend="globbingref">globbing</link> operator
(<link linkend="bash4ref">version 4+</link> of Bash).
<emphasis>Usage:</emphasis> shopt -s globstar</entry>
</row>
<row>
<entry><option>-i</option></entry>
<entry>interactive</entry>
<entry>Script runs in <firstterm>interactive</firstterm> mode</entry>
</row>
<row>
<entry><option>-n</option></entry>
<entry>noexec</entry>
<entry>Read commands in script, but do not execute them (syntax check)</entry>
</row>
<row>
<entry><option>-o Option-Name</option></entry>
<entry>(none)</entry>
<entry>Invoke the <firstterm>Option-Name</firstterm>
option</entry>
</row>
<row>
<entry><option>-o posix</option></entry>
<entry>POSIX</entry>
<entry>Change the behavior of Bash, or invoked script, to
conform to <link linkend="posix2ref">POSIX</link>
standard.</entry>
</row>
<row>
<entry><option>-o pipefail</option></entry>
<entry>pipe failure</entry>
<entry>Causes a pipeline to return the <link
linkend="exitstatusref">exit status</link> of
the last command in the pipe that returned a non-zero
return value.</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 <firstterm>restricted</firstterm>
mode (see <xref linkend="restricted-sh"/>).</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>-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>-</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 (<parameter>-- arg1 arg2</parameter>),
positional parameters set to arguments.</entry>
</row>
</tbody>
</tgroup>
</table>
</chapter> <!-- Options -->
<chapter id="gotchas">
<title>Gotchas</title>
<epigraph>
<para>Turandot: <foreignphrase>Gli enigmi sono tre, la morte
una!</foreignphrase></para>
<para>Caleph: <foreignphrase>No, no! Gli enigmi sono tre, una la
vita!</foreignphrase></para>
<para>--Puccini</para>
</epigraph>
<para><anchor id="bash3gotcha"/></para>
<para>Here are some (non-recommended!) scripting practices that
will bring excitement into an otherwise dull life.</para>
<itemizedlist>
<listitem>
<para><anchor id="inappvn"/></para>
<para>Assigning reserved words or characters to variable names.</para>
<para>
<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 okay.
# However . . . using just an underscore will not work.
_=25
echo $_ # $_ is a special variable set to last arg of last command.
# But . . . _ is a valid function name!
xyz((!*=value2 # Causes severe problems.
# As of version 3 of Bash, periods are not allowed within variable names.</programlisting>
</para>
</listitem>
<listitem>
<para>Using a hyphen or other reserved characters in a variable name (or
function name).</para>
<para>
<programlisting>var-1=23
# Use 'var_1' instead.
function-whatever () # Error
# Use 'function_whatever ()' instead.
# As of version 3 of Bash, periods are not allowed within function names.
function.whatever () # Error
# Use 'functionWhatever ()' instead.</programlisting>
</para>
</listitem>
<listitem>
<para>Using the same name for a variable and a function. This can make a
script difficult to understand.</para>
<para>
<programlisting>do_something ()
{
echo "This function does something with \"$1\"."
}
do_something=do_something
do_something do_something
# All this is legal, but highly confusing.</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="wsbad"/>Using <link
linkend="whitespaceref">whitespace</link> inappropriately.
In contrast to other programming languages, Bash can be quite
finicky about whitespace.</para>
<para>
<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 # Instead: let c=$a-$b or let "c = $a - $b"
if [ $a -le 5] # if [ $a -le 5 ] is correct.
# ^^ if [ "$a" -le 5 ] is even better.
# [[ $a -le 5 ]] also works.</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="omitsemicolon"/></para>
<para>Not terminating with a <link
linkend="semicolonref">semicolon</link> the final command
in a <link linkend="codeblockref">code block within curly
brackets</link>.</para>
<para>
<programlisting>{ ls -l; df; echo "Done." }
# bash: syntax error: unexpected end of file
{ ls -l; df; echo "Done."; }
# ^ ### Final command needs semicolon.</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="uninitvar"/></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 <firstterm>null</firstterm>,
<emphasis>not</emphasis> zero.</para>
<para><anchor id="bash4.2-uninitialized"/></para>
<para>
<programlisting>#!/bin/bash
echo "uninitialized_var = $uninitialized_var"
# uninitialized_var =
# However . . .
# if $BASH_VERSION &ge; 4.2; then
if [[ ! -v uninitialized_var ]]
then
uninitialized_var=0 # Initialize it to zero!
fi
</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="eqdif"/></para>
<para>Mixing up <firstterm>=</firstterm> and
<firstterm>-eq</firstterm> in a test. Remember,
<firstterm>=</firstterm> is for comparing literal variables
and <firstterm>-eq</firstterm> for integers.</para>
<para>
<programlisting>if [ "$a" = 273 ] # Is $a an integer or string?
if [ "$a" -eq 273 ] # If $a is an integer.
# Sometimes you can interchange -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>
</listitem>
<listitem>
<para><anchor id="numstrcompne"/></para>
<para>Misusing <link linkend="scomparison1">string comparison</link>
operators.</para>
<example id="badop">
<title>Numerical and string comparison are not equivalent</title>
<programlisting>&badop;</programlisting>
</example>
</listitem>
<listitem>
<para><anchor id="letbad"/></para>
<para>Attempting to use <link linkend="letref">let</link>
to set string variables.</para>
<para><programlisting>let "a = hello, you"
echo "$a" # 0</programlisting></para>
</listitem>
<listitem>
<para><anchor id="failquote"/></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>
</listitem>
<listitem>
<para><anchor id="failnotquote"/></para>
<para>Quoting a variable containing whitespace <link
linkend="wsquo">prevents splitting</link>. Sometimes
this produces <link linkend="varsplitting">unintended
consequences</link>.</para>
</listitem>
<listitem>
<para><anchor id="execperm"/></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 <firstterm>root</firstterm>, of course).</para>
</listitem>
<listitem>
<para><anchor id="dashnredr"/></para>
<para>Attempting to use <command>-</command> as a redirection
operator (which it is not) will usually result in an unpleasant
surprise.</para>
<para>
<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>
</listitem>
<listitem>
<para><anchor id="lateverf"/></para>
<para>Using Bash <link linkend="bash2ref">version 2+</link>
functionality may cause a bailout with error messages. Older
Linux machines may have version 1.XX of Bash as the default
installation.</para>
<para>
<programlisting>#!/bin/bash
minimum_version=2
# Since Chet Ramey is constantly adding features to Bash,
# you may set $minimum_version to 2.XX, 3.XX, or whatever is appropriate.
E_BAD_VERSION=80
if [ "$BASH_VERSION" \&lt; "$minimum_version" ]
then
echo "This script works only with Bash, version $minimum or greater."
echo "Upgrade strongly recommended."
exit $E_BAD_VERSION
fi
...</programlisting></para>
</listitem>
<listitem>
<para>Using Bash-specific functionality in a <link
linkend="bashdef">Bourne shell</link> script
(<userinput>#!/bin/sh</userinput>) on a non-Linux machine
<link linkend="binsh">may cause unexpected behavior</link>.
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> </listitem>
<listitem>
<para><anchor id="undocf"/></para>
<para>Using undocumented features in Bash turns out to be a
dangerous practice. In previous releases of this
book there were several scripts that depended on the
<quote>feature</quote> that, although the maximum value
of an <link linkend="exitstatusref">exit</link> or <link
linkend="returnref">return</link> value was 255, that limit
did not apply to <emphasis>negative</emphasis> integers.
Unfortunately, in version 2.05b and later, that loophole
disappeared. See <xref linkend="returntest"/>.</para>
</listitem>
<listitem>
<para><anchor id="gotchaexitvalanamalies"/></para>
<para>In certain contexts, a misleading <link
linkend="exitstatusref">exit status</link>
may be returned. This may occur when <link
linkend="exitvalanomaly01">setting a local variable within a
function</link> or when <link linkend="exitvalanomaly02">assigning
an arithmetic value to a variable</link>.</para>
</listitem>
<listitem>
<para><anchor id="arxs1"/>The <link linkend="arxs">exit
status of an arithmetic expression</link> is
<emphasis>not</emphasis> equivalent to an <firstterm>error
code</firstterm>.</para>
<para><programlisting>var=1 &amp;&amp; ((--var)) &amp;&amp; echo $var
# ^^^^^^^^^ Here the and-list terminates with exit status 1.
# $var doesn't echo!
echo $? # 1</programlisting></para>
</listitem>
<listitem>
<para><anchor id="dosnewlines"/></para>
<para>
A script with DOS-type newlines (<replaceable>\r\n</replaceable>)
will fail to execute, since <userinput>#!/bin/bash\r\n</userinput>
is <emphasis>not</emphasis> 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>
<programlisting>#!/bin/bash
echo "Here"
unix2dos $0 # Script changes itself to DOS format.
chmod 755 $0 # Change back to execute permission.
# The 'unix2dos' command removes execute permission.
./$0 # Script tries to run itself again.
# But it won't work as a DOS file.
echo "There"
exit 0</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="binsh"/></para>
<para>A shell script headed by <userinput>#!/bin/sh</userinput>
will 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>
</listitem>
<listitem>
<para><link linkend="indentedls">Putting whitespace in front of
the terminating limit string</link> of a <link
linkend="heredocref">here document</link> will cause unexpected
behavior in a script.</para>
</listitem>
<listitem>
<para><anchor id="rvtcaution2"/>Putting more than one
<firstterm>echo</firstterm> statement in a function <link
linkend="rvt">whose output is captured</link>.
<programlisting>add2 ()
{
echo "Whatever ... " # Delete this line!
let "retval = $1 + $2"
echo $retval
}
num1=12
num2=43
echo "Sum of $num1 and $num2 = $(add2 $num1 $num2)"
# Sum of 12 and 43 = Whatever ...
# 55
# The "echoes" concatenate.</programlisting>
This <link
linkend="rvtcaution">will not work</link>.</para>
</listitem>
<listitem>
<para><anchor id="parchildprobref"/></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.</para>
<para>
<programlisting>WHATEVER=/home/bozo
export WHATEVER
exit 0</programlisting>
<screen><prompt>bash$ </prompt><command>echo $WHATEVER</command>
<computeroutput>
</computeroutput>
<prompt>bash$ </prompt></screen>
</para>
<para>
Sure enough, back at the command prompt, $WHATEVER remains unset.
</para>
</listitem>
<listitem>
<para><anchor id="varsubsh"/></para>
<para>Setting and manipulating variables in a <link
linkend="subshellsref">subshell</link>, 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>
</listitem>
<listitem>
<para><anchor id="badread0"/></para>
<para><link linkend="piperef">Piping</link>
<command>echo</command> output to a <link
linkend="readref">read</link> may produce unexpected
results. In this scenario, the <command>read</command>
acts as if it were running in a subshell. Instead, use
the <link linkend="setref">set</link> command (as in <xref
linkend="setpos"/>).</para>
<example id="badread">
<title>Piping the output of <firstterm>echo</firstterm> to a
<firstterm>read</firstterm></title>
<programlisting>&badread;</programlisting>
</example>
<para><anchor id="pipeloop"/></para>
<para>In fact, as Anthony Richardson points out, piping to
<emphasis>any</emphasis> loop can cause a similar problem.</para>
<para>
<programlisting># Loop piping troubles.
# This example by Anthony Richardson,
#+ with addendum by Wilbert Berendsen.
foundone=false
find $HOME -type f -atime +30 -size 100k |
while true
do
read f
echo "$f is over 100KB and has not been accessed in over 30 days"
echo "Consider moving the file to archives."
foundone=true
# ------------------------------------
echo "Subshell level = $BASH_SUBSHELL"
# Subshell level = 1
# Yes, we're inside a subshell.
# ------------------------------------
done
# foundone will always be false here since it is
#+ set to true inside a subshell
if [ $foundone = false ]
then
echo "No files need archiving."
fi
# =====================Now, here is the correct way:=================
foundone=false
for f in $(find $HOME -type f -atime +30 -size 100k) # No pipe here.
do
echo "$f is over 100KB and has not been accessed in over 30 days"
echo "Consider moving the file to archives."
foundone=true
done
if [ $foundone = false ]
then
echo "No files need archiving."
fi
# ==================And here is another alternative==================
# Places the part of the script that reads the variables
#+ within a code block, so they share the same subshell.
# Thank you, W.B.
find $HOME -type f -atime +30 -size 100k | {
foundone=false
while read f
do
echo "$f is over 100KB and has not been accessed in over 30 days"
echo "Consider moving the file to archives."
foundone=true
done
if ! $foundone
then
echo "No files need archiving."
fi
}</programlisting>
</para>
<para><anchor id="ptailgrep"/></para>
<para>
A lookalike problem occurs when trying to write the
<filename>stdout</filename> of a <command>tail -f</command>
piped to <link linkend="grepref">grep</link>.
<programlisting>tail -f /var/log/messages | grep "$ERROR_MSG" >> error.log
# The "error.log" file will not have anything written to it.
# As Samuli Kaipiainen points out, this results from grep
#+ buffering its output.
# The fix is to add the "--line-buffered" parameter to grep.</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="suidscr"/></para>
<para>Using <quote>suid</quote> commands within scripts is risky,
as it may compromise system security.
<footnote><para>Setting the <link linkend="suidref">suid</link>
permission on the script itself has no effect in Linux
and most other UNIX flavors.</para></footnote>
</para>
</listitem>
<listitem>
<para><anchor id="cgiref"/></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>cracker-proof</quote> shell scripts.</para>
</listitem>
<listitem>
<para>Bash does not handle the <link linkend="doubleslashref">double slash
(<token>//</token>) string</link> correctly.</para>
</listitem>
<listitem>
<para><anchor id="gnuref"/></para>
<para>Bash scripts written for Linux or BSD systems may need
fixups to run on a commercial UNIX machine. Such
scripts often employ the GNU set of commands and filters,
which have greater functionality than their generic UNIX
counterparts. This is particularly true of such text processing
utilites as <link linkend="trref">tr</link>.</para>
</listitem>
<listitem>
<para><anchor id="updatebreaks"/></para>
<para>Sadly, updates to Bash itself have broken older scripts
that <link linkend="paragraphspace">used to work perfectly
fine</link>. Let us recall <link linkend="undocf">how
risky it is to use undocumented Bash features</link>.</para>
</listitem>
</itemizedlist>
<epigraph>
<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>
<para>--A.J. Lamb and H.W. Petrie</para>
</epigraph>
</chapter> <!-- Gotchas -->
<chapter id="scrstyle">
<title>Scripting With Style</title>
<para>Get into the habit of writing shell scripts in a structured and
systematic manner. Even on-the-fly 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
(necessarily) intended as an <emphasis>Official Shell Scripting
Stylesheet</emphasis>.</para>
<sect1 id="unofficialst">
<title>Unofficial Shell Scripting Stylesheet</title>
<itemizedlist>
<listitem>
<para>Comment your code. This makes it easier for others to
understand (and appreciate), and easier for you to maintain.
<programlisting>PASS="$PASS${MATRIX:$(($RANDOM%${#MATRIX})):1}"
# It made perfect sense when you wrote it last year,
#+ but now it's a complete mystery.
# (From Antek Sawicki's "pw.sh" script.)</programlisting>
</para>
<para>Add descriptive headers to your scripts and functions.
<programlisting>#!/bin/bash
#************************************************#
# xyz.sh #
# written by Bozo Bozeman #
# July 05, 2001 #
# #
# Clean up project files. #
#************************************************#
E_BADDIR=85 # 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, $E_BADDIR if something went wrong. #
# --------------------------------------------------------- #
cleanup_pfiles ()
{
if [ ! -d "$1" ] # Test if target directory exists.
then
echo "$1 is not a directory."
return $E_BADDIR
fi
rm -f "$1"/*
return 0 # Success.
}
cleanup_pfiles $projectdir
exit $?</programlisting></para>
</listitem>
<listitem>
<para>Avoid using <quote>magic numbers,</quote>
<footnote><para>In this context, <quote>magic
numbers</quote> have an entirely different meaning than
the <link linkend="magnumref">magic numbers</link> used
to designate file types.</para></footnote>
that is, <quote>hard-wired</quote> literal constants. Use
meaningful variable names instead. This makes the script
easier to understand and permits making changes and updates
without breaking the application.
<programlisting>if [ -f /var/log/messages ]
then
...
fi
# A year later, you decide to change the script to check /var/log/syslog.
# It is now necessary to manually change the script, instance by instance,
#+ and hope nothing breaks.
# A better way:
LOGFILE=/var/log/messages # Only line that needs to be changed.
if [ -f "$LOGFILE" ]
then
...
fi</programlisting>
</para>
</listitem>
<listitem>
<para>Choose descriptive names for variables and functions.
<programlisting>fl=`ls -al $dirname` # Cryptic.
file_listing=`ls -al $dirname` # Better.
MAXVAL=10 # All caps used for a script constant.
while [ "$index" -le "$MAXVAL" ]
...
E_NOTFOUND=95 # Uppercase for an errorcode,
#+ and name prefixed 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
export MAIL_DIRECTORY #+ variable.
GetAnswer () # Mixed case works well for a
{ #+ function name, especially
prompt=$1 #+ when it improves legibility.
echo -n $prompt
read answer
return $answer
}
GetAnswer "What is your favorite number? "
favorite_number=$?
echo $favorite_number
_uservariable=23 # Permissible, 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=95
...
...
exit $E_WRONG_ARGS</programlisting>
See also <xref linkend="exitcodes"/>.</para>
<para><emphasis>Ender</emphasis> suggests using the <link
linkend="sysexitsref">exit codes
in <filename>/usr/include/sysexits.h</filename></link> in shell
scripts, though these are primarily intended for C and C++
programming.</para>
</listitem>
<listitem>
<para>Use standardized parameter flags for script invocation.
<emphasis>Ender</emphasis> proposes the following set
of flags.</para>
<para>
<programlisting>-a All: Return all information (including hidden file info).
-b Brief: Short version, usually for other scripts.
-c Copy, concatenate, etc.
-d Daily: Use information from the whole day, and not merely
information for a specific instance/user.
-e Extended/Elaborate: (often does not include hidden file info).
-h Help: Verbose usage w/descs, aux info, discussion, help.
See also -V.
-l Log output of script.
-m Manual: Launch man-page for base command.
-n Numbers: Numerical data only.
-r Recursive: All files in a directory (and/or all sub-dirs).
-s Setup &amp; File Maintenance: Config files for this script.
-u Usage: List of invocation flags for the script.
-v Verbose: Human readable output, more or less formatted.
-V Version / License / Copy(right|left) / Contribs (email too).</programlisting>
</para>
<para>See also <xref linkend="standard-options"/>.</para>
</listitem>
<listitem>
<para>Break complex scripts into simpler modules. Use functions
where appropriate. See <xref linkend="cards"/>.</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>
<epigraph>
<para>... reading the UNIX source code to the Bourne shell (/bin/sh). I
was shocked at how much simple algorithms could be made cryptic, and
therefore useless, by a poor choice of code style. I asked myself,
<quote>Could someone be proud of this code?</quote></para>
<para>--Landon Noll</para>
</epigraph>
</sect1> <!-- Unofficial Shell Scripting Stylesheet -->
</chapter> <!-- Scripting With Style -->
<chapter id="miscellany">
<title>Miscellany</title>
<epigraph>
<para>Nobody really knows what the Bourne shell's grammar is. Even
examination of the source code is little help.</para>
<para>--Tom Duff</para>
</epigraph>
<sect1 id="intandnonint">
<title>Interactive and non-interactive shells and scripts</title>
<para>An <firstterm>interactive</firstterm> 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 <firstterm>interact</firstterm> 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
# St&eacute;phane Chazelas (thanks again).</programlisting></para>
<para>Let us consider an <firstterm>interactive</firstterm>
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 <firstterm>xterm</firstterm>.</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 <firstterm>prompt</firstterm> variable, <link
linkend="ps1ref">$PS1</link> is set. (If the user is being
prompted for input, then the script needs to display a
prompt.)</para>
<para><programlisting>if [ -z $PS1 ] # no prompt?
### if [ -v PS1 ] # On Bash 4.2+ ...
then
# non-interactive
...
else
# interactive
...
fi</programlisting></para>
<para><anchor id="iitest"/>Alternatively, the script can test
for the presence of option <quote>i</quote> in the <link
linkend="flpref">$-</link> flag.</para>
<para><programlisting>case $- in
*i*) # interactive shell
;;
*) # non-interactive shell
;;
# (Courtesy of "UNIX F.A.Q.," 1993)</programlisting></para>
<para><anchor id="ii2test"/>However, John Lange describes
an alternative method, using the <link
linkend="termtest"><token>-t</token>
<firstterm>test</firstterm> operator</link>.</para>
<para><programlisting># Test for a terminal!
fd=0 # stdin
# As we recall, the -t test option checks whether the stdin, [ -t 0 ],
#+ or stdout, [ -t 1 ], in a given script is running in a terminal.
if [ -t "$fd" ]
then
echo interactive
else
echo non-interactive
fi
# But, as John points out:
# if [ -t 0 ] works ... when you're logged in locally
# but fails when you invoke the command remotely via ssh.
# So for a true test you also have to test for a socket.
if [[ -t "$fd" || -p /dev/stdin ]]
then
echo interactive
else
echo non-interactive
fi</programlisting></para>
<note><para>Scripts may be forced to run in interactive
mode with the <token>-i</token> option or with a
<userinput>#!/bin/bash -i</userinput> header. Be aware that
this can cause erratic script behavior or show error messages
even when no error is present.</para></note>
</sect1> <!-- Interactive and non-interactive scripts -->
<sect1 id="wrapper">
<title>Shell Wrappers</title>
<para><anchor id="shwrapper"/></para>
<para>A <firstterm>wrapper</firstterm> is a shell script that embeds
a system command or utility, that accepts and passes a set of
parameters to that command.
<footnote><para>Quite a number of Linux utilities are, in fact,
shell wrappers. Some examples are
<filename>/usr/bin/pdf2ps</filename>,
<filename>/usr/bin/batch</filename>, and
<filename>/usr/bin/xmkmf</filename>.</para></footnote>
Wrapping a script around a complex command-line
simplifies invoking it. This is expecially useful
with <link linkend="sedref">sed</link> and <link
linkend="awkref">awk</link>.</para>
<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 <firstterm>reusable</firstterm>. This also
enables combining the functionality of <firstterm>sed</firstterm>
and <firstterm>awk</firstterm>, for example <link
linkend="piperef">piping</link> the output of a set of
<firstterm>sed</firstterm> commands to
<firstterm>awk</firstterm>. 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><firstterm>shell wrapper</firstterm></title>
<programlisting>&ex3;</programlisting>
</example>
<example id="ex4">
<title> A slightly more complex <firstterm>shell
wrapper</firstterm></title>
<programlisting>&ex4;</programlisting>
</example>
<example id="loggingwrapper">
<title> A generic <firstterm>shell wrapper</firstterm> that
writes to a logfile</title>
<programlisting>&loggingwrapper;</programlisting>
</example>
<example id="prasc">
<title> A <firstterm>shell wrapper</firstterm> around an awk
script</title>
<programlisting>&prasc;</programlisting>
</example>
<example id="coltotaler">
<title> A <firstterm>shell wrapper</firstterm> around another
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
<firstterm>Perl</firstterm>. 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 lend themselves to
embedding within shell scripts, and there may be some substance
to the claim that Perl can totally replace shell scripting
(though the author of the <emphasis>ABS Guide</emphasis> remains
skeptical).</para>
<para><anchor id="perlemb"/></para>
<example id="ex56">
<title>Perl embedded in a <firstterm>Bash</firstterm> script</title>
<programlisting>&ex56;</programlisting>
</example>
<para>It is even possible to combine a Bash script and Perl script
within the same file. Depending on how the script is invoked, either
the Bash part or the Perl part will execute.</para>
<para><anchor id="bashandperl0"/></para>
<example id="bashandperl">
<title>Bash and Perl scripts combined</title>
<programlisting>&bashandperl;</programlisting>
</example>
<para>
<screen><prompt>bash$ </prompt><userinput>bash bashandperl.sh</userinput>
<computeroutput>Greetings from the Bash part of the script.</computeroutput>
<prompt>bash$ </prompt><userinput>perl -x bashandperl.sh</userinput>
<computeroutput>Greetings from the Perl part of the script.</computeroutput>
</screen>
</para>
<para>It is, of course, possible to embed even more exotic scripting
languages within shell wrappers. <firstterm>Python</firstterm>,
for example ...</para>
<para><anchor id="pythonemb"/></para>
<example id="ex56py">
<title>Python embedded in a <firstterm>Bash</firstterm> script</title>
<programlisting>&ex56py;</programlisting>
</example>
<para>Wrapping a script around <firstterm>mplayer</firstterm>
and the Google's translation server, you can create something
that talks back to you.</para>
<para><anchor id="speech00"/></para>
<example id="speech0">
<title>A script that speaks</title>
<programlisting>&speech0;</programlisting>
</example>
<para>One interesting example of a complex shell wrapper is Martin
Matusiak's <ulink
url="http://sourceforge.net/projects/undvd/"><firstterm>undvd</firstterm>
script</ulink>, which provides an easy-to-use
command-line interface to the complex <ulink
url="http://www.mplayerhq.hu/DOCS/HTML/en/mencoder.html">mencoder</ulink>
utility. Another example is Itzchak Rehberg's <ulink
url="http://projects.izzysoft.de/trac/ext3undel">Ext3Undel</ulink>,
a set of scripts to recover deleted file on an
<firstterm>ext3</firstterm> filesystem.</para>
</sect1> <!-- Shell wrappers -->
<sect1 id="testsandcomparisons">
<title>Tests and Comparisons: Alternatives</title>
<para>For tests, the <link linkend="dblbrackets">[[ ]]</link>
construct may be more appropriate than <userinput>[
]</userinput>. Likewise, <link linkend="icomparison1">arithmetic
comparisons</link> might benefit from the <link
linkend="dblparens">(( ))</link> construct.
<programlisting>a=8
# All of the comparisons below are equivalent.
test "$a" -lt 16 &amp;&amp; echo "yes, $a &lt; 16" # "and list"
/bin/test "$a" -lt 16 &amp;&amp; echo "yes, $a &lt; 16"
[ "$a" -lt 16 ] &amp;&amp; echo "yes, $a &lt; 16"
[[ $a -lt 16 ]] &amp;&amp; echo "yes, $a &lt; 16" # Quoting variables within
(( a &lt; 16 )) &amp;&amp; echo "yes, $a &lt; 16" # [[ ]] and (( )) not necessary.
city="New York"
# Again, all of the comparisons below are equivalent.
test "$city" \&lt; Paris &amp;&amp; echo "Yes, Paris is greater than $city"
# Greater ASCII order.
/bin/test "$city" \&lt; Paris &amp;&amp; echo "Yes, Paris is greater than $city"
[ "$city" \&lt; Paris ] &amp;&amp; echo "Yes, Paris is greater than $city"
[[ $city &lt; Paris ]] &amp;&amp; echo "Yes, Paris is greater than $city"
# Need not quote $city.
# Thank you, S.C.</programlisting></para>
</sect1> <!-- Tests and Comparisons: Alternatives -->
<sect1 id="recursionsct">
<title>Recursion: a script calling itself</title>
<para><anchor id="scriptrecursion"/></para>
<para>Can a script <link linkend="recursionref">recursively</link>
call itself? Indeed.</para>
<example id="recurse">
<title>A (useless) script that recursively calls itself</title>
<programlisting>&recurse;</programlisting>
</example>
<example id="pbook">
<title>A (useful) script that recursively calls itself</title>
<programlisting>&pbook;</programlisting>
</example>
<example id="usrmnt">
<title>Another (useful) script that recursively calls itself</title>
<programlisting>&usrmnt;</programlisting>
</example>
<caution><para>Too many levels of recursion can exhaust the
script's stack space, causing a segfault.</para></caution>
</sect1> <!-- Recursion -->
<sect1 id="colorizing">
<title><quote>Colorizing</quote> Scripts</title>
<para><anchor id="colorizingref"/></para>
<para>The ANSI
<footnote><para><acronym>ANSI</acronym> is, of course, the
acronym for the American National Standards
Institute. This august body establishes and maintains
various technical and industrial standards.</para></footnote>
escape sequences set screen attributes, such as bold
text, and color of foreground and background. <link
linkend="dosbatch1">DOS batch files</link> commonly used
ANSI escape codes for <emphasis>color</emphasis> output,
and so can Bash scripts.</para>
<example id="ex30a">
<title>A <quote>colorized</quote> address database</title>
<programlisting>&ex30a;</programlisting>
</example>
<example id="draw-box">
<title>Drawing a box</title>
<programlisting>&drawbox;</programlisting>
</example>
<para>The simplest, and perhaps most useful ANSI escape sequence is
bold text, <command>\033[1m ... \033[0m</command>. The
<token>\033</token> represents an <link
linkend="escp">escape</link>, the <quote>[1</quote> turns on the
bold attribute, while the <quote>[0</quote> switches it off. The
<quote>m</quote> terminates each term of the escape sequence.
<screen>
<prompt>bash$ </prompt><userinput>echo -e "\033[1mThis is bold text.\033[0m"</userinput>
</screen>
</para>
<para>A similar escape sequence switches on the underline
attribute (on an <firstterm>rxvt</firstterm> and an
<firstterm>aterm</firstterm>).
<screen>
<prompt>bash$ </prompt><userinput>echo -e "\033[4mThis is underlined text.\033[0m"</userinput>
</screen>
</para>
<note><para>With an <command>echo</command>, the
<option>-e</option> option enables the escape
sequences.</para></note>
<para>Other escape sequences change the text and/or background
color.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo -e '\E[34;47mThis prints in blue.'; tput sgr0</userinput>
<prompt>bash$ </prompt><userinput>echo -e '\E[33;44m'"yellow text on blue background"; tput sgr0</userinput>
<prompt>bash$ </prompt><userinput>echo -e '\E[1;33;44m'"BOLD yellow text on blue background"; tput sgr0</userinput>
</screen>
</para>
<note><para>It's usually advisable to set the
<firstterm>bold</firstterm> attribute for light-colored foreground
text.</para></note>
<para>The <command>tput sgr0</command> restores the
terminal settings to normal. Omitting this lets all
subsequent output from that particular terminal remain
blue.</para>
<note><para>Since <command>tput sgr0</command> fails to restore
terminal settings under certain circumstances,
<command>echo -ne \E[0m</command> may be a better choice.</para></note>
<para><anchor id="coloriztempl"/></para>
<sidebar>
<para>Use the following template for writing colored text on a colored
background.</para>
<para>
<userinput>echo -e '\E[COLOR1;COLOR2mSome text goes here.'</userinput>
</para>
<para>The <quote>\E[</quote> begins the escape sequence.
The semicolon-separated numbers <quote>COLOR1</quote> and
<quote>COLOR2</quote> specify a foreground and a background
color, according to the table below. (The order of the
numbers does not matter, since the foreground and background
numbers fall in non-overlapping ranges.) The <quote>m</quote>
terminates the escape sequence, and the text begins immediately
after that.</para>
<para>Note also that <link linkend="snglquo">single quotes</link>
enclose the remainder of the command sequence following the
<command>echo -e</command>.</para>
</sidebar>
<para>The numbers in the following table work for an
<firstterm>rxvt</firstterm> terminal. Results may vary for other
terminal emulators.</para>
<para><anchor id="coloriztable"/></para>
<table>
<title>Numbers representing colors in Escape Sequences</title>
<tgroup cols="3">
<thead>
<row>
<entry>Color</entry>
<entry>Foreground</entry>
<entry>Background</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>black</option></entry>
<entry>30</entry>
<entry>40</entry>
</row>
<row>
<entry><option>red</option></entry>
<entry>31</entry>
<entry>41</entry>
</row>
<row>
<entry><option>green</option></entry>
<entry>32</entry>
<entry>42</entry>
</row>
<row>
<entry><option>yellow</option></entry>
<entry>33</entry>
<entry>43</entry>
</row>
<row>
<entry><option>blue</option></entry>
<entry>34</entry>
<entry>44</entry>
</row>
<row>
<entry><option>magenta</option></entry>
<entry>35</entry>
<entry>45</entry>
</row>
<row>
<entry><option>cyan</option></entry>
<entry>36</entry>
<entry>46</entry>
</row>
<row>
<entry><option>white</option></entry>
<entry>37</entry>
<entry>47</entry>
</row>
</tbody>
</tgroup>
</table>
<example id="colorecho">
<title>Echoing colored text</title>
<programlisting>&colorecho;</programlisting>
</example>
<para><anchor id="horseraceref"/></para>
<example id="horserace">
<title>A <quote>horserace</quote> game</title>
<programlisting>&horserace;</programlisting>
</example>
<para>See also <xref linkend="hashexample"/>, <xref
linkend="homework"/>, <xref linkend="showallc"/>, and <xref
linkend="petals"/>.</para>
<caution><para>There is, however, a major problem with all
this. <emphasis>ANSI escape sequences are emphatically
<link
linkend="portabilityissues">non-portable</link>.</emphasis>
What works fine on some terminal emulators (or the
console) may work differently, or not at all, on others.
A <quote>colorized</quote> script that looks stunning on the
script author's machine may produce unreadable output on
someone else's. This somewhat compromises the usefulness of
colorizing scripts, and possibly relegates this technique
to the status of a gimmick. Colorized scripts are probably
inappropriate in a commercial setting, i.e., your supervisor
might disapprove.</para></caution>
<para>Alister's <ulink url="http://code.google.com/p/ansi-color/">
ansi-color</ulink> utility (based on <ulink
url="http://bash.deta.in/color-1.1.tar.gz">Moshe
Jacobson's color utility</ulink> considerably simplifies using
ANSI escape sequences. It substitutes a clean and logical
syntax for the clumsy constructs just discussed.</para>
<para>Henry/teikedvl has likewise created a utility (<ulink
url="http://scriptechocolor.sourceforge.net/">http://scriptechocolor.sourceforge.net/</ulink>) to simplify creation of colorized scripts.</para>
</sect1> <!-- "Colorizing" scripts -->
<sect1 id="optimizations">
<title>Optimizations</title>
<para>Most shell scripts are quick 'n dirty solutions to non-complex
problems. As such, optimizing them for speed is not much of an
issue. Consider the case, though, where a script carries out
an important task, does it well, but runs too slowly. Rewriting
it in a compiled language may not be a palatable option. The
simplest fix would be to rewrite the parts of the script
that slow it down. Is it possible to apply principles of code
optimization even to a lowly shell script?</para>
<para>Check the loops in the script. Time consumed by repetitive
operations adds up quickly. If at all possible, remove
time-consuming operations from within loops.</para>
<para>Use <link linkend="builtinref">builtin</link> commands in
preference to system commands. Builtins execute faster and
usually do not launch a subshell when invoked.</para>
<para><anchor id="catabuse"/></para>
<para>Avoid unnecessary commands, particularly in a <link
linkend="piperef">pipe</link>.
<programlisting>cat "$file" | grep "$word"
grep "$word" "$file"
# The above command-lines have an identical effect,
#+ but the second runs faster since it launches one fewer subprocess.</programlisting>
The <link linkend="catref">cat</link> command seems especially
prone to overuse in scripts.</para>
<para><anchor id="lcall"/></para>
<sidebar><para>Disabling certain Bash options can speed up scripts.</para>
<para>As Erik Brandsberg points out:</para>
<para>If you don't need <link
linkend="unicoderef">Unicode</link> support, you can
get potentially a 2x or more improvement in speed by
simply setting the <userinput>LC_ALL</userinput> variable.
<programlisting> export LC_ALL=C
[specifies the locale as ANSI C,
thereby disabling Unicode support]
[In an example script ...]
Without [Unicode support]:
erik@erik-desktop:~/capture$ time ./cap-ngrep.sh
live2.pcap > out.txt
real 0m20.483s
user 1m34.470s
sys 0m12.869s
With [Unicode support]:
erik@erik-desktop:~/capture$ time ./cap-ngrep.sh
live2.pcap > out.txt
real 0m50.232s
user 3m51.118s
sys 0m11.221s
A large part of the overhead that is optimized is, I believe,
regex match using [[ string =~ REGEX ]],
but it may help with other portions of the code as well.
I hadn't [seen it] mentioned that this optimization helped
with Bash, but I had seen it helped with "grep,"
so why not try?</programlisting></para></sidebar>
<para><anchor id="optimes"/></para>
<note><para>Certain operators, notably <link
linkend="exprref">expr</link>, are very inefficient
and might be replaced by <link linkend="dblparens">double
parentheses</link> arithmetic expansion.
See <xref linkend="testexectime"/>.</para>
<para><programlisting>Math tests
math via $(( ))
real 0m0.294s
user 0m0.288s
sys 0m0.008s
math via expr:
real 1m17.879s # Much slower!
user 0m3.600s
sys 0m8.765s
math via let:
real 0m0.364s
user 0m0.372s
sys 0m0.000s</programlisting></para>
<para><link linkend="ifthen">Condition testing</link>
constructs in scripts deserve close scrutiny. Substitute
<link linkend="caseesac1">case</link> for <link
linkend="ifthen">if-then</link> constructs and combine tests
when possible, to minimize script execution time. Again,
refer to <xref linkend="testexectime"/>.</para>
<para><programlisting>Test using "case" construct:
real 0m0.329s
user 0m0.320s
sys 0m0.000s
Test with if [], no quotes:
real 0m0.438s
user 0m0.432s
sys 0m0.008s
Test with if [], quotes:
real 0m0.476s
user 0m0.452s
sys 0m0.024s
Test with if [], using -eq:
real 0m0.457s
user 0m0.456s
sys 0m0.000s</programlisting></para></note>
<para><anchor id="assocarrtst"/></para>
<note><para>Erik Brandsberg recommends using <link
linkend="assocarr">associative arrays</link> in preference to
conventional numeric-indexed arrays in most cases. When
overwriting values in a numeric array, there is a significant
performance penalty vs. associative arrays. Running a test
script confirms this. See <xref linkend="assocarrtest"/>.</para>
<para><programlisting>Assignment tests
Assigning a simple variable
real 0m0.418s
user 0m0.416s
sys 0m0.004s
Assigning a numeric index array entry
real 0m0.582s
user 0m0.564s
sys 0m0.016s
Overwriting a numeric index array entry
real 0m21.931s
user 0m21.913s
sys 0m0.016s
Linear reading of numeric index array
real 0m0.422s
user 0m0.416s
sys 0m0.004s
Assigning an associative array entry
real 0m1.800s
user 0m1.796s
sys 0m0.004s
Overwriting an associative array entry
real 0m1.798s
user 0m1.784s
sys 0m0.012s
Linear reading an associative array entry
real 0m0.420s
user 0m0.420s
sys 0m0.000s
Assigning a random number to a simple variable
real 0m0.402s
user 0m0.388s
sys 0m0.016s
Assigning a sparse numeric index array entry randomly into 64k cells
real 0m12.678s
user 0m12.649s
sys 0m0.028s
Reading sparse numeric index array entry
real 0m0.087s
user 0m0.084s
sys 0m0.000s
Assigning a sparse associative array entry randomly into 64k cells
real 0m0.698s
user 0m0.696s
sys 0m0.004s
Reading sparse associative index array entry
real 0m0.083s
user 0m0.084s
sys 0m0.000s</programlisting></para>
</note>
<para>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 <link linkend="awkref">awk</link> or <link
linkend="perlref">Perl</link>.</para>
<para>Write your scripts in a modular and coherent form,
<footnote><para>This usually means liberal use of
<link linkend="functionref">functions</link>.</para></footnote>
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
<firstterm>loop unrolling</firstterm>, are mostly
irrelevant. Above all, use common sense.</para>
<para>For an excellent demonstration of how optimization can
dramatically reduce the execution time of a script, see <xref
linkend="monthlypmt"/>.</para>
</sect1> <!-- Optimizations -->
<sect1 id="assortedtips">
<title>Assorted Tips</title>
<sect2>
<title>Ideas for more powerful scripts</title>
<itemizedlist>
<listitem>
<para><anchor id="pseudocoderef"/></para>
<para>You have a problem that you want to solve by writing a Bash
script. Unfortunately, you don't know quite where to start.
One method is to plunge right in and code those parts
of the script that come easily, and write the hard parts as
<firstterm>pseudo-code</firstterm>.</para>
<para><programlisting>#!/bin/bash
ARGCOUNT=1 # Need name as argument.
E_WRONGARGS=65
if [ number-of-arguments is-not-equal-to "$ARGCOUNT" ]
# ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
# Can't figure out how to code this . . .
#+ . . . so write it in pseudo-code.
then
echo "Usage: name-of-script name"
# ^^^^^^^^^^^^^^ More pseudo-code.
exit $E_WRONGARGS
fi
. . .
exit 0
# Later on, substitute working code for the pseudo-code.
# Line 6 becomes:
if [ $# -ne "$ARGCOUNT" ]
# Line 12 becomes:
echo "Usage: `basename $0` name"</programlisting></para>
<para>For an example of using pseudo-code, see the <link
linkend="newtonsqrt">Square Root</link> exercise.</para>
</listitem>
<listitem>
<para><anchor id="trackingscr"/></para>
<para>To keep a record of which user scripts have run
during a particular session 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.
whoami>> $SAVE_FILE # User invoking the script.
echo $0>> $SAVE_FILE # Script name.
date>> $SAVE_FILE # Date and time.
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><anchor id="prependref"/></para>
<para>The <token>&gt;&gt;</token> operator
<firstterm>appends</firstterm> lines to a file.
What if you wish to <firstterm>prepend</firstterm> a
line to an existing file, that is, to paste it in at the
beginning?</para>
<para>
<programlisting>file=data.txt
title="***This is the title line of data text file***"
echo $title | cat - $file >$file.new
# "cat -" concatenates stdout to $file.
# End result is
#+ to write a new file with $title appended at *beginning*.</programlisting>
</para>
<para>This is a simplified variant of the <xref
linkend="prependex"/> script given earlier. And, of course,
<link linkend="sedref">sed</link> can also do this.</para>
</listitem>
<listitem>
<para><anchor id="scriptasemb"/></para>
<para>A shell script may act as an embedded command inside
another shell script, a <firstterm>Tcl</firstterm> or
<firstterm>wish</firstterm> script, or even a <link
linkend="makefileref">Makefile</link>. It can be invoked
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><anchor id="setvaremb"/></para>
<para>Setting a variable to the contents of an embedded
<firstterm>sed</firstterm> or <firstterm>awk</firstterm>
script increases the readability of the surrounding <link
linkend="shwrapper">shell wrapper</link>. See <xref
linkend="mailformat"/> and <xref linkend="coltotaler3"/>.</para>
</listitem>
<listitem>
<para><anchor id="libroutines"/></para>
<para>Put together files containing your favorite and most useful
definitions and functions. As necessary,
<quote>include</quote> one or more of these
<quote>library files</quote> in scripts with either the
<link linkend="dotref">dot</link> (<command>.</command>)
or <link linkend="sourceref">source</link> command.</para>
<para>
<programlisting># SCRIPT LIBRARY
# ------ -------
# Note:
# No "#!" here.
# No "live code" either.
# Useful variable definitions
ROOT_UID=0 # Root has $UID 0.
E_NOTROOT=101 # Not root user error.
MAXRETVAL=255 # Maximum (positive) return value of a function.
SUCCESS=0
FAILURE=-1
# Functions
Usage () # "Usage:" message.
{
if [ -z "$1" ] # No arg passed.
then
msg=filename
else
msg=$@
fi
echo "Usage: `basename $0` "$msg""
}
Check_if_root () # Check if root running script.
{ # From "ex39.sh" example.
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi
}
CreateTempfileName () # Creates a "unique" temp filename.
{ # From "ex51.sh" example.
prefix=temp
suffix=`eval date +%s`
Tempfilename=$prefix.$suffix
}
isalpha2 () # Tests whether *entire string* is alphabetic.
{ # From "isalpha.sh" example.
[ $# -eq 1 ] || return $FAILURE
case $1 in
*[!a-zA-Z]*|"") return $FAILURE;;
*) return $SUCCESS;;
esac # Thanks, S.C.
}
abs () # Absolute value.
{ # Caution: Max return value = 255.
E_ARGERR=-999999
if [ -z "$1" ] # Need arg passed.
then
return $E_ARGERR # Obvious error value returned.
fi
if [ "$1" -ge 0 ] # If non-negative,
then #
absval=$1 # stays as-is.
else # Otherwise,
let "absval = (( 0 - $1 ))" # change sign.
fi
return $absval
}
tolower () # Converts string(s) passed as argument(s)
{ #+ to lowercase.
if [ -z "$1" ] # If no argument(s) passed,
then #+ send error message
echo "(null)" #+ (C-style void-pointer error message)
return #+ and return from function.
fi
echo "$@" | tr A-Z a-z
# Translate all passed arguments ($@).
return
# Use command substitution to set a variable to function output.
# For example:
# oldvar="A seT of miXed-caSe LEtTerS"
# newvar=`tolower "$oldvar"`
# echo "$newvar" # a set of mixed-case letters
#
# Exercise: Rewrite this function to change lowercase passed argument(s)
# to uppercase ... toupper() [easy].
}</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="commenth"/></para>
<para>Use special-purpose comment headers to increase clarity
and legibility in scripts.</para>
<para><programlisting>## Caution.
rm -rf *.zzy ## The "-rf" options to "rm" are very dangerous,
##+ especially with wild cards.
#+ Line continuation.
# This is line 1
#+ of a multi-line comment,
#+ and this is the final line.
#* Note.
#o List item.
#> Another point of view.
while [ "$var1" != "end" ] #> while test "$var1" != "end"</programlisting></para>
</listitem>
<listitem>
<para><anchor id="progbar"/></para>
<para>Dotan Barak contributes template code for a
<firstterm>progress bar</firstterm> in a script.</para>
<example id="progressbar">
<title>A Progress Bar</title>
<programlisting>&progressbar;</programlisting>
</example>
</listitem>
<listitem>
<para><anchor id="comoutbl"/></para>
<para>A particularly clever use of <link
linkend="testconstructs1">if-test</link> constructs
is for comment blocks.</para>
<para>
<programlisting>#!/bin/bash
COMMENT_BLOCK=
# Try setting the above variable to some value
#+ for an unpleasant surprise.
if [ $COMMENT_BLOCK ]; then
Comment block --
=================================
This is a comment line.
This is another comment line.
This is yet another comment line.
=================================
echo "This will not echo."
Comment blocks are error-free! Whee!
fi
echo "No more comments, please."
exit 0</programlisting>
</para>
<para>Compare this with <link linkend="cblock1">using
here documents to comment out code blocks</link>.</para>
</listitem>
<listitem>
<para><anchor id="intparam"/></para>
<para>Using the <link linkend="xstatvarref">$? exit status
variable</link>, a script may test if a parameter contains
only digits, so it can be treated as an integer.</para>
<para>
<programlisting>#!/bin/bash
SUCCESS=0
E_BADINPUT=85
test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null
# An integer is either equal to 0 or not equal to 0.
# 2>/dev/null suppresses error message.
if [ $? -ne "$SUCCESS" ]
then
echo "Usage: `basename $0` integer-input"
exit $E_BADINPUT
fi
let "sum = $1 + 25" # Would give error if $1 not integer.
echo "Sum = $sum"
# Any variable, not just a command-line parameter, can be tested this way.
exit 0</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="rvt"/>The 0 - 255 range for function return
values is a severe limitation. Global variables and
other workarounds are often problematic. An alternative
method for a function to communicate a value back to
the main body of the script is to have the function
write to <filename>stdout</filename> (usually with
<link linkend="echoref">echo</link>) the <quote>return
value,</quote> and assign this to a variable. This is
actually a variant of <link linkend="commandsubref">command
substitution.</link></para>
<example id="multiplication">
<title>Return value trickery</title>
<programlisting>&multiplication;</programlisting>
</example>
<para>The same technique also works for alphanumeric
strings. This means that a function can <quote>return</quote>
a non-numeric value.</para>
<para>
<programlisting>capitalize_ichar () # Capitalizes initial character
{ #+ of argument string(s) passed.
string0="$@" # Accepts multiple arguments.
firstchar=${string0:0:1} # First character.
string1=${string0:1} # Rest of string(s).
FirstChar=`echo "$firstchar" | tr a-z A-Z`
# Capitalize first character.
echo "$FirstChar$string1" # Output to stdout.
}
newstring=`capitalize_ichar "every sentence should start with a capital letter."`
echo "$newstring" # Every sentence should start with a capital letter.</programlisting>
</para>
<para>It is even possible for a function to <quote>return</quote>
multiple values with this method.</para>
<example id="sumproduct">
<title>Even more return value trickery</title>
<programlisting>&sumproduct;</programlisting>
</example>
<caution><para><anchor id="rvtcaution"/>There can be only
<command>one</command> <firstterm>echo</firstterm> statement
in the function for this to work. If you alter the previous
example:</para>
<para><programlisting>sum_and_product ()
{
echo "This is the sum_and_product function." # This messes things up!
echo $(( $1 + $2 )) $(( $1 * $2 ))
}
...
retval=`sum_and_product $first $second` # Assigns output of function.
# Now, this will not work correctly.</programlisting></para></caution>
</listitem>
<listitem>
<para><anchor id="passarray"/></para>
<para>Next in our bag of tricks are techniques for passing
an <link linkend="arrayref">array</link> to a
<link linkend="functionref">function</link>, then
<quote>returning</quote> an array back to the main body of
the script.</para>
<para>Passing an array involves loading the space-separated
elements of the array into a variable with <link
linkend="commandsubref">command substitution</link>. <anchor
id="retarray"/>Getting an array back as the <quote>return
value</quote> from a function uses the previously mentioned
strategem of <link linkend="echoref">echoing</link> the
array in the function, then invoking command substitution
and the <command>( ... )</command> operator to assign it to
an array.</para>
<example id="arrfunc">
<title>Passing and returning arrays</title>
<programlisting>&arrfunc;</programlisting>
</example>
<para>For a more elaborate example of passing arrays to
functions, see <xref linkend="lifeslow"/>.</para>
</listitem>
<listitem>
<para><anchor id="cstyle"/></para>
<para>Using the <link linkend="dblparens">double-parentheses
construct</link>, it is possible to use C-style syntax
for setting and incrementing/decrementing variables
and in <link linkend="forloopref1">for</link> and <link
linkend="whileloopref">while</link> loops. See <xref
linkend="forloopc"/> and <xref linkend="whloopc"/>.</para>
</listitem>
<listitem>
<para><anchor id="setpum"/></para>
<para>Setting the <link linkend="pathref">path</link> and <link
linkend="umaskref">umask</link> at the beginning of a script makes
it more <link linkend="portabilityissues">portable</link>
-- more likely to run on a <quote>foreign</quote> machine
whose user may have bollixed up the <varname>$PATH</varname>
and <command>umask</command>.
<programlisting>#!/bin/bash
PATH=/bin:/usr/bin:/usr/local/bin ; export PATH
umask 022 # Files that the script creates will have 755 permission.
# Thanks to Ian D. Allen, for this tip.</programlisting></para>
</listitem>
<listitem>
<para><anchor id="filteroutp"/></para>
<para>A useful scripting technique is to
<emphasis>repeatedly</emphasis> feed the output of a filter
(by piping) back to the <emphasis>same filter</emphasis>, but
with a different set of arguments and/or options. Especially
suitable for this are <link linkend="trref">tr</link> and
<link linkend="grepref">grep</link>.</para>
<para>
<programlisting># From "wstrings.sh" example.
wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`</programlisting>
</para>
<example id="agram">
<title>Fun with anagrams</title>
<programlisting>&agram;</programlisting>
</example>
<para>See also <xref linkend="constat"/>, <xref
linkend="cryptoquote"/>, and <xref linkend="soundex"/>.</para>
</listitem>
<listitem>
<para><anchor id="commblahd"/></para>
<para>Use <quote><link linkend="anonheredoc0">anonymous here
documents</link></quote> to comment out blocks of code,
to save having to individually comment out each line with
a <token>#</token>. See <xref linkend="commentblock"/>.</para>
</listitem>
<listitem>
<para><anchor id="whatisref3"/></para>
<para>Running a script on a machine that relies on a command
that might not be installed is dangerous. Use <link
linkend="whatisref">whatis</link> to avoid potential problems
with this.</para>
<para>
<programlisting>CMD=command1 # First choice.
PlanB=command2 # Fallback option.
command_test=$(whatis "$CMD" | grep 'nothing appropriate')
# If 'command1' not found on system , 'whatis' will return
#+ "command1: nothing appropriate."
#
# A safer alternative is:
# command_test=$(whereis "$CMD" | grep \/)
# But then the sense of the following test would have to be reversed,
#+ since the $command_test variable holds content only if
#+ the $CMD exists on the system.
# (Thanks, bojster.)
if [[ -z "$command_test" ]] # Check whether command present.
then
$CMD option1 option2 # Run command1 with options.
else # Otherwise,
$PlanB #+ run command2.
fi</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="ifgrepfix"/></para>
<para>An <link linkend="ifgrepref">if-grep test</link> may not
return expected results in an error case, when text is output to
<filename>stderr</filename>, rather that
<filename>stdout</filename>.
<programlisting>if ls -l nonexistent_filename | grep -q 'No such file or directory'
then echo "File \"nonexistent_filename\" does not exist."
fi</programlisting></para>
<para><link linkend="ioredirref">Redirecting</link>
<filename>stderr</filename> to <filename>stdout</filename> fixes
this.
<programlisting>if ls -l nonexistent_filename 2&gt;&amp;1 | grep -q 'No such file or directory'
# ^^^^
then echo "File \"nonexistent_filename\" does not exist."
fi
# Thanks, Chris Martin, for pointing this out.</programlisting></para>
</listitem>
<listitem>
<para><anchor id="subshtmp"/>
If you absolutely must access a subshell variable outside the
subshell, here's a way to do it.
<programlisting>TMPFILE=tmpfile # Create a temp file to store the variable.
( # Inside the subshell ...
inner_variable=Inner
echo $inner_variable
echo $inner_variable >>$TMPFILE # Append to temp file.
)
# Outside the subshell ...
echo; echo "-----"; echo
echo $inner_variable # Null, as expected.
echo "-----"; echo
# Now ...
read inner_variable &lt;$TMPFILE # Read back shell variable.
rm -f "$TMPFILE" # Get rid of temp file.
echo "$inner_variable" # It's an ugly kludge, but it works.</programlisting>
</para>
</listitem>
<listitem>
<para><anchor id="runpartsref2"/></para>
<para>The <link linkend="runpartsref">run-parts</link>
command is handy for running a set of command
scripts in a particular sequence, especially in
combination with <link linkend="cronref">cron</link> or
<link linkend="atref">at</link>.</para>
</listitem>
<listitem>
<para><anchor id="rcsref"/></para>
<para>For doing multiple revisions on a complex script, use the
<firstterm>rcs</firstterm> Revision Control System package.</para>
<para> Among other benefits of this is automatically updated ID
header tags. The <command>co</command> command in
<firstterm>rcs</firstterm> does a parameter replacement of
certain reserved key words, for example, replacing
<parameter># $Id$</parameter> in a script with something like:
<programlisting># $Id$</programlisting></para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Widgets</title>
<para><anchor id="widgetref"/></para>
<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 <firstterm>Xscript</firstterm>,
<firstterm>Xmenu</firstterm>, and <firstterm>widtools</firstterm>.
The first two of these no longer seem
to be maintained. Fortunately, it is still
possible to obtain <firstterm>widtools</firstterm> <ulink
url="http://www.batse.msfc.nasa.gov/~mallozzi/home/software/xforms/src/widtools-2.0.tgz">here</ulink>.
</para>
<caution><para>The <firstterm>widtools</firstterm> (widget tools)
package requires the <firstterm>XForms</firstterm> library to
be installed. Additionally, the <link
linkend="makefileref">Makefile</link> 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><anchor id="dialogref"/></para>
<para>The <firstterm>dialog</firstterm> family of tools offers a method
of calling <quote>dialog</quote> widgets from a shell script. The
original <firstterm>dialog</firstterm> utility works in a text
console, but its successors, <firstterm>gdialog</firstterm>,
<firstterm>Xdialog</firstterm>, and <firstterm>kdialog</firstterm>
use X-Windows-based widget sets.</para>
<example id="dialog">
<title>Widgets invoked from a shell script</title>
<programlisting>&dialog;</programlisting>
</example>
<para><anchor id="xmessageref2"/>
The <link linkend="xmessageref">xmessage</link> command is
a simple method of popping up a message/query window. For
example:
<programlisting>xmessage Fatal error in script! -button exit</programlisting>
</para>
<para><anchor id="zenityref2"/>
The latest entry in the widget sweepstakes is
<link linkend="zenityref">zenity</link>.
This utility pops up
<firstterm>GTK+</firstterm> dialog widgets-and-windows,
and it works very nicely within a script.
<programlisting>get_info ()
{
zenity --entry # Pops up query window . . .
#+ and prints user entry to stdout.
# Also try the --calendar and --scale options.
}
answer=$( get_info ) # Capture stdout in $answer variable.
echo "User entered: "$answer""</programlisting>
</para>
<para>For other methods of scripting with widgets, try
<firstterm>Tk</firstterm> or <firstterm>wish</firstterm>
(<firstterm>Tcl</firstterm> derivatives),
<firstterm>PerlTk</firstterm> (<firstterm>Perl</firstterm>
with <firstterm>Tk</firstterm> extensions),
<firstterm>tksh</firstterm> (<firstterm>ksh</firstterm>
with <firstterm>Tk</firstterm> extensions),
<firstterm>XForms4Perl</firstterm>
(<firstterm>Perl</firstterm> with
<firstterm>XForms</firstterm> extensions),
<firstterm>Gtk-Perl</firstterm> (<firstterm>Perl</firstterm>
with <firstterm>Gtk</firstterm> extensions), or
<firstterm>PyQt</firstterm> (<firstterm>Python</firstterm>
with <firstterm>Qt</firstterm> extensions).</para>
</sect2>
</sect1> <!-- Assorted Tips -->
<sect1 id="securityissues">
<title>Security Issues</title>
<sect2 id="infectedscripts">
<title>Infected Shell Scripts</title>
<para><anchor id="infectedscripts1"/></para>
<para>A brief warning about script security is indicated.
A shell script may contain a <firstterm>worm</firstterm>,
<firstterm>trojan</firstterm>, or even a
<firstterm>virus</firstterm>. For that reason, never run
as <firstterm>root</firstterm> a script (or permit it to
be inserted into the system startup scripts in <filename
class="directory">/etc/rc.d</filename>) unless you have obtained
said script from a trusted source or you have carefully analyzed
it to make certain it does nothing harmful.</para>
<para>Various researchers at Bell Labs and other sites, including M.
Douglas McIlroy, Tom Duff, and Fred Cohen have investigated the
implications of shell script viruses. They conclude that it is
all too easy for even a novice, a <quote>script kiddie,</quote>
to write one.
<footnote><para>See Marius van Oers' article, <ulink
url="http://www.virusbtn.com/magazine/archives/200204/malshell.xml">Unix
Shell Scripting Malware</ulink>, and also the
<link linkend="denningref"><emphasis>Denning</emphasis>
reference</link> in the
<firstterm>bibliography</firstterm>.</para></footnote>
</para>
<para>Here is yet another reason to learn scripting. Being able to
look at and understand scripts may protect your system from
being compromised by a rogue script.</para>
</sect2> <!-- Infected Shell Scripts -->
<sect2 id="hidingsource">
<title>Hiding Shell Script Source</title>
<para>For security purposes, it may be necessary to render a script
unreadable. If only there were a utility to create a stripped
binary executable from a script. Francisco Rosales' <ulink
url="http://www.datsi.fi.upm.es/~frosal/sources/">shc --
generic shell script compiler</ulink> does exactly that.</para>
<para>Unfortunately, according to <ulink
url="http://www.linuxjournal.com/article/8256">an article</ulink> in
the October, 2005 <emphasis>Linux Journal</emphasis>,
the binary can, in at least some cases, be decrypted to recover
the original script source. Still, this could be a useful
method of keeping scripts secure from all but the most skilled
hackers.</para>
</sect2> <!-- Hiding Shell Script Source -->
<sect2 id="securitytips">
<title>Writing Secure Shell Scripts</title>
<para><anchor id="securitytips1"/></para>
<para><emphasis>Dan Stromberg</emphasis> suggests the following
guidelines for writing (relatively) secure shell scripts.</para>
<para>
<itemizedlist>
<listitem>
<para>Don't put secret data in <link
linkend="envref">environment variables</link>.</para>
</listitem>
<listitem>
<para>Don't pass secret data in an external
command's arguments (pass them in via a <link
linkend="piperef">pipe</link> or <link
linkend="ioredirref">redirection</link> instead).</para>
</listitem>
<listitem>
<para>Set your <link linkend="pathref">$PATH</link>
carefully. Don't just trust whatever path you
inherit from the caller if your script is running as
<firstterm>root</firstterm>. In fact, whenever you use
an environment variable inherited from the caller, think
about what could happen if the caller put something
misleading in the variable, e.g., if the caller set
<link linkend="homedirref">$HOME</link> to <filename
class="directory">/etc</filename>.</para>
</listitem>
</itemizedlist>
</para>
</sect2> <!-- Security Tips -->
</sect1> <!-- Security -->
<sect1 id="portabilityissues">
<title>Portability Issues</title>
<epigraph>
<para>It is easier to port a shell than a shell script.</para>
<para>--Larry Wall</para>
</epigraph>
<para>This book deals specifically with Bash scripting on
a GNU/Linux system. All the same, users of <command>sh</command>
and <command>ksh</command> will find much of value here.</para>
<para><anchor id="posix3ref"/>As it happens, many of the various
shells and scripting languages seem to be converging toward the
<link linkend="posix2ref">POSIX</link> 1003.2 standard. Invoking
Bash with the <option>--posix</option> option or inserting
a <command>set -o posix</command> at the head of a script
causes Bash to conform very closely to this standard. Another
alternative is to use a <firstterm>#!/bin/sh</firstterm> <link
linkend="shabangref">sha-bang header</link> in the script,
rather than <firstterm>#!/bin/bash</firstterm>.
<footnote><para>Or, better yet, <link
linkend="envv2ref">#!/bin/env sh</link>.</para></footnote>
Note that <filename>/bin/sh</filename> is a <link
linkend="linkref">link</link> to <filename>/bin/bash</filename>
in Linux and certain other flavors of UNIX, and a script invoked
this way disables extended Bash functionality.</para>
<para>Most Bash scripts will run as-is under
<command>ksh</command>, and vice-versa, since Chet Ramey has
been busily porting <command>ksh</command> features to the
latest versions of Bash.</para>
<para>On a commercial UNIX machine, scripts using GNU-specific
features of standard commands may not work. This has become less
of a problem in the last few years, as the GNU utilities have
pretty much displaced their proprietary
counterparts even on <quote>big-iron</quote> UNIX.
<ulink url="http://linux.oreillynet.com/pub/a/linux/2002/02/28/caldera.html">Caldera's
release of the source</ulink> to many of the original UNIX
utilities has accelerated the trend.</para>
<para><anchor id="bashcompat"/></para>
<para>Bash has certain features that the traditional <link
linkend="bashdef">Bourne shell</link> lacks. Among these are:
<itemizedlist>
<listitem>
<para>Certain extended <link
linkend="invocationoptionsref">invocation options</link></para>
</listitem>
<listitem>
<para><link linkend="commandsubref">Command substitution</link> using
<command>$( )</command> notation</para>
</listitem>
<listitem>
<para><link linkend="braceexpref3">Brace expansion</link></para>
</listitem>
<listitem>
<para>Certain <link linkend="arrayref">array</link> operations,
and <link linkend="assocarr">associative arrays</link></para>
</listitem>
<listitem>
<para>The <link linkend="dblbrackets">double brackets</link>
extended test construct</para>
</listitem>
<listitem>
<para>The <link linkend="dblparensref">double-parentheses</link>
arithmetic-evaluation construct</para>
</listitem>
<listitem>
<para>Certain <link linkend="stringmanip">string manipulation</link>
operations</para>
</listitem>
<listitem>
<para><link linkend="processsubref">Process substitution</link></para>
</listitem>
<listitem>
<para>A Regular Expression <link linkend="regexmatchref">matching
operator</link></para>
</listitem>
<listitem>
<para>Bash-specific <link linkend="builtinref">builtins</link></para>
</listitem>
<listitem>
<para><link linkend="coprocref">Coprocesses</link></para>
</listitem>
</itemizedlist>
</para>
<para>See the <ulink url="ftp://ftp.cwru.edu/pub/bash/FAQ">Bash
F.A.Q.</ulink> for a complete listing.</para>
<sect2> <!-- A Test Suite -->
<title>A Test Suite</title>
<para><anchor id="testsuite0"/>Let us illustrate some of the
incompatibilities between Bash and the classic
Bourne shell. Download and install the <ulink
url="http://freshmeat.net/projects/bournesh"><quote>Heirloom
Bourne Shell</quote></ulink> and run the following
script, first using Bash, then the classic
<firstterm>sh</firstterm>.</para>
<example id="testsuite">
<title>Test Suite</title>
<programlisting>&testsuite;</programlisting>
</example>
</sect2> <!-- End: A Test Suite -->
</sect1> <!-- Portability Issues -->
<sect1 id="winscript">
<title>Shell Scripting Under Windows</title>
<para>Even users running <emphasis>that other</emphasis> OS can
run UNIX-like shell scripts, and therefore benefit
from many of the lessons of this book. The <ulink
url="http://sourceware.cygnus.com/cygwin/">
Cygwin</ulink> package from Cygnus and the <ulink
url="http://www.mkssoftware.com/">MKS utilities</ulink> from
Mortice Kern Associates add shell scripting capabilities to
Windows.</para>
<para>Another alternative is <ulink
url="http://www2.research.att.com/~gsf/download/uwin/uwin.html">
UWIN</ulink>, written by David Korn of AT&amp;T, of <link
linkend="kornshellref">Korn Shell</link> fame.</para>
<para>In 2006, Microsoft released the <trademark
class="registered">Windows Powershell</trademark>,
which contains limited Bash-like command-line scripting
capabilities.</para>
</sect1> <!-- Shell Scripting Under Windows -->
</chapter> <!-- Miscellany -->
<chapter id="bash2">
<title>Bash, versions 2, 3, and 4</title>
<sect1 id="bashver2">
<title>Bash, version 2</title>
<para><anchor id="bash2ref"/></para>
<para>
The current version of <firstterm>Bash</firstterm>, the one
you have running on your machine, is most likely version 2.xx.yy,
3.xx.yy, or 4.xx.yy.
<screen><prompt>bash$ </prompt><userinput>echo $BASH_VERSION</userinput>
<computeroutput>3.2.25(1)-release</computeroutput>
</screen>
</para>
<para>The version 2 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>
<para><anchor id="varrefnew"/></para>
<example id="ex78">
<title>Indirect variable references - the new way</title>
<programlisting>&ex78;</programlisting>
</example>
<example id="resistor">
<title>Simple database application, using indirect variable
referencing</title>
<programlisting>&resistor;</programlisting>
</example>
<example id="cards">
<title>Using arrays and other miscellaneous trickery
to deal four random hands from a deck of cards</title>
<programlisting>&cards;</programlisting>
</example>
</sect1> <!-- Bash, Version 2 -->
<sect1 id="bashver3">
<title>Bash, version 3</title>
<para><anchor id="bash3ref"/></para>
<para>On July 27, 2004, Chet Ramey released version 3 of Bash.
This update fixed quite a number of bugs and added new
features.</para>
<para>Some of the more important added features:
<itemizedlist>
<listitem>
<para><anchor id="braceexpref3"/></para>
<para>A new, more generalized <command>{a..z}</command> <link
linkend="braceexpref">brace expansion</link> operator.</para>
<para><programlisting>#!/bin/bash
for i in {1..10}
# Simpler and more straightforward than
#+ for i in $(seq 10)
do
echo -n "$i "
done
echo
# 1 2 3 4 5 6 7 8 9 10
# Or just . . .
echo {a..z} # a b c d e f g h i j k l m n o p q r s t u v w x y z
echo {e..m} # e f g h i j k l m
echo {z..a} # z y x w v u t s r q p o n m l k j i h g f e d c b a
# Works backwards, too.
echo {25..30} # 25 26 27 28 29 30
echo {3..-2} # 3 2 1 0 -1 -2
echo {X..d} # X Y Z [ ] ^ _ ` a b c d
# Shows (some of) the ASCII characters between Z and a,
#+ but don't rely on this type of behavior because . . .
echo {]..a} # {]..a}
# Why?
# You can tack on prefixes and suffixes.
echo "Number #"{1..4}, "..."
# Number #1, Number #2, Number #3, Number #4, ...
# You can concatenate brace-expansion sets.
echo {1..3}{x..z}" +" "..."
# 1x + 1y + 1z + 2x + 2y + 2z + 3x + 3y + 3z + ...
# Generates an algebraic expression.
# This could be used to find permutations.
# You can nest brace-expansion sets.
echo {{a..c},{1..3}}
# a b c 1 2 3
# The "comma operator" splices together strings.
# ########## ######### ############ ########### ######### ###############
# Unfortunately, brace expansion does not lend itself to parameterization.
var1=1
var2=5
echo {$var1..$var2} # {1..5}
# Yet, as Emiliano G. points out, using "eval" overcomes this limitation.
start=0
end=10
for index in $(eval echo {$start..$end})
do
echo -n "$index " # 0 1 2 3 4 5 6 7 8 9 10
done
echo</programlisting></para>
</listitem>
<listitem>
<para>The <command>${!array[@]}</command> operator, which
expands to all the indices of a given <link
linkend="arrayref">array</link>.</para>
<para><programlisting>#!/bin/bash
Array=(element-zero element-one element-two element-three)
echo ${Array[0]} # element-zero
# First element of array.
echo ${!Array[@]} # 0 1 2 3
# All the indices of Array.
for i in ${!Array[@]}
do
echo ${Array[i]} # element-zero
# element-one
# element-two
# element-three
#
# All the elements in Array.
done</programlisting></para>
</listitem>
<listitem>
<para><anchor id="regexmatchref"/></para>
<para>The <command>=~</command> <link linkend="regexref">Regular
Expression</link> matching operator within a <link
linkend="dblbrackets">double brackets</link> test expression.
(Perl has a similar operator.)</para>
<para><programlisting>#!/bin/bash
variable="This is a fine mess."
echo "$variable"
# Regex matching with =~ operator within [[ double brackets ]].
if [[ "$variable" =~ T.........fin*es* ]]
# NOTE: As of version 3.2 of Bash, expression to match no longer quoted.
then
echo "match found"
# match found
fi</programlisting></para>
<para>Or, more usefully:</para>
<para><programlisting>#!/bin/bash
input=$1
if [[ "$input" =~ "[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]" ]]
# ^ NOTE: Quoting not necessary, as of version 3.2 of Bash.
# NNN-NN-NNNN (where each N is a digit).
then
echo "Social Security number."
# Process SSN.
else
echo "Not a Social Security number!"
# Or, ask for corrected input.
fi</programlisting></para>
<para>For additional examples of using the
<command>=~</command> operator, see <xref linkend="whx"/>,
<xref linkend="mailboxgrep"/>, <xref
linkend="findsplit"/>, and <xref linkend="tohtml"/>.</para>
</listitem>
<listitem>
<para><anchor id="pipefailref"/></para>
<para>The new <option>set -o pipefail</option> option is
useful for debugging <link linkend="piperef">pipes</link>. If
this option is set, then the <link
linkend="exitstatusref">exit status</link> of a pipe
is the exit status of the last command in the pipe to
<emphasis>fail</emphasis> (return a non-zero value), rather
than the actual final command in the pipe.</para>
<para>See <xref linkend="fc4upd"/>.</para>
</listitem>
</itemizedlist>
</para>
<caution>
<para>The update to version 3 of Bash breaks a few scripts
that worked under earlier versions. <emphasis>Test critical legacy
scripts to make sure they still work!</emphasis></para>
<para>As it happens, a couple of the scripts in the
<emphasis>Advanced Bash Scripting Guide</emphasis> had to be
fixed up (see <xref linkend="tout"/>, for instance).</para>
</caution>
<sect2> <!-- Bash, Version 3.1 -->
<title>Bash, version 3.1</title>
<para>The version 3.1 update of Bash introduces a number of bugfixes
and a few minor changes.</para>
<itemizedlist>
<listitem>
<para>The <token>+=</token> operator is now permitted in
in places where previously only the <token>=</token>
assignment operator was recognized.</para>
<para><anchor id="pluseqstr"/></para>
<para><programlisting>a=1
echo $a # 1
a+=5 # Won't work under versions of Bash earlier than 3.1.
echo $a # 15
a+=Hello
echo $a # 15Hello</programlisting>
</para>
<para>Here, <token>+=</token> functions as a <firstterm>string
concatenation</firstterm> operator. Note that its behavior
in this particular context is different than within a
<link linkend="letref">let</link> construct.</para>
<para><programlisting>a=1
echo $a # 1
let a+=5 # Integer arithmetic, rather than string concatenation.
echo $a # 6
let a+=Hello # Doesn't "add" anything to a.
echo $a # 6</programlisting>
</para>
<para><anchor id="pathappend"/>Jeffrey Haemer points out
that this concatenation operator can be quite
useful. In this instance, we append a directory to the
<varname>$PATH</varname>.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>echo $PATH</userinput>
<computeroutput>/usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin/:/usr/games</computeroutput>
<prompt>bash$ </prompt><userinput>PATH+=:/opt/bin</userinput>
<prompt>bash$ </prompt><userinput>echo $PATH</userinput>
<computeroutput>/usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin/:/usr/games:/opt/bin</computeroutput>
</screen>
</para>
</listitem>
</itemizedlist>
</sect2> <!-- Bash, Version 3.1 -->
<sect2> <!-- Bash, Version 3.2 -->
<title>Bash, version 3.2</title>
<para>This is pretty much a bugfix update.</para>
<itemizedlist>
<listitem>
<para>In <link linkend="psglob"><firstterm>global</firstterm>
parameter substitutions</link>, the pattern no longer anchors
at the start of the string.</para>
</listitem>
<listitem>
<para>The <option>--wordexp</option> option disables
<link linkend="processsubref">process substitution</link>.</para>
</listitem>
<listitem>
<para>The <command>=~</command> <link
linkend="regexmatchref">Regular Expression
match operator</link> no longer requires
<link linkend="quotingref">quoting</link> of the
<firstterm>pattern</firstterm> within <link
linkend="dblbrackets">[[ ... ]]</link>.</para>
<caution>
<para>In fact, quoting in this context is
<emphasis>not</emphasis> advisable as it may
cause <firstterm>regex</firstterm> evaluation to fail.
Chet Ramey states in the <link linkend="bashfaq">Bash
FAQ</link> that quoting explicitly disables regex evaluation.
See also the <ulink
url="https://bugs.launchpad.net/ubuntu-website/+bug/109931">
Ubuntu Bug List</ulink> and <ulink
url="http://en.wikinerds.org/index.php/Bash_syntax_and_semantics">
Wikinerds on Bash syntax</ulink>.</para>
<para>Setting <emphasis>shopt -s compat31</emphasis>
in a script causes reversion to the original
behavior.</para>
</caution>
</listitem>
</itemizedlist>
</sect2> <!-- Bash, Version 3.2 -->
</sect1> <!-- Bash, Version 3 -->
<sect1 id="bashver4">
<title>Bash, version 4</title>
<para><anchor id="bash4ref"/></para>
<para>Chet Ramey announced Version 4 of Bash on the 20th
of February, 2009. This release has a number of significant
new features, as well as some important bugfixes.</para>
<para>Among the new goodies:</para>
<itemizedlist>
<listitem>
<para><anchor id="assocarr"/>Associative arrays.
<footnote><para>To be more specific, Bash 4+ has
<emphasis>limited</emphasis> support for associative
arrays. It's a bare-bones implementation,
and it lacks the much of the functionality of such
arrays in other programming languages. Note, however,
that <link linkend="assocarrtst">associative arrays in
Bash seem to execute faster and more efficiently than
numerically-indexed arrays</link>.</para></footnote>
</para>
<sidebar>
<para>An <firstterm>associative</firstterm> array can
be thought of as a set of two linked arrays -- one holding
the <firstterm>data</firstterm>, and the other the
<firstterm>keys</firstterm> that index the individual elements
of the <firstterm>data</firstterm> array.</para>
</sidebar>
<example id="fetchaddress">
<title>A simple address database</title>
<programlisting>&fetchaddress;</programlisting>
</example>
<example id="fetchaddress2">
<title>A somewhat more elaborate address database</title>
<programlisting>&fetchaddress2;</programlisting>
</example>
<para>See <xref linkend="samorse"/> for an interesting
usage of an <firstterm>associative array</firstterm>.</para>
<caution><para>Elements of the <firstterm>index</firstterm> array
may include embedded <link linkend="whitespaceref">space
characters</link>, or even leading and/or trailing space
characters. However, index array elements containing
<emphasis>only</emphasis> <firstterm>whitespace</firstterm>
are <emphasis>not</emphasis> permitted.</para>
<para><programlisting>address[ ]="Blank" # Error!</programlisting>
</para></caution>
</listitem>
<listitem>
<para><anchor id="ncterm"/>Enhancements to the
<link linkend="caseesac1">case</link> construct:
the <replaceable>;;&amp;</replaceable> and
<replaceable>;&amp;</replaceable> terminators.</para>
<example id="case4">
<title>Testing characters</title>
<programlisting>&case4;</programlisting>
</example>
</listitem>
<listitem>
<para><anchor id="coprocref"/>The new <command>coproc</command>
builtin enables two parallel <link
linkend="processref">processes</link> to communicate and
interact. As Chet Ramey states in the
<link linkend="bashfaq">Bash FAQ</link>
<footnote><para>Copyright 1995-2009 by Chester Ramey.</para></footnote>
, ver. 4.01:
</para>
<blockquote>
<literallayout>
There is a new 'coproc' reserved word that specifies a coprocess:
an asynchronous command run with two pipes connected to the creating
shell. Coprocs can be named. The input and output file descriptors
and the PID of the coprocess are available to the calling shell in
variables with coproc-specific names.
George Dimitriu explains,
"... coproc ... is a feature used in Bash process substitution,
which now is made publicly available."
This means it can be explicitly invoked in a script, rather than
just being a behind-the-scenes mechanism used by Bash.
</literallayout>
</blockquote>
<para>Coprocesses use <firstterm>file descriptors</firstterm>.
<link linkend="fdref2">File descriptors enable processes and
pipes to communicate</link>.</para>
<para><programlisting>#!/bin/bash4
# A coprocess communicates with a while-read loop.
coproc { cat mx_data.txt; sleep 2; }
# ^^^^^^^
# Try running this without "sleep 2" and see what happens.
while read -u ${COPROC[0]} line # ${COPROC[0]} is the
do #+ file descriptor of the coprocess.
echo "$line" | sed -e 's/line/NOT-ORIGINAL-TEXT/'
done
kill $COPROC_PID # No longer need the coprocess,
#+ so kill its PID.</programlisting></para>
<para>But, be careful!</para>
<para><programlisting>#!/bin/bash4
echo; echo
a=aaa
b=bbb
c=ccc
coproc echo "one two three"
while read -u ${COPROC[0]} a b c; # Note that this loop
do #+ runs in a subshell.
echo "Inside while-read loop: ";
echo "a = $a"; echo "b = $b"; echo "c = $c"
echo "coproc file descriptor: ${COPROC[0]}"
done
# a = one
# b = two
# c = three
# So far, so good, but ...
echo "-----------------"
echo "Outside while-read loop: "
echo "a = $a" # a =
echo "b = $b" # b =
echo "c = $c" # c =
echo "coproc file descriptor: ${COPROC[0]}"
echo
# The coproc is still running, but ...
#+ it still doesn't enable the parent process
#+ to "inherit" variables from the child process, the while-read loop.
# Compare this to the "badread.sh" script.</programlisting></para>
<caution>
<para>The coprocess is <firstterm>asynchronous</firstterm>,
and this might cause a problem. It may terminate before another
process has finished communicating with it.</para>
<programlisting>#!/bin/bash4
coproc cpname { for i in {0..10}; do echo "index = $i"; done; }
# ^^^^^^ This is a *named* coprocess.
read -u ${cpname[0]}
echo $REPLY # index = 0
echo ${COPROC[0]} #+ No output ... the coprocess timed out
# after the first loop iteration.
# However, George Dimitriu has a partial fix.
coproc cpname { for i in {0..10}; do echo "index = $i"; done; sleep 1;
echo hi > myo; cat - >> myo; }
# ^^^^^ This is a *named* coprocess.
echo "I am main"$'\04' >&amp;${cpname[1]}
myfd=${cpname[0]}
echo myfd=$myfd
### while read -u $myfd
### do
### echo $REPLY;
### done
echo $cpname_PID
# Run this with and without the commented-out while-loop, and it is
#+ apparent that each process, the executing shell and the coprocess,
#+ waits for the other to finish writing in its own write-enabled pipe.</programlisting>
</caution>
</listitem>
<listitem>
<para><anchor id="mapfileref"/>The new <command>mapfile</command>
builtin makes it possible to load an array with the contents
of a text file without using a loop or <link
linkend="arrayinitcs">command substitution</link>.</para>
<para><programlisting>#!/bin/bash4
mapfile Arr1 &lt; $0
# Same result as Arr1=( $(cat $0) )
echo "${Arr1[@]}" # Copies this entire script out to stdout.
echo "--"; echo
# But, not the same as read -a !!!
read -a Arr2 &lt; $0
echo "${Arr2[@]}" # Reads only first line of script into the array.
exit</programlisting></para>
</listitem>
<listitem>
<para>The <link linkend="readref">read</link> builtin got
a minor facelift. The <option>-t</option>
<link linkend="readtimed">timeout</link> option now accepts
(decimal) fractional values
<footnote><para>This only works with <link
linkend="piperef">pipes</link> and certain other
<firstterm>special</firstterm> files.</para></footnote>
and the <option>-i</option> option
permits preloading the edit buffer.
<footnote><para>But only in conjunction with
<link linkend="readlineref">readline</link>, i.e.,
from the command-line.</para></footnote>
Unfortunately, these enhancements are still a work in progress
and not (yet) usable in scripts.</para>
</listitem>
<listitem>
<para><anchor id="casemodparamsub"/>
<link linkend="paramsubref">Parameter substitution</link>
gets <firstterm>case-modification</firstterm> operators.</para>
<para><programlisting>#!/bin/bash4
var=veryMixedUpVariable
echo ${var} # veryMixedUpVariable
echo ${var^} # VeryMixedUpVariable
# * First char --> uppercase.
echo ${var^^} # VERYMIXEDUPVARIABLE
# ** All chars --> uppercase.
echo ${var,} # veryMixedUpVariable
# * First char --> lowercase.
echo ${var,,} # verymixedupvariable
# ** All chars --> lowercase.</programlisting></para>
</listitem>
<listitem>
<para><anchor id="declarecasemod"/></para>
<para>The <link linkend="declareref">declare</link> builtin now
accepts the <option>-l</option> <firstterm>lowercase</firstterm>
and <option>-c</option> <firstterm>capitalize</firstterm>
options.</para>
<para><programlisting>#!/bin/bash4
declare -l var1 # Will change to lowercase
var1=MixedCaseVARIABLE
echo "$var1" # mixedcasevariable
# Same effect as echo $var1 | tr A-Z a-z
declare -c var2 # Changes only initial char to uppercase.
var2=originally_lowercase
echo "$var2" # Originally_lowercase
# NOT the same effect as echo $var2 | tr a-z A-Z</programlisting></para>
</listitem>
<listitem>
<para><anchor id="braceexpref4"/>
<link linkend="braceexpref">Brace expansion</link> has more options.</para>
<para><firstterm>Increment/decrement</firstterm>, specified in the
final term within braces.</para>
<para><programlisting>#!/bin/bash4
echo {40..60..2}
# 40 42 44 46 48 50 52 54 56 58 60
# All the even numbers, between 40 and 60.
echo {60..40..2}
# 60 58 56 54 52 50 48 46 44 42 40
# All the even numbers, between 40 and 60, counting backwards.
# In effect, a decrement.
echo {60..40..-2}
# The same output. The minus sign is not necessary.
# But, what about letters and symbols?
echo {X..d}
# X Y Z [ ] ^ _ ` a b c d
# Does not echo the \ which escapes a space.</programlisting></para>
<para><firstterm>Zero-padding</firstterm>, specified in the
first term within braces, prefixes each term in the output
with the <emphasis>same number</emphasis> of zeroes.</para>
<screen>
<prompt>bash4$ </prompt><userinput>echo {010..15}</userinput>
<computeroutput>010 011 012 013 014 015</computeroutput>
<prompt>bash4$ </prompt><userinput>echo {000..10}</userinput>
<computeroutput>000 001 002 003 004 005 006 007 008 009 010</computeroutput>
</screen>
</listitem>
<listitem>
<para><anchor id="substrextref4"/></para>
<para><link linkend="substrextref4"><firstterm>Substring
extraction</firstterm> on <firstterm>positional
parameters</firstterm></link> now starts with <link
linkend="scrnameparam">$0</link> as the
<firstterm>zero-index</firstterm>. (This corrects an
inconsistency in the treatment of positional parameters.)</para>
<para><programlisting>#!/bin/bash
# show-params.bash
# Requires version 4+ of Bash.
# Invoke this scripts with at least one positional parameter.
E_BADPARAMS=99
if [ -z "$1" ]
then
echo "Usage $0 param1 ..."
exit $E_BADPARAMS
fi
echo ${@:0}
# bash3 show-params.bash4 one two three
# one two three
# bash4 show-params.bash4 one two three
# show-params.bash4 one two three
# $0 $1 $2 $3</programlisting></para>
</listitem>
<listitem>
<para><anchor id="globstarref"/>The new <token>**</token>
<link linkend="globbingref">globbing</link> operator
matches filenames and directories
<link linkend="recursionref0">recursively</link>.</para>
<para><programlisting>#!/bin/bash4
# filelist.bash4
shopt -s globstar # Must enable globstar, otherwise ** doesn't work.
# The globstar shell option is new to version 4 of Bash.
echo "Using *"; echo
for filename in *
do
echo "$filename"
done # Lists only files in current directory ($PWD).
echo; echo "--------------"; echo
echo "Using **"
for filename in **
do
echo "$filename"
done # Lists complete file tree, recursively.
exit
Using *
allmyfiles
filelist.bash4
--------------
Using **
allmyfiles
allmyfiles/file.index.txt
allmyfiles/my_music
allmyfiles/my_music/me-singing-60s-folksongs.ogg
allmyfiles/my_music/me-singing-opera.ogg
allmyfiles/my_music/piano-lesson.1.ogg
allmyfiles/my_pictures
allmyfiles/my_pictures/at-beach-with-Jade.png
allmyfiles/my_pictures/picnic-with-Melissa.png
filelist.bash4</programlisting></para>
</listitem>
<listitem>
<para>The new <link linkend="bashpidref">$BASHPID</link>
internal variable.</para>
</listitem>
<listitem>
<para><anchor id="cnfh"/></para>
<para>There is a new <link linkend="builtinref">builtin</link>
error-handling function named
<command>command_not_found_handle</command>.</para>
<para><programlisting>#!/bin/bash4
command_not_found_handle ()
{ # Accepts implicit parameters.
echo "The following command is not valid: \""$1\"""
echo "With the following argument(s): \""$2\"" \""$3\""" # $4, $5 ...
} # $1, $2, etc. are not explicitly passed to the function.
bad_command arg1 arg2
# The following command is not valid: "bad_command"
# With the following argument(s): "arg1" "arg2"</programlisting></para>
</listitem>
</itemizedlist>
<sidebar>
<para><emphasis>Editorial comment</emphasis></para>
<para>Associative arrays? Coprocesses? Whatever happened
to the lean and mean Bash we have come to know and love?
Could it be suffering from (horrors!) <quote>feature
creep</quote>? Or perhaps even Korn shell envy?</para>
<para><emphasis>Note to Chet Ramey:</emphasis> Please add only
<emphasis>essential</emphasis> features in future Bash
releases -- perhaps <firstterm>for-each</firstterm>
loops and support for multi-dimensional arrays.
<footnote><para>And while you're at it, consider fixing
the notorious <link linkend="pipereadref0">piped read</link>
problem.</para></footnote>
Most Bash users won't need, won't use, and likely won't greatly
appreciate complex <quote>features</quote> like built-in
debuggers, Perl interfaces, and bolt-on rocket boosters.</para>
</sidebar>
<sect2> <!-- Bash, Version 4.1 -->
<title>Bash, version 4.1</title>
<para><anchor id="bash41"/>Version 4.1 of Bash, released in May,
2010, was primarily a bugfix update.</para>
<itemizedlist>
<listitem>
<para>The <link linkend="printfref">printf</link> command
now accepts a <option>-v</option> option for setting <link
linkend="arrayref">array</link> indices.</para>
</listitem>
<listitem>
<para>Within <link linkend="dblbrackets">double brackets</link>,
the <command>&gt;</command> and <command>&lt;</command>
string comparison operators now conform to the <link
linkend="localeref">locale</link>. Since the locale setting may
affect the sorting order of string expressions, this
has side-effects on comparison tests within
<emphasis>[[ ... ]]</emphasis> expressions.</para>
</listitem>
<listitem>
<para>The <link linkend="readref">read</link> builtin
now takes a <option>-N</option> option (<firstterm>read -N
chars</firstterm>), which causes the <firstterm>read</firstterm>
to terminate after <firstterm>chars</firstterm>
characters.</para>
<example id="readn">
<title>Reading N characters</title>
<programlisting>&readn;</programlisting>
</example>
</listitem>
<listitem>
<para><link linkend="heredocref">Here documents</link>
embedded in <link linkend="commandsubref0">
<userinput>$( ... )</userinput> command substitution</link>
constructs may terminate with a simple
<command>)</command>.</para>
<example id="herecommsub">
<title>Using a <firstterm>here document</firstterm>
to set a variable</title>
<programlisting>&herecommsub;</programlisting>
</example>
</listitem>
</itemizedlist>
</sect2> <!-- Bash, Version 4.1 -->
<sect2> <!-- Bash, Version 4.2 -->
<title>Bash, version 4.2</title>
<para><anchor id="bash42"/>Version 4.2 of Bash, released
in February, 2011, contains a number of new features and
enhancements, in addition to bugfixes.</para>
<itemizedlist>
<listitem>
<para>Bash now supports the
<replaceable>\u</replaceable>
and <replaceable>\U</replaceable>
<firstterm>Unicode</firstterm> escape.</para>
<para><anchor id="unicoderef"/></para>
<sidebar><para>Unicode is a cross-platform standard for encoding
into numerical values letters and graphic symbols.
This permits representing and displaying characters
in foreign alphabets and unusual fonts.</para></sidebar>
<para><anchor id="unicoderef2"/></para>
<para><programlisting>echo -e '\u2630' # Horizontal triple bar character.
# Equivalent to the more roundabout:
echo -e "\xE2\x98\xB0"
# Recognized by earlier Bash versions.
echo -e '\u220F' # PI (Greek letter and mathematical symbol)
echo -e '\u0416' # Capital "ZHE" (Cyrillic letter)
echo -e '\u2708' # Airplane (Dingbat font) symbol
echo -e '\u2622' # Radioactivity trefoil
echo -e "The amplifier circuit requires a 100 \u2126 pull-up resistor."
unicode_var='\u2640'
echo -e $unicode_var # Female symbol
printf "$unicode_var \n" # Female symbol, with newline
# And for something a bit more elaborate . . .
# We can store Unicode symbols in an associative array,
#+ then retrieve them by name.
# Run this in a gnome-terminal or a terminal with a large, bold font
#+ for better legibility.
declare -A symbol # Associative array.
symbol[script_E]='\u2130'
symbol[script_F]='\u2131'
symbol[script_J]='\u2110'
symbol[script_M]='\u2133'
symbol[Rx]='\u211E'
symbol[TEL]='\u2121'
symbol[FAX]='\u213B'
symbol[care_of]='\u2105'
symbol[account]='\u2100'
symbol[trademark]='\u2122'
echo -ne "${symbol[script_E]} "
echo -ne "${symbol[script_F]} "
echo -ne "${symbol[script_J]} "
echo -ne "${symbol[script_M]} "
echo -ne "${symbol[Rx]} "
echo -ne "${symbol[TEL]} "
echo -ne "${symbol[FAX]} "
echo -ne "${symbol[care_of]} "
echo -ne "${symbol[account]} "
echo -ne "${symbol[trademark]} "
echo</programlisting></para>
<note>
<para>The above example uses the
<link linkend="strq"><command>$' ... '</command></link>
<firstterm>string-expansion</firstterm> construct.</para>
</note>
</listitem>
<listitem>
<para><anchor id="lastpiperef"/></para>
<para>When the <replaceable>lastpipe</replaceable> shell option
is set, the last command in a <link
linkend="piperef">pipe</link> <emphasis>doesn't run in a
subshell</emphasis>.</para>
<example id="lastpipeopt">
<title>Piping input to a <link
linkend="readref">read</link></title>
<programlisting>&lastpipeopt;</programlisting>
</example>
<para>This option offers possible <quote>fixups</quote>
for these example scripts:
<xref linkend="badread"/> and
<xref linkend="readpipe"/>.</para>
</listitem>
<listitem>
<para>Negative <link linkend="arrayref">array</link> indices
permit counting backwards from the end of an array.</para>
<example id="negarray">
<title>Negative array indices</title>
<programlisting>&negarray;</programlisting>
</example>
</listitem>
<listitem>
<para><link linkend="substrextr01">Substring extraction</link>
uses a negative <firstterm>length</firstterm> parameter to
specify an offset from the <firstterm>end</firstterm> of the
target string.</para>
<example id="negoffset">
<title>Negative parameter in string-extraction
construct</title>
<programlisting>&negoffset;</programlisting>
</example>
</listitem>
</itemizedlist>
</sect2> <!-- Bash, Version 4.2 -->
</sect1> <!-- Bash, Version 4 -->
</chapter> <!-- Bash, versions 2, 3, and 4 -->
</part> <!-- Part 5 (Advanced Topics) -->
<chapter id="endnotes">
<title>Endnotes</title>
<sect1 id="authorsnote">
<title>Author's Note</title>
<epigraph>
<para><foreignphrase>doce ut discas</foreignphrase></para>
<para>(Teach, that you yourself may learn.)</para>
</epigraph>
<para>How did I come to write a scripting book? It's a strange
tale. It seems that a few 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 the subject. I was looking for a
book that would take difficult concepts, turn them inside out, and
explain them in excruciating detail, with well-commented examples.
<footnote><para>This is the notorious <emphasis>flog
it to death</emphasis> technique that works so well
with slow learners, eccentrics, odd ducks, fools and
geniuses.</para></footnote>
In fact, I was looking for <emphasis>this very book</emphasis>,
or something very much like it. Unfortunately, <link
linkend="kochanref">it didn't exist</link>, and if I wanted it,
I'd have to write it. And so, here we are, folks.</para>
<para>That reminds me of the apocryphal story about a 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 very 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 obsessed by it,
driven by it . . . and somehow I can't help admiring the old
coot.</para>
</sect1> <!-- Author's Note -->
<sect1 id="aboutauthor">
<title>About the Author</title>
<subtitle>Who is this guy anyhow?</subtitle>
<para><anchor id="nocreds"/>The author claims no credentials or
special qualifications,
<footnote><para>In fact, he has no credentials or special
qualifications. He's a school dropout with no formal credentials
or professional experience whatsoever. None. Zero. Nada. Aside
from the <emphasis>ABS Guide</emphasis>, his major claim to
fame is a First Place in the sack race at the Colfax Elementary
School Field Day in June, 1958.</para></footnote>
other than a compulsion to write.
<footnote><para>Those who can, do. Those who can't . . . get an
MCSE.</para></footnote>
</para>
<para>This book is somewhat of a departure from his other major work,
<ulink url="http://bash.deta.in/hmw60.zip">
HOW-2 Meet Women: The Shy Man's Guide to
Relationships</ulink>. He has also written the <ulink
url="http://tldp.org/HOWTO/Software-Building-HOWTO.html">Software-Building
HOWTO</ulink>. Of late, he has been trying his
(heavy) hand at fiction: <ulink
url="http://bash.deta.in/dave-dawson-over-berlin.epub">Dave Dawson
Over Berlin (First Installment)</ulink>
<ulink url="http://bash.deta.in/dave-dawson-over-berlin.II.epub">Dave Dawson
Over Berlin (Second Installment)</ulink> and
<ulink url="http://bash.deta.in/dave-dawson-over-berlin.III.epub">Dave Dawson
Over Berlin (Third Installment)</ulink>
. He also has a few
<emphasis>Instructables</emphasis> (<ulink
url="http://www.instructables.com/id/Arduino-Morse-Code-Shield/">here</ulink>,
<ulink
url="http://www.instructables.com/id/Haywired-Hackduino/">here</ulink>,
<ulink
url="http://www.instructables.com/id/Arduino-DIY-SD-Card-Logging-Shield/">here</ulink>,
<ulink
url="http://www.instructables.com/id/Binguino-An-Arduino-based-Bingo-Number-Generato/">here</ulink>,
<ulink
url="http://www.instructables.com/id/The-Raspberry-Pi-Lapdock-Connection/">here</ulink>,
<ulink
url="http://www.instructables.com/id/The-Raspberry-Pi-Arduino-Connection/">here</ulink>, and
<ulink
url="http://www.instructables.com/id/Switchable-Dual-Voltage-33v5v-Hacduino/">here</ulink>
to his (dis)credit.</para>
<para>A Linux user since 1995 (Slackware 2.2, kernel 1.2.1),
the author has emitted a few
software truffles, including the <ulink
url="http://ibiblio.org/pub/Linux/utils/file/cruft-0.2.tar.gz">cruft</ulink>
one-time pad encryption utility, the <ulink
url="http://ibiblio.org/pub/Linux/apps/financial/mcalc-1.6.tar.gz">mcalc</ulink>
mortgage calculator, the <ulink
url="http://ibiblio.org/pub/Linux/games/amusements/judge-1.0.tar.gz">judge</ulink>
Scrabble&reg; adjudicator, the <ulink
url="http://ibiblio.org/pub/Linux/libs/yawl-0.3.2.tar.gz">yawl</ulink>
word gaming list package, and the <ulink
url="http://bash.deta.in/qky.README.html">Quacky</ulink>
anagramming gaming package. He got off to a rather shaky start in the
computer game -- programming FORTRAN IV on a CDC 3800 (on paper coding
pads, with occasional forays on a keypunch machine and a Friden
Flexowriter) -- and is not the least bit nostalgic for those
days.</para>
<para>Living in an out-of-the-way community with wife and orange
tabby, he cherishes human frailty, especially his own.
<footnote><para>Sometimes it seems as if he has spent
his entire life flouting conventional wisdom and defying the
sonorous Voice of Authority: <quote><emphasis>Hey, you
can't do that!</emphasis></quote></para></footnote>
</para>
</sect1> <!-- About the Author -->
<sect1 id="wherehelp">
<title>Where to Go For Help</title>
<para><ulink url="mailto:thegrendel.abs@gmail.com">The author</ulink>
is no longer supporting or updating this document. He will not
answer questions about this book or about general scripting topics.
</para>
<sidebar>
<para>If you need assistance with a schoolwork assignment,
read the pertinent sections of this and other reference works.
Do your best to solve the problem using your own wits and
resources. <emphasis>Please do not waste the author's time.</emphasis>
You will get neither help nor sympathy.
<footnote><para>Well, if you <emphasis>absolutely</emphasis>
insist, you can try modifying <xref linkend="homework"/> to suit
your purposes.</para></footnote>
</para>
<para>Likewise, kindly refrain from annoying the author
with solicitations, offers of employment, or <quote>business
opportunities.</quote> He is doing just fine, and requires
neither help nor sympathy, thank you.</para>
<para>Please note that the author will <emphasis>not</emphasis> answer
scripting questions for Sun/Solaris/Oracle or Apple systems. The
endarkened execs and the arachnoid corporate attorneys of
those particular outfits have been using litigation in a
predatory manner and/or as a weapon against the Open Source
Community. Any Solaris or Apple users needing scripting help
will therefore kindly direct their concerns to corporate
customer service.</para>
</sidebar>
<epigraph>
<para>... sophisticated in mechanism but possibly agile
operating under noises being extremely suppressed ...</para>
<para>--<emphasis>CI-300 printer manual</emphasis></para>
</epigraph>
</sect1> <!-- Where to Go For Help -->
<sect1 id="toolsused">
<title>Tools Used to Produce This Book</title>
<sect2 id="software-hardware">
<title>Hardware</title>
<para>A used IBM Thinkpad, model 760XL laptop (P166, 104 meg RAM)
running Red Hat 7.1/7.3. Sure, it's slow and has a funky
keyboard, but it beats the heck out of a No. 2 pencil and a
Big Chief tablet.</para>
<para>
<emphasis>Update:</emphasis> upgraded to a
770Z Thinkpad (P2-366, 192 meg RAM) running FC3. Anyone
feel like donating a later-model laptop to a starving writer
&lt;g&gt;?
</para>
<para>
<emphasis>Update:</emphasis> upgraded to a T61 Thinkpad
running Mandriva 2011. No longer starving &lt;g&gt;,
but not too proud to accept donations.
</para>
</sect2> <!-- Hardware -->
<sect2 id="software-printware">
<title>Software and Printware</title>
<orderedlist id="software-printware2" numeration="lowerroman">
<listitem>
<para>Bram Moolenaar's powerful SGML-aware <ulink
url="http://www.vim.org">vim</ulink> text editor.</para>
</listitem>
<listitem>
<para><ulink
url="http://www.netfolder.com/DSSSL/">OpenJade</ulink>,
a DSSSL rendering engine for converting SGML documents into other
formats.</para>
</listitem>
<listitem>
<para><ulink url="http://nwalsh.com/docbook/dsssl/"> Norman
Walsh's DSSSL stylesheets</ulink>.</para>
</listitem>
<listitem>
<para><citetitle pubwork="book">DocBook, The Definitive
Guide</citetitle>, by Norman Walsh and Leonard Muellner
(O'Reilly, ISBN 1-56592-580-7). This is still the standard
reference for anyone attempting to write a document in Docbook
SGML format.</para>
</listitem>
</orderedlist>
</sect2> <!-- Software and Printware -->
</sect1> <!-- Tools Used -->
<sect1 id="credits">
<title>Credits</title>
<para><emphasis>Community participation made this project
possible.</emphasis> The author gratefully acknowledges that
writing this book would have been unthinkable without
help and feedback from all you people out there.</para>
<para><ulink url="mailto:feloy@free.fr">Philippe Martin</ulink>
translated the first version (0.1) of 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 you can 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">St&eacute;phane
Chazelas</ulink> sent a long list of corrections, additions,
and example scripts. More than a contributor, he had, in effect,
for a while taken on the role of <emphasis>co-editor</emphasis>
for this document. <foreignphrase>Merci
beaucoup!</foreignphrase></para>
<para>Paulo Marcel Coelho Aragao offered many corrections, both major
and minor, and contributed quite a number of helpful
suggestions.</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 in the
preliminary version (0.1) of this document. 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 <link linkend="zfifo">example script</link>.</para>
<para>Many thanks to <ulink
url="mailto:mikaku@fiwix.org">Jordi Sanfeliu</ulink>
for giving permission to use his fine tree script (<xref
linkend="tree"/>), and to Rick Boivie for revising it.</para>
<para>Likewise, thanks to <ulink
url="mailto:charpov@cs.unh.edu">Michel Charpentier</ulink> for
permission to use his <link linkend="dcref">dc</link> factoring script
(<xref linkend="factr"/>).</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><ulink url="mailto:emmanuel.rouat@wanadoo.fr">Emmanuel
Rouat</ulink> suggested corrections and additions on <link
linkend="commandsubref">command substitution</link>,
<link linkend="aliasref">aliases</link>, and <link
linkend="pathmanagement">path management</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>Rick Boivie contributed the delightfully recursive
<emphasis>pb.sh</emphasis> script (<xref linkend="pbook"/>),
revised the <emphasis>tree.sh</emphasis> script (<xref
linkend="tree"/>), and suggested performance improvements
for the <emphasis>monthlypmt.sh</emphasis> script (<xref
linkend="monthlypmt"/>).</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>Oleg Philon sent suggestions concerning <link
linkend="cutref">cut</link> and <link
linkend="pidofref">pidof</link>.</para>
<para>Michael Zick extended the <link linkend="emptyarray">empty
array</link> example to demonstrate some surprising array
properties. He also contributed the <emphasis>isspammer</emphasis>
scripts (<xref linkend="isspammer"/> and <xref
linkend="isspammer2"/>).</para>
<para>Marc-Jano Knopp sent corrections and clarifications on DOS
batch files.</para>
<para>Hyun Jin Cha found several typos in the document in the
process of doing a Korean translation. Thanks for pointing
these out.</para>
<para>Andreas Abraham sent in a long list of typographical
errors and other corrections. Special thanks!</para>
<para>Others contributing scripts, making helpful suggestions, and
pointing out errors were Gabor Kiss, Leopold Toetsch,
Peter Tillier, Marcus Berglof, Tony Richardson, Nick Drage
(script ideas!), Rich Bartell, Jess Thrysoee, Adam Lazur, Bram
Moolenaar, Baris Cicek, Greg Keraunen, Keith Matthews, Sandro
Magi, Albert Reiner, Dim Segebart, Rory Winston, Lee Bigelow,
Wayne Pollock, <quote>jipe,</quote> <quote>bojster,</quote>
<quote>nyal,</quote> <quote>Hobbit,</quote> <quote>Ender,</quote>
<quote>Little Monster</quote> (Alexis), <quote>Mark,</quote>
<quote>Patsie,</quote> <quote>vladz,</quote> Peggy Russell,
Emilio Conti, Ian. D. Allen, Hans-Joerg Diers, Arun Giridhar,
Dennis Leeuw, Dan Jacobson, Aurelio Marinho Jargas, Edward
Scholtz, Jean Helou, Chris Martin, Lee Maschmeyer, Bruno Haible,
Wilbert Berendsen, Sebastien Godard, Bj&ouml;n Eriksson, John
MacDonald, John Lange, Joshua Tschida, Troy Engel, Manfred
Schwarb, Amit Singh, Bill Gradwohl, E. Choroba, David Lombard,
Jason Parker, Steve Parker, Bruce W. Clare, William Park, Vernia
Damiano, Mihai Maties, Mark Alexander, Jeremy Impson, Ken Fuchs,
Jared Martin, Frank Wang, Sylvain Fourmanoit, Matthew Sage,
Matthew Walker, Kenny Stauffer, Filip Moritz, Andrzej Stefanski,
Daniel Albers, Jeffrey Haemer, Stefano Palmeri, Nils Radtke,
Sigurd Solaas, Serghey Rodin, Jeroen Domburg, Alfredo Pironti,
Phil Braham, Bruno de Oliveira Schneider, Stefano Falsetto,
Chris Morgan, Walter Dnes, Linc Fessenden, Michael Iatrou, Pharis
Monalo, Jesse Gough, Fabian Kreutz, Mark Norman, Harald Koenig,
Dan Stromberg, Peter Knowles, Francisco Lobo, Mariusz Gniazdowski,
Sebastian Arming, Chetankumar Phulpagare, Benno Schulenberg,
Tedman Eng, Jochen DeSmet, Juan Nicolas Ruiz, Oliver Beckstein,
Achmed Darwish, Dotan Barak, Richard Neill, Albert Siersema,
Omair Eshkenazi, Geoff Lee, Graham Ewart, JuanJo Ciarlante,
Cliff Bamford, Nathan Coulter, Ramses Rodriguez Martinez,
Evgeniy Ivanov, Craig Barnes, George Dimitriu, Kevin LeBlanc,
Antonio Macchi, Tomas Pospisek, David Wheeler, Erik Brandsberg,
YongYe, Andreas K&uuml;hne, P&aacute;draig Brady, Joseph
Steinhauser, and David Lawyer (himself an author of four
HOWTOs).</para>
<para>My gratitude to <ulink url="mailto:chet@po.cwru.edu">Chet
Ramey</ulink> and Brian Fox for writing <firstterm>Bash</firstterm>,
and building into it elegant and powerful scripting
capabilities rivaling those of <firstterm>ksh</firstterm>.</para>
<para>Very special thanks to the hard-working volunteers at
the <ulink url="http://www.tldp.org">Linux Documentation
Project</ulink>. The LDP hosts a repository of Linux knowledge
and lore, and has, to a great extent, enabled the publication
of this book.</para>
<para>Thanks and appreciation to IBM, Red Hat, Google, the <ulink
url="http://www.fsf.org">Free Software Foundation</ulink>, and
all the good people fighting the good fight to keep Open Source
software free and open.</para>
<para>Belated thanks to my fourth grade teacher, Miss Spencer,
for emotional support and for convincing me that maybe, just
maybe I wasn't a total loss.</para>
<para>Thanks most of all to my wife, Anita, for her encouragement,
inspiration, and emotional support.</para>
</sect1> <!-- Credits -->
<sect1 id="disclaimer">
<title>Disclaimer</title>
<para>(This is a variant of the standard <ulink
url="http://www.tldp.org">LDP</ulink> disclaimer.)</para>
<para>No liability for the contents of this document can be
accepted. Use the concepts, examples and information at your
own risk. There may be errors, omissions, and inaccuracies
that could cause you to lose data, harm your system, or induce
involuntary electrocution, so <emphasis>proceed with appropriate
caution</emphasis>. The author takes no responsibility for any
damages, incidental or otherwise.</para>
<para>As it happens, it is highly unlikely that either you or
your system will suffer ill effects, aside from uncontrollable
hiccups. In fact, the <foreignphrase>raison
d'etre</foreignphrase> of this book is to enable its readers
to analyze shell scripts and determine whether they have <link
linkend="gotchas">unanticipated consequences</link>.</para>
</sect1> <!-- Disclaimer -->
</chapter> <!-- End Notes -->
<bibliography id="biblio">
<anchor id="biblioref"/>
<epigraph>
<para>Those who do not understand UNIX are condemned to reinvent it,
poorly.</para>
<para>--Henry Spencer</para>
<para><anchor id="denningref"/></para>
</epigraph>
<biblioentry>
<authorgroup>
<editor><firstname>Peter</firstname><surname>Denning</surname></editor>
</authorgroup>
<title>Computers Under Attack: Intruders, Worms, and Viruses</title>
<publisher>
<publishername>ACM Press</publishername>
</publisher>
<copyright>
<year>1990</year>
</copyright>
<isbn>0-201-53067-8</isbn>
<abstract><para>This compendium contains a couple of articles on
shell script viruses.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Ken</firstname><surname>Burtch</surname></author>
</authorgroup>
<title><ulink url="http://www.samspublishing.com/title/0672326426">Linux Shell Scripting with Bash</ulink></title>
<edition>1st edition</edition>
<publisher>
<publishername>Sams Publishing (Pearson)</publishername>
</publisher>
<copyright>
<year>2004</year>
</copyright>
<isbn>0672326426</isbn>
<abstract><para>
Covers much of the same material as the <emphasis>ABS
Guide</emphasis>, though in a different style.</para>
<para>*</para>
<para><anchor id="dgsedref"/></para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Daniel</firstname><surname>Goldman</surname></author>
</authorgroup>
<title><ulink url="http://www.sed-book.com/">Definitive Guide
to Sed</ulink></title>
<edition>1st edition</edition>
<copyright>
<year>2013</year>
</copyright>
<abstract><para>
This ebook is an excellent introduction to
<firstterm>sed</firstterm>. Rather than being a conversion from
a printed volume, it was specifically designed and formatted
for viewing on an ebook reader. Well-written, informative,
and useful as a reference as well as a tutorial. Highly
recommended.</para>
<para>*</para>
</abstract>
</biblioentry>
<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>
Unfolding the full power of shell scripting requires at least a passing
familiarity with <link linkend="sedref"><firstterm>sed</firstterm>
and <firstterm>awk</firstterm></link>. This is the classic
tutorial. It includes an excellent introduction to
<firstterm>Regular Expressions</firstterm>. Recommended.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Jeffrey</firstname><surname>Friedl</surname></author>
</authorgroup>
<title>Mastering Regular Expressions</title>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2002</year>
</copyright>
<isbn>0-596-00289-0</isbn>
<abstract><para>Still the best all-around reference on <link
linkend="regexref">Regular Expressions</link>.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Aeleen</firstname><surname>Frisch</surname></author>
</authorgroup>
<title>Essential System Administration</title>
<edition>3rd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2002</year>
</copyright>
<isbn>0-596-00343-9</isbn>
<abstract><para>This excellent manual provides a decent introduction
to shell scripting from a sys admin point of view. It includes
comprehensive explanations of the startup and initialization
scripts in a UNIX system.</para>
<para>*</para>
<para><anchor id="kochanref"/></para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Stephen</firstname><surname>Kochan</surname></author>
<author><firstname>Patrick</firstname><surname>Wood</surname></author>
</authorgroup>
<title>Unix Shell Programming</title>
<publisher>
<publishername>Hayden</publishername>
</publisher>
<copyright>
<year>1990</year>
</copyright>
<isbn>067248448X</isbn>
<abstract>
<para>Still considered a standard reference, though somewhat dated, and
a bit <quote>wooden</quote> stylistically speaking.
<footnote><para>It was hard to resist the obvious pun. No slight
intended, since the book is a pretty decent introduction to
the basic concepts of shell scripting.</para></footnote>
In fact, this book was the <firstterm>ABS Guide</firstterm> author's
first exposure to UNIX shell scripting, lo these many years ago.</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>Surprisingly 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 id="mayerref">
<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. Highly recommended, but unfortunately
out of print.</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>Pretty good treatment of 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 sadly deficient in its coverage of writing scripts 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>3rd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<publisher>
<publishername>Random House</publishername>
</publisher>
<copyright>
<year>2002</year>
</copyright>
<isbn>0-596-00330-7</isbn>
<abstract><para>Contains a couple of sections of very informative
in-depth articles on shell programming, but falls short of being
a self-teaching manual. It reproduces much of the <link
linkend="regexref">Regular Expressions</link> tutorial from the
Dougherty and Robbins book, above. The comprehensive coverage
of UNIX commands makes this book worthy of a place on your
bookshelf.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Clifford</firstname><surname>Pickover</surname></author>
</authorgroup>
<title>Computers, Pattern, Chaos, and Beauty</title>
<publisher>
<publishername>St. Martin's Press</publishername>
</publisher>
<copyright>
<year>1990</year>
</copyright>
<isbn>0-312-04123-3</isbn>
<abstract><para>A treasure trove of ideas and recipes for
computer-based exploration of mathematical oddities.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>George</firstname><surname>Polya</surname></author>
</authorgroup>
<title>How To Solve It</title>
<publisher>
<publishername>Princeton University Press</publishername>
</publisher>
<copyright>
<year>1973</year>
</copyright>
<isbn>0-691-02356-5</isbn>
<abstract><para>The classic tutorial on problem-solving methods
(algorithms), with special emphasis on how to teach them.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Chet</firstname><surname>Ramey</surname></author>
<author><firstname>Brian</firstname><surname>Fox</surname></author>
</authorgroup>
<title><ulink
url="http://www.network-theory.co.uk/bash/manual/">The GNU Bash Reference Manual</ulink></title>
<publisher>
<publishername>Network Theory Ltd</publishername>
</publisher>
<copyright>
<year>2003</year>
</copyright>
<isbn>0-9541617-7-7</isbn>
<abstract><para>This manual is the definitive reference for
GNU Bash. The authors of this manual, Chet Ramey and Brian Fox,
are the original developers of GNU Bash. For each copy sold,
the publisher donates $1 to the Free Software Foundation.</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,
especially if you're a sysadmin). A bargain at $4.95, but
unfortunately no longer available for free download.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Arnold</firstname><surname>Robbins</surname></author>
</authorgroup>
<title>Effective Awk Programming</title>
<publisher>
<publishername>Free Software Foundation / O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2000</year>
</copyright>
<isbn>1-882114-26-4</isbn>
<abstract>
<para>The absolute best <link linkend="awkref">awk</link>
tutorial and reference. The free electronic version of this
book is part of the <firstterm>awk</firstterm> documentation,
and printed copies of the latest version are available from
O'Reilly and Associates.</para>
<para>This book served as an inspiration for the author
of the <firstterm>ABS Guide</firstterm>.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Bill</firstname><surname>Rosenblatt</surname></author>
</authorgroup>
<title>Learning the Korn Shell</title>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>1993</year>
</copyright>
<isbn>1-56592-054-6</isbn>
<abstract>
<para><anchor id="kornshellref"/>This well-written book contains
some excellent pointers on shell scripting in general.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Paul</firstname><surname>Sheer</surname></author>
</authorgroup>
<title>LINUX: Rute User's Tutorial and Exposition</title>
<edition>1st edition</edition>
<publisher>
<publishername></publishername>
</publisher>
<copyright>
<year>2002</year>
</copyright>
<isbn>0-13-033351-4</isbn>
<abstract>
<para>Very detailed and readable introduction to Linux system
administration.</para>
<para>The book is available in print, or
<ulink
url="http://burks.brighton.ac.uk/burks/linux/rute/rute.htm">on-line</ulink>.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Ellen</firstname><surname>Siever</surname></author>
<author><surname>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.
It even has a Bash section.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<authorgroup>
<author><firstname>Dave</firstname><surname>Taylor</surname></author>
</authorgroup>
<title>Wicked Cool Shell Scripts: 101 Scripts for Linux, Mac OS X, and Unix Systems</title>
<edition>1st edition</edition>
<publisher>
<publishername>No Starch Press</publishername>
</publisher>
<copyright>
<year>2004</year>
</copyright>
<isbn>1-59327-012-7</isbn>
<abstract><para>Pretty much what the title promises . . .</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<title>The UNIX CD Bookshelf</title>
<edition>3rd edition</edition>
<publisher>
<publishername>O'Reilly and Associates</publishername>
</publisher>
<copyright>
<year>2003</year>
</copyright>
<isbn>0-596-00392-7</isbn>
<abstract><para>An array of seven UNIX books on CD ROM, including
<citetitle pubwork="book">UNIX Power Tools</citetitle>,
<citetitle pubwork="book">Sed and Awk</citetitle>, and <citetitle
pubwork="book">Learning the Korn Shell</citetitle>. A complete
set of all the UNIX references and tutorials you would ever need
at about $130. Buy this one, even if it means going into debt
and not paying the rent.</para>
<para>Update: Seems to have somehow fallen out of print.
Ah, well. You can still buy the dead-tree editions of these books.</para>
<para>*</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The O'Reilly books on Perl. (Actually,
<emphasis>any</emphasis> O'Reilly books.)</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>* * *</para>
<para><command>Other Resources</command></para>
</abstract>
</biblioentry>
<biblioentry>
<abstract><para>Fioretti, Marco, <quote>Scripting for X
Productivity,</quote> <ulink url="linuxjournal.com"><citetitle
pubwork="journal">Linux Journal</citetitle></ulink>, Issue 113,
September, 2003, pp. 86-9.</para></abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Ben Okopnik's well-written <citetitle
pubwork="article">introductory Bash scripting</citetitle>
articles in issues 53, 54, 55, 57, and 59 of the
<ulink url="http://www.linuxgazette.net"><citetitle
pubwork="journal">Linux Gazette</citetitle></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 <citetitle pubwork="article">Bash -
The GNU Shell</citetitle>, a two-part series published in issues 3
and 4 of the <ulink url="http://www.linuxjournal.com"><citetitle
pubwork="journal">Linux Journal</citetitle></ulink>, July-August
1994.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Mike G's <ulink
url="http://www.tldp.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><anchor id="bashfaq"/>Chet Ramey's
<ulink url="http://tiswww.case.edu/php/chet/bash/FAQ">Bash
FAQ</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="http://wooledge.org:8000/BashFAQ">
Greg's WIKI: Bash FAQ</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.shelldorado.com">SHELLdorado </ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://www.splode.com/~friedman/software/scripts/src/">Noah
Friedman's script site</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="http://bashcookbook.com/bashinfo/">Examples</ulink>
from the <citetitle pubwork="book">The Bash Scripting
Cookbook</citetitle>, by Albing, Vossen, and Newham.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Example shell scripts at <ulink
url="http://www.zazzybob.com">zazzybob</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Steve Parker's <ulink
url="http://steve-parker.org/sh/sh.shtml">Shell Programming
Stuff</ulink>. In fact, all of his shell scripting
books are highly recommended. See also Steve's <ulink
url="http://nixshell.wordpress.com/2011/07/13/arcade-games-written-in-a-shell-script/">Arcade
Games written in a shell script</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>An excellent collection of Bash scripting tips, tricks,
and resources at the <ulink
url="http://www.bash-hackers.org/wiki.doku.php">Bash Hackers
Wiki</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Giles Orr's <ulink
url="http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/">Bash-Prompt
HOWTO</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The <ulink
url="http://www.pixelbeat.org/cmdline.html"><emphasis>Pixelbeat</emphasis>
command-line reference</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Very nice <command>sed</command>,
<command>awk</command>, and regular expression tutorials at
<ulink url="http://www.grymoire.com/Unix/index.html">The UNIX
Grymoire</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The GNU <ulink
url="http://www.gnu.org/software/sed/manual/">sed</ulink>
and
<ulink url="http://www.gnu.org/software/gawk/manual/">
gawk</ulink> manuals. As you recall, <link
linkend="gnugawk">gawk</link> is the enhanced GNU version of
<command>awk</command>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Many interesting sed scripts at the <ulink
url="http://sed.sourceforge.net/grabbag/"> seder's grab bag</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Tips and tricks at <ulink
url="http://linuxreviews.org"> Linux Reviews</ulink>.</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>David Wheeler's <ulink
url="http://www.dwheeler.com/essays/filenames-in-shell.html">Filenames
in Shell</ulink> essay.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><quote>Shelltris</quote> and <quote>shellitaire</quote>
at <ulink url="http://www.shellscriptgames.com">Shell Script
Games</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>YongYe's wonderfully complex <ulink
url="http://bash.deta.in/Tetris_Game.sh">Tetris game
script</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Mark Komarinski's <ulink
url="http://www.tldp.org/HOWTO/Printing-Usage-HOWTO.html">Printing-Usage
HOWTO</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="http://www.linux-usb.org/USB-guide/book1.html">The
Linux USB subsystem</ulink> (helpful in writing scripts affecting
USB peripherals).</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>There is some nice material on <link
linkend="ioredirref">I/O redirection</link> in <ulink
url="http://sunsite.ualberta.ca/Documentation/Gnu/textutils-2.0/html_chapter/textutils_10.html">
chapter 10 of the textutils documentation</ulink> at the <ulink
url="http://sunsite.ualberta.ca/Documentation"> University of
Alberta site</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="mailto:humbubba@smarty.smart.net">Rick
Hohensee</ulink> has written the
<firstterm>osimpa</firstterm> i386 assembler
entirely as Bash scripts.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><firstterm>dgatwood</firstterm>
has a very nice <ulink url="http://www.shellscriptgames.com/">
shell script games</ulink> site, featuring a Tetris&reg;
clone and solitaire.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Aurelio Marinho Jargas has written a <ulink
url="http://txt2regex.sf.net">Regular expression
wizard</ulink>. He has also written an informative <ulink
url="http://guia-er.sf.net">book</ulink> on Regular Expressions,
in Portuguese.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="mailto:brtompkins@comcast.net">Ben
Tomkins</ulink> has created the <ulink
url="http://bashnavigator.sourceforge.net">
Bash Navigator</ulink> directory management tool.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink url="mailto:opengeometry@yahoo.ca">William Park</ulink>
has been working on a project
to incorporate certain <firstterm>Awk</firstterm> and
<firstterm>Python</firstterm> features into Bash. Among these is
a <firstterm>gdbm</firstterm> interface. He has released
<firstterm>bashdiff</firstterm>
on <ulink url="http://freshmeat.net">Freshmeat.net</ulink>. He
has an <ulink
url="http://linuxgazette.net/108/park.html">article</ulink>
in the November, 2004 issue of the <ulink
url="http://www.linuxgazette.net"><citetitle
pubwork="journal">Linux Gazette</citetitle></ulink>
on adding string functions to Bash, with a <ulink
url="http://linuxgazette.net/109/park.html">followup
article</ulink> in the December issue, and <ulink
url="http://linuxgazette.net/110/park.htm">yet another</ulink>
in the January, 2005 issue.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Peter Knowles has written an
<ulink url="http://booklistgensh.peterknowles.com/">elaborate
Bash script</ulink> that generates a book list on the <ulink
url="http://www.dottocomu.com/b/archives/002571.html">Sony
Librie</ulink> e-book reader. This useful tool facilitates
loading non-DRM user content on the <emphasis>Librie</emphasis>
(and the newer <emphasis>PRS-xxx-series</emphasis> devices).</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Tim Waugh's <ulink
url="http://cyberelk.net/tim/xmlto/">xmlto</ulink> is an
elaborate Bash script for converting Docbook XML documents to
other formats.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Philip Patterson's <ulink
url="http://www.gossiplabs.org">logforbash</ulink>
logging/debugging script.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><ulink
url="http://auctiongallery.sourceforge.net">AuctionGallery</ulink>,
an application for eBay <quote>power sellers</quote> coded
in Bash.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Of historical interest are Colin Needham's
<firstterm>original International Movie Database (IMDB)
reader polling scripts</firstterm>, which nicely illustrate
the use of <link linkend="awkref">awk</link> for string
parsing. Unfortunately, the URL link is broken.</para>
<para>---</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Fritz Mehner has written a <ulink
url="http://vim.sourceforge.net/scripts/script.php?script_id=365">bash-support
plugin</ulink> for the <firstterm>vim</firstterm> text editor.
He has also also come up with his own <ulink
url="http://lug.fh-swf.de/vim/vim-bash/StyleGuideShell.en.pdf">stylesheet
for Bash</ulink>. Compare it with the <link linkend="unofficialst">ABS Guide
Unofficial Stylesheet</link>.</para>
<para>---</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><emphasis>Penguin Pete</emphasis> has quite a number of
shell scripting tips and hints on <ulink
url="http://www.penguinpetes.com">his superb
site</ulink>. Highly recommended.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The excellent <citetitle pubwork="book"> Bash Reference
Manual</citetitle>, by Chet Ramey and Brian Fox, distributed as
part of the <firstterm>bash-2-doc</firstterm> package (available
as an <link linkend="rpmref">rpm</link>). See especially the
instructive example scripts in this package.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>John Lion's classic, <ulink
url="http://www.lemis.com/grog/Documentation/Lions/index.html">
<emphasis>A Commentary on the Sixth Edition UNIX Operating
System</emphasis></ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The <ulink
url="news:comp.unix.shell">comp.os.unix.shell</ulink>
newsgroup.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para><anchor id="ddlink"/></para>
<para>The <ulink
url="http://www.linuxquestions.org/questions/showthread.php?t=362506"><firstterm>dd</firstterm>
thread</ulink> on <ulink
url="http://www.linuxquestions.org">Linux Questions</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The
<ulink
url="http://www.newsville.com/cgi-bin/getfaq?file=comp.unix.shell/comp.unix.shell_FAQ_-_Answers_to_Frequently_Asked_Questions">comp.os.unix.shell
FAQ</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>Assorted comp.os.unix <ulink
url="http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.shell.html">
FAQs</ulink>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The <ulink
url="http://en.wikipedia.org/wiki/Dc_(Unix)"><firstterm>Wikipedia</firstterm>
article</ulink> covering <link linkend="dcref">dc</link>.</para>
</abstract>
</biblioentry>
<biblioentry>
<abstract>
<para>The <link linkend="manref">manpages</link> 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 <firstterm>texinfo</firstterm> documentation on
<command>bash</command>, <command>dd</command>,
<command>m4</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. Some are useful,
too. Have fun analyzing and running them.</para>
<example id="mailformat">
<title><firstterm>mailformat</firstterm>: Formatting an e-mail
message</title>
<programlisting>&mailformat;</programlisting>
</example>
<example id="rn">
<title><firstterm>rn</firstterm>: A simple-minded file renaming
utility</title>
<para>This script is a modification of <xref
linkend="lowercase"/>.</para>
<programlisting>&rn;</programlisting>
</example>
<example id="blankrename">
<title><firstterm>blank-rename</firstterm>: Renames filenames containing
blanks</title>
<para>This is an even simpler-minded version of previous script.</para>
<programlisting>&blankrename;</programlisting>
</example>
<example id="encryptedpw">
<title><firstterm>encryptedpw</firstterm>: Uploading to an ftp site,
using a locally encrypted password</title>
<programlisting>&encryptedpw;</programlisting>
</example>
<example id="copycd">
<title><firstterm>copy-cd</firstterm>: Copying a data CD</title>
<programlisting>&copycd;</programlisting>
</example>
<example id="collatz">
<title>Collatz series</title>
<programlisting>&collatz;</programlisting>
</example>
<para><anchor id="daysbetween0"/></para>
<example id="daysbetween">
<title><firstterm>days-between</firstterm>: Days between two
dates</title>
<programlisting>&daysbetween;</programlisting>
</example>
<example id="makedict">
<title>Making a <firstterm>dictionary</firstterm></title>
<programlisting>&makedict;</programlisting>
</example>
<para><anchor id="soundex0"/></para>
<example id="soundex">
<title>Soundex conversion</title>
<programlisting>&soundex;</programlisting>
</example>
<para><anchor id="liferef"/></para>
<example id="lifeslow">
<title><firstterm>Game of Life</firstterm></title>
<programlisting>&lifeslow;</programlisting>
</example>
<example id="gen0data">
<title>Data file for <firstterm>Game of Life</firstterm></title>
<programlisting>&gen0data;</programlisting>
</example>
<para>+++</para>
<para>The following script is by Mark Moraes of the University
of Toronto. See the file <filename>Moraes-COPYRIGHT</filename>
for permissions and restrictions. This file is included in the
combined <link linkend="where_tarball">HTML/source tarball</link>
of the <emphasis>ABS Guide</emphasis>.</para>
<example id="behead">
<title><firstterm>behead</firstterm>: Removing mail and news
message headers</title>
<programlisting>&behead;</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>
<para><anchor id="pw0"/></para>
<example id="pw">
<title><firstterm>password</firstterm>: Generating random
8-character passwords</title>
<programlisting>&pw;</programlisting>
</example>
<para>+</para>
<para><anchor id="zfifo"/>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><firstterm>fifo</firstterm>: Making daily backups, using
named pipes</title>
<programlisting>&fifo;</programlisting>
</example>
<para>+</para>
<para><anchor id="primes1"/></para>
<para>St&eacute;phane Chazelas used the following script to
demonstrate generating prime numbers without arrays.</para>
<para><anchor id="primes00"/></para>
<example id="primes">
<title>Generating prime numbers using the modulo operator</title>
<programlisting>&primes;</programlisting>
</example>
<para>+</para>
<para>Rick Boivie's revision of Jordi Sanfeliu's
<emphasis>tree</emphasis> script.</para>
<example id="tree">
<title><firstterm>tree</firstterm>: Displaying a directory tree</title>
<programlisting>&tree;</programlisting>
</example>
<para>Patsie's version of a directory <firstterm>tree</firstterm>
script.</para>
<example id="tree2">
<title><firstterm>tree2</firstterm>: Alternate directory tree script</title>
<programlisting>&tree2;</programlisting>
</example>
<para>Noah Friedman permitted use of his <emphasis>string
function</emphasis> script. It essentially reproduces some
of the <firstterm>C</firstterm>-library string manipulation
functions.</para>
<example id="string">
<title><firstterm>string functions</firstterm>: C-style string
functions</title>
<programlisting>&string;</programlisting>
</example>
<para>Michael Zick's complex array example uses the <link
linkend="md5sumref">md5sum</link> check sum command to encode directory
information.</para>
<example id="directoryinfo">
<title>Directory information</title>
<programlisting>&directoryinfo;</programlisting>
</example>
<para>St&eacute;phane Chazelas demonstrates object-oriented programming in a
Bash script.</para>
<para>Mariusz Gniazdowski contributed a <link linkend="hashref">hash</link>
library for use in scripts.</para>
<example id="hashlib">
<title>Library of hash functions</title>
<programlisting>&hashlib;</programlisting>
</example>
<para>Here is an example script using the foregoing hash library.</para>
<example id="hashexample">
<title>Colorizing text using hash functions</title>
<programlisting>&hashexample;</programlisting>
</example>
<para><anchor id="hashex2_0"/>An example illustrating the mechanics
of hashing, but from a different point of view.</para>
<example id="hashex2">
<title>More on hash functions</title>
<programlisting>&hashex2;</programlisting>
</example>
<para>Now for a script that installs and mounts
those cute USB keychain solid-state <quote>hard drives.</quote></para>
<example id="usbinst">
<title>Mounting USB keychain storage devices</title>
<programlisting>&usbinst;</programlisting>
</example>
<para>Converting a text file to HTML format.</para>
<example id="tohtml">
<title>Converting to HTML</title>
<programlisting>&tohtml;</programlisting>
</example>
<para>Here is something to warm the hearts of webmasters and mistresses:
a script that saves weblogs.</para>
<example id="archivweblogs">
<title>Preserving weblogs</title>
<programlisting>&archiveweblogs;</programlisting>
</example>
<para><anchor id="protectliteral0"/>How to keep the shell from
expanding and reinterpreting text strings.</para>
<example id="protectliteral">
<title>Protecting literal strings</title>
<programlisting>&protectliteral;</programlisting>
</example>
<para><anchor id="unprotectliteral0"/>But, what if you
<emphasis>want</emphasis> the shell to expand
and reinterpret strings?</para>
<example id="unprotectliteral">
<title>Unprotecting literal strings</title>
<programlisting>&unprotectliteral;</programlisting>
</example>
<para>This interesting script helps hunt down spammers.</para>
<para><anchor id="isspammer2_0"/></para>
<example id="isspammer2">
<title>Spammer Identification</title>
<programlisting>&isspammer2;</programlisting>
</example>
<para>Another anti-spam script.</para>
<para><anchor id="whx0"/></para>
<example id="whx">
<title>Spammer Hunt</title>
<programlisting>&whx;</programlisting>
</example>
<para><quote>Little Monster's</quote> front end to <link
linkend="wgetref">wget</link>.</para>
<example id="wgetter2">
<title>Making <firstterm>wget</firstterm> easier to use</title>
<programlisting>&wgetter2;</programlisting>
</example>
<example id="bashpodder">
<title>A <firstterm>podcasting</firstterm> script</title>
<programlisting>&bashpodder;</programlisting>
</example>
<example id="nightlybackup">
<title>Nightly backup to a firewire HD</title>
<programlisting>&nightlybackup;</programlisting>
</example>
<example id="cdll">
<title>An expanded <firstterm>cd</firstterm> command</title>
<programlisting>&cdll;</programlisting>
</example>
<example id="soundcardon">
<title>A soundcard setup script</title>
<programlisting>&soundcardon;</programlisting>
</example>
<para><anchor id="findsplit0"/></para>
<example id="findsplit">
<title>Locating split paragraphs in a text file</title>
<programlisting>&findsplit;</programlisting>
</example>
<para><anchor id="insertionsort0"/></para>
<example id="insertionsort">
<title>Insertion sort</title>
<programlisting>&insertionsort;</programlisting>
</example>
<example id="stddev">
<title>Standard Deviation</title>
<programlisting>&stddev;</programlisting>
</example>
<example id="padsw">
<title>A <firstterm>pad</firstterm> file generator for shareware
authors</title>
<programlisting>&padsw;</programlisting>
</example>
<example id="maned">
<title>A <firstterm>man page</firstterm> editor</title>
<programlisting>&maned;</programlisting>
</example>
<example id="petals">
<title>Petals Around the Rose</title>
<programlisting>&petals;</programlisting>
</example>
<example id="qky">
<title>Quacky: a Perquackey-type word game</title>
<programlisting>&qky;</programlisting>
</example>
<example id="nim">
<title>Nim</title>
<programlisting>&nim;</programlisting>
</example>
<example id="stopwatch">
<title>A command-line stopwatch</title>
<programlisting>&stopwatch;</programlisting>
</example>
<example id="homework">
<title>An all-purpose shell scripting homework assignment solution</title>
<programlisting>&homework;</programlisting>
</example>
<para><anchor id="ktour0"/></para>
<example id="ktour">
<title>The Knight's Tour</title>
<programlisting>&ktour;</programlisting>
</example>
<example id="msquare">
<title>Magic Squares</title>
<programlisting>&msquare;</programlisting>
</example>
<example id="fifteen">
<title>Fifteen Puzzle</title>
<programlisting>&fifteen;</programlisting>
</example>
<para><anchor id="hanoi2ref"/></para>
<example id="hanoi2">
<title><firstterm>The Towers of Hanoi, graphic
version</firstterm></title>
<programlisting>&hanoi2;</programlisting>
</example>
<para><anchor id="hanoi2aref"/></para>
<example id="hanoi2a">
<title><firstterm>The Towers of Hanoi, alternate graphic
version</firstterm></title>
<programlisting>&hanoi2a;</programlisting>
</example>
<example id="usegetopt">
<title>An alternate version of the
<link linkend="getoptsimple">getopt-simple.sh</link> script</title>
<programlisting>&usegetopt;</programlisting>
</example>
<example id="usegetopt2">
<title>The version of the
<firstterm>UseGetOpt.sh</firstterm> example used in the <link
linkend="tabexpansion">Tab Expansion appendix</link></title>
<programlisting>&usegetopt2;</programlisting>
</example>
<example id="showallc">
<title>Cycling through all the possible color backgrounds</title>
<programlisting>&showallc;</programlisting>
</example>
<example id="samorse">
<title>Morse Code Practice</title>
<programlisting>&samorse;</programlisting>
</example>
<example id="base64">
<title>Base64 encoding/decoding</title>
<programlisting>&base64;</programlisting>
</example>
<example id="sedappend">
<title>Inserting text in a file using
<firstterm>sed</firstterm></title>
<programlisting>&sedappend;</programlisting>
</example>
<example id="gronsfeld">
<title>The Gronsfeld Cipher</title>
<programlisting>&gronsfeld;</programlisting>
</example>
<example id="bingo">
<title>Bingo Number Generator</title>
<programlisting>&bingo;</programlisting>
</example>
<para><anchor id="basicsrev0"/>To end this section, a review of the
basics . . . and more.</para>
<example id="basicsreviewed">
<title>Basics Reviewed</title>
<programlisting>&basicsreviewed;</programlisting>
</example>
<example id="testexectime">
<title>Testing execution times of various commands</title>
<programlisting>&testexectime;</programlisting>
</example>
<example id="assocarrtest">
<title>Associative arrays vs. conventional arrays (execution
times)</title>
<programlisting>&assocarrtest;</programlisting>
</example>
</appendix>
<!-- End Contributed Scripts appendix -->
<appendix id="refcards">
<title>Reference Cards</title>
<para>The following reference cards provide a useful
<emphasis>summary</emphasis> of certain scripting concepts.
The foregoing text treats these matters in more depth, as well as
giving usage examples.</para>
<para><anchor id="specshvartab"/></para>
<table pgwide="0">
<title>Special Shell Variables</title>
<tgroup cols="2">
<thead>
<row>
<entry>Variable</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>$0</option></entry>
<entry>Filename of script</entry>
</row>
<row>
<entry><option>$1</option></entry>
<entry>Positional parameter #1</entry>
</row>
<row>
<entry><option>$2 - $9</option></entry>
<entry>Positional parameters #2 - #9</entry>
</row>
<row>
<entry><option>${10}</option></entry>
<entry>Positional parameter #10</entry>
</row>
<row>
<entry><option>$#</option></entry>
<entry>Number of positional parameters</entry>
</row>
<row>
<entry><option>"$*"</option></entry>
<entry>All the positional parameters (as a single word) *</entry>
</row>
<row>
<entry><option>"$@"</option></entry>
<entry>All the positional parameters (as separate strings)</entry>
</row>
<row>
<entry><option>${#*}</option></entry>
<entry>Number of positional parameters</entry>
</row>
<row>
<entry><option>${#@}</option></entry>
<entry>Number of positional parameters</entry>
</row>
<row>
<entry><option>$?</option></entry>
<entry>Return value</entry>
</row>
<row>
<entry><option>$$</option></entry>
<entry>Process ID (PID) of script</entry>
</row>
<row>
<entry><option>$-</option></entry>
<entry>Flags passed to script (using
<firstterm>set</firstterm>)</entry>
</row>
<row>
<entry><option>$_</option></entry>
<entry>Last argument of previous command</entry>
</row>
<row>
<entry><option>$!</option></entry>
<entry>Process ID (PID) of last job run in background</entry>
</row>
</tbody>
</tgroup>
</table>
<para><command>*</command> <emphasis>Must be quoted</emphasis>,
otherwise it defaults to
<varname>$@</varname>.</para>
<para><anchor id="bincomptab"/></para>
<table>
<title>TEST Operators: Binary Comparison</title>
<tgroup cols="5">
<thead>
<row>
<entry>Operator</entry>
<entry>Meaning</entry>
<entry>-----</entry>
<entry>Operator</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry></entry>
<entry></entry>
</row>
<row>
<entry><link linkend="icomparison1">Arithmetic
Comparison</link></entry>
<entry></entry>
<entry></entry>
<entry><link linkend="scomparison1">String
Comparison</link></entry>
<entry></entry>
</row>
<row>
<entry><option>-eq</option></entry>
<entry>Equal to</entry>
<entry></entry>
<entry><option>=</option></entry>
<entry>Equal to</entry>
</row>
<row>
<entry></entry>
<entry></entry>
<entry></entry>
<entry><option>==</option></entry>
<entry>Equal to</entry>
</row>
<row>
<entry><option>-ne</option></entry>
<entry>Not equal to</entry>
<entry></entry>
<entry><option>!=</option></entry>
<entry>Not equal to</entry>
</row>
<row>
<entry><option>-lt</option></entry>
<entry>Less than</entry>
<entry></entry>
<entry><option>\&lt;</option></entry>
<entry>Less than (<link linkend="asciidef">ASCII</link>) *</entry>
</row>
<row>
<entry><option>-le</option></entry>
<entry>Less than or equal to</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry><option>-gt</option></entry>
<entry>Greater than</entry>
<entry></entry>
<entry><option>\&gt;</option></entry>
<entry>Greater than (ASCII) *</entry>
</row>
<row>
<entry><option>-ge</option></entry>
<entry>Greater than or equal to</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry></entry>
<entry></entry>
<entry></entry>
<entry><option>-z</option></entry>
<entry>String is empty</entry>
</row>
<row>
<entry></entry>
<entry></entry>
<entry></entry>
<entry><option>-n</option></entry>
<entry>String is not empty</entry>
</row>
<row>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>Arithmetic Comparison</entry>
<entry><link linkend="dblprx">within double
parentheses</link> (( ... ))</entry>
</row>
<row>
<entry><option>&gt;</option></entry>
<entry>Greater than</entry>
</row>
<row>
<entry><option>&gt;=</option></entry>
<entry>Greater than or equal to</entry>
</row>
<row>
<entry><option>&lt;</option></entry>
<entry>Less than</entry>
</row>
<row>
<entry><option>&lt;=</option></entry>
<entry>Less than or equal to</entry>
</row>
</tbody>
</tgroup>
</table>
<para><command>*</command> <emphasis>If within a
double-bracket</emphasis> <token>[[ ... ]]</token> <emphasis>test construct,
then no escape</emphasis> <token>\</token> <emphasis>is
needed.</emphasis></para>
<para><anchor id="filestab"/></para>
<table>
<title>TEST Operators: Files</title>
<tgroup cols="5">
<thead>
<row>
<entry>Operator</entry>
<entry>Tests Whether</entry>
<entry>-----</entry>
<entry>Operator</entry>
<entry>Tests Whether</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>-e</option></entry>
<entry>File exists</entry>
<entry></entry>
<entry><option>-s</option></entry>
<entry>File is not zero size</entry>
</row>
<row>
<entry><option>-f</option></entry>
<entry>File is a <firstterm>regular</firstterm> file</entry>
</row>
<row>
<entry><option>-d</option></entry>
<entry>File is a <firstterm>directory</firstterm></entry>
<entry></entry>
<entry><option>-r</option></entry>
<entry>File has <firstterm>read</firstterm>
permission</entry>
</row>
<row>
<entry><option>-h</option></entry>
<entry>File is a <link linkend="symlinkref">symbolic link</link></entry>
<entry></entry>
<entry><option>-w</option></entry>
<entry>File has <firstterm>write</firstterm>
permission</entry>
</row>
<row>
<entry><option>-L</option></entry>
<entry>File is a <firstterm>symbolic link</firstterm></entry>
<entry></entry>
<entry><option>-x</option></entry>
<entry>File has <firstterm>execute</firstterm>
permission</entry>
</row>
<row>
<entry><option>-b</option></entry>
<entry>File is a <link linkend="blockdevref">block
device</link></entry>
</row>
<row>
<entry><option>-c</option></entry>
<entry>File is a <link linkend="chardevref">character
device</link></entry>
<entry></entry>
<entry><option>-g</option></entry>
<entry><firstterm>sgid</firstterm> flag set</entry>
</row>
<row>
<entry><option>-p</option></entry>
<entry>File is a <link linkend="piperef">pipe</link></entry>
<entry></entry>
<entry><option>-u</option></entry>
<entry><firstterm>suid</firstterm> flag set</entry>
</row>
<row>
<entry><option>-S</option></entry>
<entry>File is a <link linkend="socketref">socket</link></entry>
<entry></entry>
<entry><option>-k</option></entry>
<entry><quote>sticky bit</quote> set</entry>
</row>
<row>
<entry><option>-t</option></entry>
<entry>File is associated with a
<firstterm>terminal</firstterm></entry>
</row>
<row><entry></entry></row>
<row>
<entry><option>-N</option></entry>
<entry>File modified since it was last read</entry>
<entry></entry>
<entry><option>F1 -nt F2</option></entry>
<entry>File F1 is <emphasis>newer</emphasis> than F2 *</entry>
</row>
<row>
<entry><option>-O</option></entry>
<entry>You own the file</entry>
<entry></entry>
<entry><option>F1 -ot F2</option></entry>
<entry>File F1 is <emphasis>older</emphasis> than F2 *</entry>
</row>
<row>
<entry><option>-G</option></entry>
<entry><firstterm>Group id</firstterm> of file same as
yours</entry>
<entry></entry>
<entry><option>F1 -ef F2</option></entry>
<entry>Files F1 and F2 are <firstterm>hard links</firstterm>
to the same file *</entry>
</row>
<row><entry></entry></row>
<row>
<entry><option>!</option></entry>
<entry>NOT (inverts sense of above tests)</entry>
</row>
</tbody>
</tgroup>
</table>
<para><command>*</command> <firstterm>Binary</firstterm> operator
(requires two operands).</para>
<para><anchor id="parsubtab"/></para>
<table pgwide="0">
<title>Parameter Substitution and Expansion</title>
<tgroup cols="2">
<thead>
<row>
<entry>Expression</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>${var}</option></entry>
<entry>Value of <parameter>var</parameter> (same as
<parameter>$var</parameter>)</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${var-$DEFAULT}</option></entry>
<entry>If <parameter>var</parameter> not set, <link
linkend="evalref">evaluate</link> expression
as <parameter>$DEFAULT</parameter> *</entry>
</row>
<row>
<entry><option>${var:-$DEFAULT}</option></entry>
<entry>If <parameter>var</parameter> not set or is empty,
<firstterm>evaluate</firstterm> expression as
<parameter>$DEFAULT</parameter>
*</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${var=$DEFAULT}</option></entry>
<entry>If <parameter>var</parameter> not set, evaluate expression
as <parameter>$DEFAULT</parameter> *</entry>
</row>
<row>
<entry><option>${var:=$DEFAULT}</option></entry>
<entry>If <parameter>var</parameter> not set or is empty, evaluate expression
as <parameter>$DEFAULT</parameter> *</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${var+$OTHER}</option></entry>
<entry>If <parameter>var</parameter> set, evaluate expression as
<parameter>$OTHER</parameter>, otherwise as null string</entry>
</row>
<row>
<entry><option>${var:+$OTHER}</option></entry>
<entry>If <parameter>var</parameter> set, evaluate expression as
<parameter>$OTHER</parameter>, otherwise as null string</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${var?$ERR_MSG}</option></entry>
<entry>If <parameter>var</parameter> not set, print
<parameter>$ERR_MSG</parameter> and abort script
with an exit status of <errorcode>1</errorcode>.*</entry>
</row>
<row>
<entry><option>${var:?$ERR_MSG}</option></entry>
<entry>If <parameter>var</parameter> not set, print
<parameter>$ERR_MSG</parameter> and abort script
with an exit status of <errorcode>1</errorcode>.*</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${!varprefix*}</option></entry>
<entry>Matches all previously declared variables beginning with
<parameter>varprefix</parameter></entry>
</row>
<row>
<entry><option>${!varprefix@}</option></entry>
<entry>Matches all previously declared variables beginning with
<parameter>varprefix</parameter></entry>
</row>
</tbody>
</tgroup>
</table>
<para><command>*</command> If <parameter>var</parameter>
<emphasis>is</emphasis> set, evaluate the expression as
<parameter>$var</parameter> with no side-effects.</para>
<para><command># Note</command> that some of the above behavior
of operators has changed from earlier versions of Bash.</para>
<para><anchor id="stringopstab"/></para>
<table pgwide="0">
<title>String Operations</title>
<tgroup cols="2">
<thead>
<row>
<entry>Expression</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>${#string}</option></entry>
<entry>Length of <parameter>$string</parameter></entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${string:position}</option></entry>
<entry>Extract substring from <parameter>$string</parameter>
at <parameter>$position</parameter></entry>
</row>
<row>
<entry><option>${string:position:length}</option></entry>
<entry>Extract <parameter>$length</parameter>
characters substring from <parameter>$string</parameter>
at <parameter>$position</parameter> [zero-indexed,
first character is at position 0]</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${string#substring}</option></entry>
<entry>Strip shortest match of
<parameter>$substring</parameter> from front of
<parameter>$string</parameter></entry>
</row>
<row>
<entry><option>${string##substring}</option></entry>
<entry>Strip longest match of
<parameter>$substring</parameter> from front of
<parameter>$string</parameter></entry>
</row>
<row>
<entry><option>${string%substring}</option></entry>
<entry>Strip shortest match of
<parameter>$substring</parameter> from back of
<parameter>$string</parameter></entry>
</row>
<row>
<entry><option>${string%%substring}</option></entry>
<entry>Strip longest match of
<parameter>$substring</parameter> from back of
<parameter>$string</parameter></entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>${string/substring/replacement}</option></entry>
<entry>Replace first match of
<parameter>$substring</parameter> with
<parameter>$replacement</parameter></entry>
</row>
<row>
<entry><option>${string//substring/replacement}</option></entry>
<entry>Replace <emphasis>all</emphasis> matches of
<parameter>$substring</parameter> with
<parameter>$replacement</parameter></entry>
</row>
<row>
<entry><option>${string/#substring/replacement}</option></entry>
<entry>If <parameter>$substring</parameter>
matches <emphasis>front</emphasis> end of
<parameter>$string</parameter>, substitute
<parameter>$replacement</parameter> for
<parameter>$substring</parameter></entry>
</row>
<row>
<entry><option>${string/%substring/replacement}</option></entry>
<entry>If <parameter>$substring</parameter>
matches <emphasis>back</emphasis> end of
<parameter>$string</parameter>, substitute
<parameter>$replacement</parameter> for
<parameter>$substring</parameter></entry>
</row>
<row><entry></entry><entry></entry></row>
<row><entry></entry><entry></entry></row>
<row>
<entry><option>expr match "$string" '$substring'</option></entry>
<entry>Length of matching <parameter>$substring</parameter>*
at beginning of <parameter>$string</parameter></entry>
</row>
<row>
<entry><option>expr "$string" : '$substring'</option></entry>
<entry>Length of matching <parameter>$substring</parameter>*
at beginning of <parameter>$string</parameter></entry>
</row>
<row>
<entry><option>expr index "$string" $substring</option></entry>
<entry>Numerical position in <parameter>$string</parameter>
of first character in <parameter>$substring</parameter>*
that matches [0 if no match, first character counts as
position 1]</entry>
</row>
<row>
<entry><option>expr substr $string $position
$length</option></entry>
<entry>Extract <parameter>$length</parameter> characters
from <parameter>$string</parameter> starting at
<parameter>$position</parameter> [0 if no match, first
character counts as position 1]</entry>
</row>
<row>
<entry><option>expr match "$string"
'\($substring\)'</option></entry>
<entry>Extract <parameter>$substring</parameter>*, searching
from beginning of <parameter>$string</parameter></entry>
</row>
<row>
<entry><option>expr "$string" :
'\($substring\)'</option></entry>
<entry>Extract <parameter>$substring</parameter>* , searching
from beginning of <parameter>$string</parameter></entry>
</row>
<row>
<entry><option>expr match "$string"
'.*\($substring\)'</option></entry>
<entry>Extract <parameter>$substring</parameter>*, searching
from end of <parameter>$string</parameter></entry>
</row>
<row>
<entry><option>expr "$string" :
'.*\($substring\)'</option></entry>
<entry>Extract <parameter>$substring</parameter>*, searching
from end of <parameter>$string</parameter></entry>
</row>
</tbody>
</tgroup>
</table>
<para><command>*</command> Where <parameter>$substring</parameter> is a
<link linkend="regexref">Regular Expression</link>.</para>
<para><anchor id="misctab"/></para>
<table pgwide="0">
<title>Miscellaneous Constructs</title>
<tgroup cols="2">
<thead>
<row>
<entry>Expression</entry>
<entry>Interpretation</entry>
</row>
</thead>
<tbody>
<row><entry></entry><entry></entry></row>
<row>
<entry><link linkend="bracketsref">Brackets</link></entry><entry></entry>
</row>
<row>
<entry><option>if [ CONDITION ]</option></entry>
<entry><link linkend="leftbracket">Test construct</link></entry>
</row>
<row>
<entry><option>if [[ CONDITION ]]</option></entry>
<entry><link linkend="dblbrackets">Extended test construct</link></entry>
</row>
<row>
<entry><option>Array[1]=element1</option></entry>
<entry><link linkend="arrayref">Array initialization</link></entry>
</row>
<row>
<entry><option> [a-z]</option></entry>
<entry><link linkend="bracketsref">Range of
characters</link> within a <link linkend="regexref">Regular
Expression</link></entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry>Curly Brackets</entry><entry></entry>
</row>
<row>
<entry><option>${variable}</option></entry>
<entry><link linkend="paramsubref">Parameter substitution</link></entry>
</row>
<row>
<entry><option>${!variable}</option></entry>
<entry><link linkend="ivrref">Indirect variable reference</link></entry>
</row>
<row>
<entry><option>{ command1; command2; . . . commandN; }</option></entry>
<entry><link linkend="codeblockref">Block of code</link></entry>
</row>
<row>
<entry><option>{string1,string2,string3,...}</option></entry>
<entry><link linkend="braceexpref">Brace expansion</link></entry>
</row>
<row>
<entry><option>{a..z}</option></entry>
<entry><link linkend="braceexpref3">Extended brace expansion</link></entry>
</row>
<row>
<entry><option>{}</option></entry>
<entry>Text replacement, after <link
linkend="curlybracketsref">find</link> and <link
linkend="xargscurlyref">xargs</link></entry>
</row>
<row><entry></entry><entry></entry></row>
<row><entry></entry><entry></entry></row>
<row>
<entry><link linkend="parensref">Parentheses</link></entry><entry></entry>
</row>
<row>
<entry><option>( command1; command2 )</option></entry>
<entry>Command group executed within a <link
linkend="subshellsref">subshell</link></entry>
</row>
<row>
<entry><option>Array=(element1 element2 element3)</option></entry>
<entry><link linkend="arrayinit0">Array initialization</link></entry>
</row>
<row>
<entry><option>result=$(COMMAND)</option></entry>
<entry><link linkend="csparens">Command substitution</link>,
new style</entry>
</row>
<row>
<entry><option>&gt;(COMMAND)</option></entry>
<entry><link linkend="processsubref">Process substitution</link></entry>
</row>
<row>
<entry><option>&lt;(COMMAND)</option></entry>
<entry>Process substitution</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><link linkend="dblparens">Double Parentheses</link></entry><entry></entry>
</row>
<row>
<entry><option>(( var = 78 ))</option></entry>
<entry><link linkend="dblparensref">Integer arithmetic</link></entry>
</row>
<row>
<entry><option>var=$(( 20 + 5 ))</option></entry>
<entry>Integer arithmetic, with variable assignment</entry>
</row>
<row>
<entry><option>(( var++ ))</option></entry>
<entry><firstterm>C-style</firstterm> <link
linkend="plusplusref"> variable increment</link></entry>
</row>
<row>
<entry><option>(( var-- ))</option></entry>
<entry><firstterm>C-style</firstterm> <link
linkend="plusplusref"> variable decrement</link></entry>
</row>
<row>
<entry><option>(( var0 = var1&lt;98?9:21 ))</option></entry>
<entry><firstterm>C-style</firstterm> <link
linkend="cstrinary"> ternary</link> operation</entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><link linkend="quotingref">Quoting</link></entry><entry></entry>
</row>
<row>
<entry><option>"$variable"</option></entry>
<entry><link linkend="dblquo">"Weak" quoting</link></entry>
</row>
<row>
<entry><option>'string'</option></entry>
<entry><link linkend="snglquo">'Strong' quoting</link></entry>
</row>
<row><entry></entry><entry></entry></row>
<row>
<entry><link linkend="backquotesref">Back Quotes</link></entry><entry></entry>
</row>
<row>
<entry><option>result=`COMMAND`</option></entry>
<entry><link linkend="commandsubref">Command
substitution</link>, classic style</entry>
</row>
</tbody>
</tgroup>
</table>
</appendix>
<!-- Reference Cards 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 <command>C</command>-style syntax</para>
<para>For all their differences, the two utilities share a similar
invocation syntax, use <link linkend="regexref">regular
expressions </link>, read input by default
from <filename>stdin</filename>, and output to
<filename>stdout</filename>. These are well-behaved UNIX tools,
and they work together well. The output from one can be piped
to the other, and their combined capabilities give shell scripts
some of the power of <link linkend="perlref">Perl</link>.</para>
<note><para>One important difference between the utilities is
that while shell scripts can easily pass arguments to sed, it
is more cumbersome for awk (see <xref linkend="coltotaler"/>
and <xref linkend="coltotaler2"/>).
</para></note>
<sect1>
<title>Sed</title>
<para><firstterm>Sed</firstterm> is a non-interactive
<footnote><para><firstterm>Sed</firstterm> executes without
user intervention.</para></footnote>
<command>s</command>tream <command>ed</command>itor. 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,
<firstterm>sed</firstterm> is usually one of several tool
components in a pipe.</para>
<para><firstterm>Sed</firstterm> determines which lines of
its input that it will operate on from the <firstterm>address
range</firstterm> 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 <firstterm>sed</firstterm> 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 <firstterm>sed</firstterm>
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>
<para><anchor id="sedbasictable"/></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>[address] i pattern Filename</option></entry>
<entry>insert</entry>
<entry>Insert pattern at address indicated in file Filename.
Usually used with <option>-i</option>
<replaceable>in-place</replaceable> option.</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>
(<firstterm>global</firstterm>) operator is appended to a
<firstterm>substitute</firstterm> command, the substitution
operates only on the <emphasis>first</emphasis> 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' $filename
# 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.)
#
# Operates on the text contained in file $filename.
</programlisting></para>
<para>In certain cases, a <firstterm>sed</firstterm> editing command will
not work with single quotes.</para>
<para><programlisting>
filename=file1.txt
pattern=BEGIN
sed "/^$pattern/d" "$filename" # Works as specified.
# sed '/^$pattern/d' "$filename" has unexpected results.
# In this instance, with strong quoting (' ... '),
#+ "$pattern" will not expand to "BEGIN".</programlisting></para>
<note><para><firstterm>Sed</firstterm> uses 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 may be omitted.</para></note>
<para><programlisting>sed -n '/xzy/p' $filename
# 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>
<para><anchor id="sedoptable"/></para>
<table>
<title>Examples of sed operators</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>echo "Working on it." | sed -e '1i How far are you along?'</option></entry>
<entry>Prints "How far are you along?" as first line,
"Working on it" as second.</entry>
</row>
<row>
<entry><option>5i 'Linux is great.' file.txt</option></entry>
<entry>Inserts 'Linux is great.' at line 5 of the file
file.txt.</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>
<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
<screen><userinput>The most important parts of any application are its GUI and sound effects</userinput></screen>
results in
<screen><computeroutput>The most important parts of any application are its and sound effects</computeroutput></screen></para>
<para>A backslash forces the <command>sed</command> replacement
command to continue on to the next line. This has the effect of
using the <firstterm>newline</firstterm> at the end of the first
line as the <firstterm>replacement string</firstterm>.
<programlisting>s/^ */\
/g</programlisting>
This substitution replaces line-beginning spaces with a
newline. The net result is to replace paragraph indents with a
blank line between paragraphs.</para>
<para>An address range followed by one or more operations may require
open and closed curly brackets, with appropriate newlines.
<programlisting>/[0-9A-Za-z]/,/^$/{
/^$/d
}</programlisting>
This deletes only the first of each set of consecutive blank
lines. That might be useful for single-spacing a text file,
but retaining the blank line(s) between paragraphs.</para>
<note><para>The usual delimiter that <firstterm>sed</firstterm> uses is
<token>/</token>. However, <emphasis>sed</emphasis> allows other
delimiters, such as <token>%</token>. This is useful when
<token>/</token> is part of a replacement string, as in a file pathname.
See <xref linkend="findstring"/> and <xref
linkend="stripc"/>.</para></note>
<para><anchor id="doublespace"/></para>
<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="tree2"/></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>
<listitem><para><xref linkend="wf"/></para></listitem>
<listitem><para><xref linkend="lifeslow"/></para></listitem>
<listitem><para><xref linkend="selfdocument"/></para></listitem>
<listitem><para><xref linkend="dictlookup"/></para></listitem>
<listitem><para><xref linkend="whx"/></para></listitem>
<listitem><para><xref linkend="bashpodder"/></para></listitem>
<listitem><para><xref linkend="tohtml"/></para></listitem>
<listitem><para><xref linkend="stopwatch"/></para></listitem>
<listitem><para><xref linkend="sedappend"/></para></listitem>
</orderedlist>
</para>
<para>For a more extensive treatment of <firstterm>sed</firstterm>,
refer to the <link linkend="dgsedref">pertinent references</link>
in the <xref linkend="biblio"/>.</para>
</sect1>
<!-- End sed primer -->
<sect1 id="awk">
<title>Awk</title>
<para><anchor id="awkref"/></para>
<para><firstterm>Awk</firstterm>
<footnote><para>Its name derives from the initials of its authors,
<command>A</command>ho, <command>W</command>einberg, and
<command>K</command>ernighan.</para></footnote>
is a full-featured text processing language with a syntax
reminiscent of <firstterm>C</firstterm>. While it possesses an
extensive set of operators and capabilities, we will cover only
a few of these here - the ones most useful in shell scripts.</para>
<para>Awk breaks each line of input passed to it into
<anchor id="fieldref2"/>
<link linkend="fieldref">fields</link>. By default, a field
is a string of consecutive characters delimited by <link
linkend="whitespaceref">whitespace</link>, though there are options
for changing this. Awk parses and operates on each separate
field. This makes it ideal for handling structured text files
-- especially tables -- data organized into consistent chunks,
such as rows and columns.</para>
<para><link linkend="snglquo">Strong quoting</link> and <link
linkend="codeblockref">curly brackets</link> enclose blocks of
awk code within a shell script.</para>
<para><programlisting># $1 is field #1, $2 is field #2, etc.
echo one two | awk '{print $1}'
# one
echo one two | awk '{print $2}'
# two
# But what is field #0 ($0)?
echo one two | awk '{print $0}'
# one two
# All the fields!
awk '{print $3}' $filename
# Prints field #3 of file $filename to stdout.
awk '{print $1 $5 $6}' $filename
# Prints fields #1, #5, and #6 of file $filename.
awk '{print $0}' $filename
# Prints the entire file!
# Same effect as: cat $filename . . . or . . . sed '' $filename</programlisting></para>
<para>We have just seen the awk <firstterm>print</firstterm> 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 <parameter>column_number</parameter> to
the running total of <parameter>total</parameter>>. Finally, to print
<quote>total</quote>, there is an <command>END</command> command
block, executed after the script has processed all its input.
<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>The following example illustrates how <command>awk</command> can
add text-parsing tools to a shell script.</para>
<example id="lettercount2">
<title>Counting Letter Occurrences</title>
<programlisting>&lettercount2;</programlisting>
</example>
<para>For simpler examples of awk within shell scripts, see:
<orderedlist>
<listitem><para><xref linkend="ex44"/></para></listitem>
<listitem><para><xref linkend="redir4"/></para></listitem>
<listitem><para><xref linkend="stripc"/></para></listitem>
<listitem><para><xref linkend="coltotaler"/></para></listitem>
<listitem><para><xref linkend="coltotaler2"/></para></listitem>
<listitem><para><xref linkend="coltotaler3"/></para></listitem>
<listitem><para><xref linkend="pidid"/></para></listitem>
<listitem><para><xref linkend="constat"/></para></listitem>
<listitem><para><xref linkend="fileinfo"/></para></listitem>
<listitem><para><xref linkend="blotout"/></para></listitem>
<listitem><para><xref linkend="seedingrandom"/></para></listitem>
<listitem><para><xref linkend="idelete"/></para></listitem>
<listitem><para><xref linkend="substringex"/></para></listitem>
<listitem><para><xref linkend="sumproduct"/></para></listitem>
<listitem><para><xref linkend="userlist"/></para></listitem>
<listitem><para><xref linkend="prasc"/></para></listitem>
<listitem><para><xref linkend="hypot"/></para></listitem>
<listitem><para><xref linkend="ascii3sh"/></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="pathmanagement">
<title>Parsing and Managing Pathnames</title>
<para>Emmanual Rouat contributed the following example of parsing
and transforming <firstterm>filenames</firstterm> and, in
particular, <link linkend="pathnameref">pathnames</link>. It draws
heavily on the functionality of <firstterm>sed</firstterm>.</para>
<para>
<programlisting>#!/usr/bin/env bash
#-----------------------------------------------------------
# Management of PATH, LD_LIBRARY_PATH, MANPATH variables...
# By Emmanuel Rouat &lt;no-email&gt;
# (Inspired by the bash documentation 'pathfuncs' and on
# discussions found on stackoverflow:
# http://stackoverflow.com/questions/370047/
# http://stackoverflow.com/questions/273909/#346860 )
# Last modified: Sat Sep 22 12:01:55 CEST 2012
#
# The following functions handle spaces correctly.
# These functions belong in .bash_profile rather than in
# .bashrc, I guess.
#
# The modular aspect of these functions should make it easy
# to expand them to handle path substitutions instead
# of path removal etc....
#
# See http://www.catonmat.net/blog/awk-one-liners-explained-part-two/
# (item 43) for an explanation of the 'duplicate-entries' removal
# (it's a nice trick!)
#-----------------------------------------------------------
# Show $@ (usually PATH) as list.
function p_show() { local p="$@" &amp;&amp; for p; do [[ ${!p} ]] &amp;&amp;
echo -e ${!p//:/\\n}; done }
# Filter out empty lines, multiple/trailing slashes, and duplicate entries.
function p_filter()
{ awk '/^[ \t]*$/ {next} {sub(/\/+$/, "");gsub(/\/+/, "/")}!x[$0]++' ;}
# Rebuild list of items into ':' separated word (PATH-like).
function p_build() { paste -sd: ;}
# Clean $1 (typically PATH) and rebuild it
function p_clean()
{ local p=${1} &amp;&amp; eval ${p}='$(p_show ${p} | p_filter | p_build)' ;}
# Remove $1 from $2 (found on stackoverflow, with modifications).
function p_rm()
{ local d=$(echo $1 | p_filter) p=${2} &amp;&amp;
eval ${p}='$(p_show ${p} | p_filter | grep -xv "${d}" | p_build)' ;}
# Same as previous, but filters on a pattern (dangerous...
#+ don't use 'bin' or '/' as pattern!).
function p_rmpat()
{ local d=$(echo $1 | p_filter) p=${2} &amp;&amp; eval ${p}='$(p_show ${p} |
p_filter | grep -v "${d}" | p_build)' ;}
# Delete $1 from $2 and append it cleanly.
function p_append()
{ local d=$(echo $1 | p_filter) p=${2} &amp;&amp; p_rm "${d}" ${p} &amp;&amp;
eval ${p}='$(p_show ${p} d | p_build)' ;}
# Delete $1 from $2 and prepend it cleanly.
function p_prepend()
{ local d=$(echo $1 | p_filter) p=${2} &amp;&amp; p_rm "${d}" ${p} &amp;&amp;
eval ${p}='$(p_show d ${p} | p_build)' ;}
# Some tests:
echo
MYPATH="/bin:/usr/bin/:/bin://bin/"
p_append "/project//my project/bin" MYPATH
echo "Append '/project//my project/bin' to '/bin:/usr/bin/:/bin://bin/'"
echo "(result should be: /bin:/usr/bin:/project/my project/bin)"
echo $MYPATH
echo
MYOTHERPATH="/bin:/usr/bin/:/bin:/project//my project/bin"
p_prepend "/project//my project/bin" MYOTHERPATH
echo "Prepend '/project//my project/bin' \
to '/bin:/usr/bin/:/bin:/project//my project/bin/'"
echo "(result should be: /project/my project/bin:/bin:/usr/bin)"
echo $MYOTHERPATH
echo
p_prepend "/project//my project/bin" FOOPATH # FOOPATH doesn't exist.
echo "Prepend '/project//my project/bin' to an unset variable"
echo "(result should be: /project/my project/bin)"
echo $FOOPATH
echo
BARPATH="/a:/b/://b c://a:/my local pub"
p_clean BARPATH
echo "Clean BARPATH='/a:/b/://b c://a:/my local pub'"
echo "(result should be: /a:/b:/b c:/my local pub)"
echo $BARPATH
</programlisting>
</para>
<para>***</para>
<para>David Wheeler kindly permitted me to use his instructive
examples.</para>
<para><programlisting>Doing it correctly: A quick summary
by David Wheeler
http://www.dwheeler.com/essays/filenames-in-shell.html
So, how can you process filenames correctly in shell? Here's a quick
summary about how to do it correctly, for the impatient who "just want the
answer". In short: Double-quote to use "$variable" instead of $variable,
set IFS to just newline and tab, prefix all globs/filenames so they cannot
begin with "-" when expanded, and use one of a few templates that work
correctly. Here are some of those templates that work correctly:
IFS="$(printf '\n\t')"
# Remove SPACE, so filenames with spaces work well.
# Correct glob use:
#+ always use "for" loop, prefix glob, check for existence:
for file in ./* ; do # Use "./*" ... NEVER bare "*" ...
if [ -e "$file" ] ; then # Make sure it isn't an empty match.
COMMAND ... "$file" ...
fi
done
# Correct glob use, but requires nonstandard bash extension.
shopt -s nullglob # Bash extension,
#+ so that empty glob matches will work.
for file in ./* ; do # Use "./*", NEVER bare "*"
COMMAND ... "$file" ...
done
# These handle all filenames correctly;
#+ can be unwieldy if COMMAND is large:
find ... -exec COMMAND... {} \;
find ... -exec COMMAND... {} \+ # If multiple files are okay for COMMAND.
# This skips filenames with control characters
#+ (including tab and newline).
IFS="$(printf '\n\t')"
controlchars="$(printf '*[\001-\037\177]*')"
for file in $(find . ! -name "$controlchars"') ; do
COMMAND "$file" ...
done
# Okay if filenames can't contain tabs or newlines --
#+ beware the assumption.
IFS="$(printf '\n\t')"
for file in $(find .) ; do
COMMAND "$file" ...
done
# Requires nonstandard but common extensions in find and xargs:
find . -print0 | xargs -0 COMMAND
# Requires nonstandard extensions to find and to shell (bash works).
# variables might not stay set once the loop ends:
find . -print0 | while IFS="" read -r -d "" file ; do ...
COMMAND "$file" # Use quoted "$file", not $file, everywhere.
done
# Requires nonstandard extensions to find and to shell (bash works).
# Underlying system must include named pipes (FIFOs)
#+ or the /dev/fd mechanism.
# In this version, variables *do* stay set after the loop ends,
# and you can read from stdin.
#+ (Change the 4 to another number if fd 4 is needed.)
while IFS="" read -r -d "" file &lt;&amp;4 ; do
COMMAND "$file" # Use quoted "$file" -- not $file, everywhere.
done 4&lt; &lt;(find . -print0)
# Named pipe version.
# Requires nonstandard extensions to find and to shell's read (bash ok).
# Underlying system must include named pipes (FIFOs).
# Again, in this version, variables *do* stay set after the loop ends,
# and you can read from stdin.
# (Change the 4 to something else if fd 4 needed).
mkfifo mypipe
find . -print0 > mypipe &amp;
while IFS="" read -r -d "" file &lt;&amp;4 ; do
COMMAND "$file" # Use quoted "$file", not $file, everywhere.
done 4&lt; mypipe</programlisting></para>
</appendix>
<!-- End path management appendix -->
<appendix id="exitcodes">
<title>Exit Codes With Special Meanings</title>
<para><anchor id="exitcodesref"/></para>
<table>
<title><firstterm>Reserved</firstterm> 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> and other impermissible operations</entry>
</row>
<row>
<entry><option>2</option></entry>
<entry>Misuse of shell builtins (according to Bash documentation)</entry>
<entry>empty_function() {}</entry>
<entry><link linkend="missingkeyword">Missing keyword</link>
or command, or permission problem (and <link
linkend="differr2"><firstterm>diff</firstterm> return code
on a failed binary file comparison</link>).</entry>
</row>
<row>
<entry><option>126</option></entry>
<entry>Command invoked cannot execute</entry>
<entry>/dev/null</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>illegal_command</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 <returnvalue>0 - 255</returnvalue> (see
first footnote)</entry>
</row>
<row>
<entry><option>128+n</option></entry>
<entry>Fatal error signal <quote>n</quote></entry>
<entry><firstterm>kill -9</firstterm> <varname>$PPID</varname> of script</entry>
<entry><userinput>$?</userinput> returns
<errorcode>137</errorcode> (128 + 9)</entry>
</row>
<row>
<entry><option>130</option></entry>
<entry>Script terminated by Control-C</entry>
<entry><emphasis>Ctl-C</emphasis></entry>
<entry>Control-C is fatal error signal
<errorcode>2</errorcode>, (130 = 128 + 2, see above)</entry>
</row>
<row>
<entry><option>255*</option></entry>
<entry>Exit status out of range</entry>
<entry>exit <returnvalue>-1</returnvalue></entry>
<entry><command>exit</command> takes only integer args in the
range <errorcode>0 - 255</errorcode></entry>
</row>
</tbody>
</tgroup>
</table>
<para>According to the above table, exit codes <errorcode>1 - 2,
126 - 165, and 255</errorcode>
<footnote><para><anchor id="excoor"/>Out of range exit values
can result in unexpected exit codes. An exit value
greater than <errorcode>255</errorcode> returns an
exit code <link linkend="moduloref">modulo</link>
<errorcode>256</errorcode>. For example, <firstterm>exit
3809</firstterm> gives an exit code of <errorcode>225</errorcode>
(3809 % 256 = 225).</para></footnote>
have special meanings, and should therefore be avoided for
user-specified exit parameters. Ending a script with <firstterm>exit
127</firstterm> would certainly cause confusion when troubleshooting
(is the error code a <quote>command not found</quote> or a
user-defined one?). However, many scripts use an <firstterm>exit
1</firstterm> as a general bailout-upon-error. Since exit code
<errorcode>1</errorcode> signifies so many possible errors,
it is not particularly useful in debugging.</para>
<para><anchor id="sysexitsref"/></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 for C and C++ programmers. A similar
standard for scripting might be appropriate. The author of
this document proposes restricting user-defined exit codes to
the range <returnvalue>64 - 113</returnvalue> (in addition to
<returnvalue>0</returnvalue>, for success), to conform with
the C/C++ standard. This would allot 50 valid codes, and make
troubleshooting scripts more straightforward.
<footnote><para>An update of <filename
class="headerfile">/usr/include/sysexits.h</filename>
allocates previously unused exit codes from <returnvalue>64
- 78</returnvalue>. It may be anticipated that the range of
unallotted exit codes will be further restricted in the future.
The author of this document will <emphasis>not</emphasis> do
fixups on the scripting examples to conform to the changing
standard. This should not cause any problems, since there
is no overlap or conflict in usage of exit codes between
compiled C/C++ binaries and shell scripts.</para></footnote>
All user-defined exit codes in the accompanying examples to
this document 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 <firstterm>sh</firstterm> prompt. Running the
<firstterm>C-shell</firstterm> or <firstterm>tcsh</firstterm>
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 St&eacute;phane Chazelas, and revised
by the document author</emphasis></para>
<para><anchor id="stdinoutdef"/></para>
<para>A command expects the first three <link linkend="fdref">file
descriptors</link> to be available. The first, <firstterm>fd
0</firstterm> (standard input, <filename>stdin</filename>),
is for reading. The other two (<firstterm>fd 1</firstterm>,
<filename>stdout</filename> and <firstterm>fd 2</firstterm>,
<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;&amp;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 >&amp;-</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 permissible). File descriptors may be reassigned
locally (for a command, a command group, a <link
linkend="subshellsref">subshell</link>, 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;&amp;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 &lt; /tmp/fifo1 &amp; exec 7> /tmp/fifo1
exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&amp;7)
exec 3>&amp;1
(
(
(
while read a; do echo "FIFO2: $a"; done &lt; /tmp/fifo2 | tee /dev/stderr \
| tee /dev/fd/4 | tee /dev/fd/5 | tee /dev/fd/6 >&amp;7 &amp; exec 3> /tmp/fifo2
echo 1st, to stdout
sleep 1
echo 2nd, to stderr >&amp;2
sleep 1
echo 3rd, to fd 3 >&amp;3
sleep 1
echo 4th, to fd 4 >&amp;4
sleep 1
echo 5th, to fd 5 >&amp;5
sleep 1
echo 6th, through a pipe | sed 's/.*/PIPE: &amp;, to fd 5/' >&amp;5
sleep 1
echo 7th, to fd 6 >&amp;6
sleep 1
echo 8th, to fd 7 >&amp;7
sleep 1
echo 9th, to fd 8 >&amp;8
) 4>&amp;1 >&amp;3 3>&amp;- | while read a; do echo "FD4: $a"; done 1>&amp;3 5>&amp;- 6>&amp;-
) 5>&amp;1 >&amp;3 | while read a; do echo "FD5: $a"; done 1>&amp;3 6>&amp;-
) 6>&amp;1 >&amp;3 | while read a; do echo "FD6: $a"; done 3>&amp;-
rm -f /tmp/fifo1 /tmp/fifo2
# For each command and subshell, figure out which fd points to what.
# Good luck!
exit 0</programlisting>
</para>
</appendix>
<!-- A Detailed Introduction to I/O and I/O Redirection -->
<appendix id="command-line-options">
<title>Command-Line Options</title>
<para>Many executables, whether binaries or script files, accept
options to modify their run-time behavior. For example: from
the command-line, typing <command>command -o</command>
would invoke <emphasis>command</emphasis>, with option
<option>o</option>.</para>
<sect1 id="standard-options">
<title>Standard Command-Line Options</title>
<para>Over time, there has evolved a loose standard for the
meanings of command-line option flags. The GNU utilities conform
more closely to this <quote>standard</quote> than older UNIX
utilities.</para>
<para>Traditionally, UNIX command-line options consist of a dash,
followed by one or more lowercase letters. The GNU utilities
added a double-dash, followed by a complete word or compound
word.</para>
<para>The two most widely-accepted options are:</para>
<itemizedlist id="widelyaccopt">
<listitem>
<para><option>-h</option></para>
<para><option>--help</option></para>
<para><firstterm>Help</firstterm>: Give usage message and exit.</para>
</listitem>
<listitem>
<para><option>-v</option></para>
<para><option>--version</option></para>
<para><firstterm>Version</firstterm>: Show program version and exit.</para>
</listitem>
</itemizedlist>
<para>Other common options are:</para>
<itemizedlist id="otheroptns">
<listitem>
<para><option>-a</option></para>
<para><option>--all</option></para>
<para><firstterm>All</firstterm>: show <emphasis>all</emphasis>
information or operate on <emphasis>all</emphasis> arguments.</para>
</listitem>
<listitem>
<para><option>-l</option></para>
<para><option>--list</option></para>
<para><firstterm>List</firstterm>: list files or arguments without
taking other action.</para>
</listitem>
<listitem>
<para><option>-o</option></para>
<para><firstterm>Output</firstterm> filename</para>
</listitem>
<listitem>
<para><option>-q</option></para>
<para><option>--quiet</option></para>
<para><firstterm>Quiet</firstterm>: suppress
<filename>stdout</filename>.</para>
</listitem>
<listitem>
<para><option>-r</option></para>
<para><option>-R</option></para>
<para><option>--recursive</option></para>
<para><firstterm>Recursive</firstterm>: Operate recursively (down
directory tree).</para>
</listitem>
<listitem>
<para><option>-v</option></para>
<para><option>--verbose</option></para>
<para><firstterm>Verbose</firstterm>: output additional information to
<filename>stdout</filename> or <filename>stderr</filename>.</para>
</listitem>
<listitem>
<para><option>-z</option></para>
<para><option>--compress</option></para>
<para><firstterm>Compress</firstterm>: apply compression (usually
<link linkend="gzipref">gzip</link>).</para>
</listitem>
</itemizedlist>
<para>However:</para>
<itemizedlist id="exceptionsopts">
<listitem>
<para>In <command>tar</command> and <command>gawk</command>:</para>
<para><option>-f</option></para>
<para><option>--file</option></para>
<para><firstterm>File</firstterm>: filename follows.</para>
</listitem>
<listitem>
<para>In <command>cp</command>, <command>mv</command>,
<command>rm</command>:</para>
<para><option>-f</option></para>
<para><option>--force</option></para>
<para><firstterm>Force</firstterm>: force overwrite of target file(s).</para>
</listitem>
</itemizedlist>
<caution><para>Many UNIX and Linux utilities deviate from this
<quote>standard,</quote> so it is dangerous to
<emphasis>assume</emphasis> that a given option will behave in a
standard way. Always check the man page for the command in question
when in doubt.</para></caution>
<para>A complete table of recommended options for the GNU utilities
is available at <ulink
url="http://www.gnu.org/prep/standards/">the GNU standards page</ulink>.</para>
</sect1>
<sect1 id="bash-options">
<title>Bash Command-Line Options</title>
<para><anchor id="clopts"/></para>
<para><firstterm>Bash</firstterm> itself has a number of command-line
options. Here are some of the more useful ones.</para>
<itemizedlist id="bash-commline-opts">
<listitem>
<para><option>-c</option></para>
<para><emphasis>Read commands from the following string and assign any
arguments to the <link linkend="posparamref">positional
parameters</link>.</emphasis></para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>bash -c 'set a b c d; IFS="+-;"; echo "$*"'</userinput>
<computeroutput>a+b+c+d</computeroutput>
</screen>
</para>
</listitem>
<listitem>
<para><option>-r</option></para>
<para><option>--restricted</option></para>
<para><emphasis>Runs the shell, or a script, in <link
linkend="restrictedshref">restricted mode</link>.</emphasis></para>
</listitem>
<listitem>
<para><option>--posix</option></para>
<para><emphasis>Forces Bash to conform to <link
linkend="posix2ref">POSIX</link> mode.</emphasis></para>
</listitem>
<listitem>
<para><option>--version</option></para>
<para><emphasis>Display Bash version information and
exit.</emphasis></para>
</listitem>
<listitem>
<para><option>--</option></para>
<para><emphasis>End of options. Anything further on the command
line is an argument, not an option.</emphasis></para>
</listitem>
</itemizedlist>
</sect1>
</appendix>
<!-- End Command-Line Options appendix -->
<appendix id="files">
<title> Important Files</title>
<variablelist id="filesref">
<title><anchor id="filesref1"/>startup files</title>
<varlistentry>
<term></term>
<listitem>
<para>These files contain the aliases and <link
linkend="envref">environmental variables</link>
made available to Bash running as a user shell and to all
Bash scripts invoked after system initialization.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/profile</filename></term>
<listitem>
<para>Systemwide defaults, mostly setting the environment
(all Bourne-type shells, not just Bash
<footnote><para>This does not apply to <command>csh</command>,
<command>tcsh</command>, and other shells not related to or
descended from the classic Bourne shell
(<command>sh</command>).</para></footnote>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/etc/bashrc</filename></term>
<listitem>
<para>systemwide functions and <link
linkend="aliasref">aliases</link> for Bash</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_profile</filename></term>
<listitem>
<para>user-specific Bash environmental default settings,
found in each user's home directory (the local counterpart
to <filename>/etc/profile</filename>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bashrc</filename></term>
<listitem>
<para>user-specific Bash init file, found in each user's home
directory (the local counterpart to
<filename>/etc/bashrc</filename>). Only interactive
shells and user scripts read this file. See
<xref linkend="sample-bashrc"/> for a sample
<filename>.bashrc</filename> file.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="logoutfileref">
<title><anchor id="logoutfileref1"/>logout file</title>
<varlistentry>
<term><filename><varname>$HOME</varname>/.bash_logout</filename></term>
<listitem>
<para>user-specific instruction file, found in
each user's home directory. Upon exit from a login (Bash)
shell, the commands in this file execute.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="datafilesref">
<title><anchor id="datafilesref1"/>data files</title>
<varlistentry>
<term><filename>/etc/passwd</filename></term>
<listitem>
<para>A listing of all the user accounts on the system,
their identities, their home directories, the groups they
belong to, and their default shell. Note that the user
passwords are <emphasis>not</emphasis>
stored in this file,
<footnote><para>In older versions of UNIX, passwords
<emphasis>were</emphasis> stored in
<filename>/etc/passwd</filename>, and that explains
the name of the file.</para></footnote>
but in <filename>/etc/shadow</filename> in encrypted form.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="sysconfref">
<title><anchor id="sysconfref1"/>system configuration files</title>
<varlistentry>
<term><filename>/etc/sysconfig/hwconf</filename></term>
<listitem>
<para>Listing and description of attached hardware devices.
This information is in text form and can be extracted and
parsed.</para>
<para>
<screen>
<prompt>bash$ </prompt><userinput>grep -A 5 AUDIO /etc/sysconfig/hwconf</userinput>
<computeroutput>class: AUDIO
bus: PCI
detached: 0
driver: snd-intel8x0
desc: "Intel Corporation 82801CA/CAM AC'97 Audio Controller"
vendorId: 8086</computeroutput>
</screen>
</para>
<note><para>This file is present on Red Hat and Fedora Core
installations, but may be missing from other
distros.</para></note>
</listitem>
</varlistentry>
</variablelist>
</appendix>
<!-- End Files appendix -->
<appendix id="systemdirs">
<title>Important System Directories</title>
<para>Sysadmins and anyone else writing administrative scripts
should be intimately familiar with the following system
directories.</para>
<itemizedlist>
<listitem>
<para><filename class="directory">/bin</filename></para>
<para>Binaries (executables). Basic system programs
and utilities (such as <command>bash</command>).</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/bin</filename>
<footnote>
<para>Some early UNIX systems had a fast, small-capacity fixed
disk (containing <filename class="directory">/</filename>,
the root partition), and a second drive which
was larger, but slower (containing <filename
class="directory">/usr</filename> and other
partitions). The most frequently used programs and
utilities therefore resided on the small-but-fast
drive, in <filename class="directory">/bin</filename>,
and the others on the slower drive, in <filename
class="directory">/usr/bin</filename>.</para>
<para>This likewise accounts for the split between
<filename class="directory">/sbin</filename> and
<filename class="directory">/usr/sbin</filename>,
<filename class="directory">/lib</filename> and <filename
class="directory">/usr/lib</filename>, etc.</para>
</footnote>
</para>
<para>More system binaries.</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/local/bin</filename></para>
<para>Miscellaneous binaries local to the particular machine.</para>
</listitem>
<listitem>
<para><filename class="directory">/sbin</filename></para>
<para>System binaries. Basic system administrative programs
and utilities (such as <command>fsck</command>).</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/sbin</filename></para>
<para>More system administrative programs and utilities.</para>
</listitem>
<listitem>
<para><filename class="directory">/etc</filename></para>
<para><firstterm>Et cetera</firstterm>. Systemwide configuration
scripts.</para>
<para>Of particular interest are the
<link
linkend="fstabref"><filename>/etc/fstab</filename></link>
(filesystem table),
<filename>/etc/mtab</filename>
(mounted filesystem table), and the <link
linkend="inittabref"><filename>/etc/inittab</filename></link>
files.</para>
</listitem>
<listitem>
<para><filename class="directory">/etc/rc.d</filename></para>
<para>Boot scripts, on Red Hat and derivative distributions
of Linux.</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/share/doc</filename></para>
<para>Documentation for installed packages.</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/man</filename></para>
<para>The systemwide <link linkend="manref">manpages</link>.</para>
</listitem>
<listitem>
<para><filename class="directory">/dev</filename></para>
<para>Device directory. Entries (but <emphasis>not</emphasis>
mount points) for physical and virtual devices.
See <xref linkend="devproc"/>.</para>
</listitem>
<listitem>
<para><filename class="directory">/proc</filename></para>
<para>Process directory. Contains information and statistics
about running processes and kernel parameters.
See <xref linkend="devproc"/>.</para>
</listitem>
<listitem>
<para><filename class="directory">/sys</filename></para>
<para>Systemwide device directory. Contains information and
statistics about device and device names. This is newly
added to Linux with the 2.6.X kernels.</para>
</listitem>
<listitem>
<para><filename class="directory">/mnt</filename></para>
<para><firstterm>Mount</firstterm>. Directory for mounting
hard drive partitions, such as <filename
class="directory">/mnt/dos</filename>, and physical
devices. In newer Linux distros, the <filename
class="directory">/media</filename> directory has taken
over as the preferred mount point for I/O devices.</para>
</listitem>
<listitem>
<para><filename class="directory">/media</filename></para>
<para>In newer Linux distros, the preferred mount point for
I/O devices, such as CD/DVD drives or USB flash drives.</para>
</listitem>
<listitem>
<para><filename class="directory">/var</filename></para>
<para><firstterm>Variable</firstterm> (changeable) system
files. This is a catchall <quote>scratchpad</quote>
directory for data generated while a Linux/UNIX machine
is running.</para>
</listitem>
<listitem>
<para><filename class="directory">/var/log</filename></para>
<para>Systemwide log files.</para>
</listitem>
<listitem>
<para><filename class="directory">/var/spool/mail</filename></para>
<para>User mail spool.</para>
</listitem>
<listitem>
<para><filename class="directory">/lib</filename></para>
<para>Systemwide library files.</para>
</listitem>
<listitem>
<para><filename class="directory">/usr/lib</filename></para>
<para>More systemwide library files.</para>
</listitem>
<listitem>
<para><filename class="directory">/tmp</filename></para>
<para>System temporary files.</para>
</listitem>
<listitem>
<para><filename class="directory">/boot</filename></para>
<para>System <firstterm>boot</firstterm> directory. The kernel,
module links, system map, and boot manager reside here.</para>
<warning><para>Altering files in this directory may result in an
unbootable system.</para></warning>
</listitem>
</itemizedlist>
</appendix>
<appendix id="tabexpansion">
&TABEXP;
</appendix>
<!-- End Tab Expansion appendix -->
<appendix id="localization">
<title>Localization</title>
<para>Localization is an undocumented Bash feature.</para>
<para><anchor id="localeref"/>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
# Script by St&eacute;phane Chazelas,
#+ modified by Bruno Haible, bugfixed by Alfredo Pironti.
. gettext.sh
E_CDERROR=65
error()
{
printf "$@" >&amp;2
exit $E_CDERROR
}
cd $var || error "`eval_gettext \"Can\'t cd to \\\$var.\"`"
# The triple backslashes (escapes) in front of $var needed
#+ "because eval_gettext expects a string
#+ where the variable values have not yet been substituted."
# -- per Bruno Haible
read -p "`gettext \"Enter the value: \"`" var
# ...
# ------------------------------------------------------------------
# Alfredo Pironti comments:
# This script has been modified to not use the $"..." syntax in
#+ favor of the "`gettext \"...\"`" syntax.
# This is ok, but with the new localized.sh program, the commands
#+ "bash -D filename" and "bash --dump-po-string filename"
#+ will produce no output
#+ (because those command are only searching for the $"..." strings)!
# The ONLY way to extract strings from the new file is to use the
# 'xgettext' program. However, the xgettext program is buggy.
# Note that 'xgettext' has another bug.
#
# The shell fragment:
# gettext -s "I like Bash"
# will be correctly extracted, but . . .
# xgettext -s "I like Bash"
# . . . fails!
# 'xgettext' will extract "-s" because
#+ the command only extracts the
#+ very first argument after the 'gettext' word.
# Escape characters:
#
# To localize a sentence like
# echo -e "Hello\tworld!"
#+ you must use
# echo -e "`gettext \"Hello\\tworld\"`"
# The "double escape character" before the `t' is needed because
#+ 'gettext' will search for a string like: 'Hello\tworld'
# This is because gettext will read one literal `\')
#+ and will output a string like "Bonjour\tmonde",
#+ so the 'echo' command will display the message correctly.
#
# You may not use
# echo "`gettext -e \"Hello\tworld\"`"
#+ due to the xgettext bug explained above.
# Let's localize the following shell fragment:
# echo "-h display help and exit"
#
# First, one could do this:
# echo "`gettext \"-h display help and exit\"`"
# This way 'xgettext' will work ok,
#+ but the 'gettext' program will read "-h" as an option!
#
# One solution could be
# echo "`gettext -- \"-h display help and exit\"`"
# This way 'gettext' will work,
#+ but 'xgettext' will extract "--", as referred to above.
#
# The workaround you may use to get this string localized is
# echo -e "`gettext \"\\0-h display help and exit\"`"
# We have added a \0 (NULL) at the beginning of the sentence.
# This way 'gettext' works correctly, as does 'xgettext.'
# Moreover, the NULL character won't change the behavior
#+ of the 'echo' command.
# ------------------------------------------------------------------</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>
<note>
<para>Bruno Haible points out:</para>
<para>Starting with gettext-0.12.2, <command>xgettext -o - localized.sh</command>
is recommended instead of <command>bash --dump-po-strings
localized.sh</command>, because <command>xgettext</command> . . .</para>
<para>1. understands the gettext and eval_gettext commands
(whereas bash --dump-po-strings understands only its deprecated
$"..." syntax)</para>
<para>2. can extract comments placed by the programmer, intended
to be read by the translator.</para>
<para>This shell code is then not specific to Bash any
more; it works the same way with Bash 1.x and other /bin/sh
implementations.</para>
</note>
<para>Now, build a <filename>language.po</filename>
file for each language that the script will be translated
into, specifying the <replaceable>msgstr</replaceable>. Alfredo
Pironti gives the following example:</para>
<para>fr.po:
<programlisting>#: a:6
msgid "Can't cd to $var."
msgstr "Impossible de se positionner dans le repertoire $var."
#: a:7
msgid "Enter the value: "
msgstr "Entrez la valeur : "
# The string are dumped with the variable names, not with the %s syntax,
#+ similar to C programs.
#+ This is a very cool feature if the programmer uses
#+ variable names that make sense!</programlisting>
</para>
<para>Then, run <link linkend="msgfmtref">msgfmt</link>.</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")" "$@" >&amp;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 set and
exported to the environment. This should be done within the
script itself.</para>
<para>---</para>
<para>This appendix written by St&eacute;phane Chazelas,
with modifications suggested by Alfredo Pironti,
and by Bruno Haible, maintainer of GNU <link
linkend="gettextref">gettext</link>.</para>
</appendix>
<!-- Localization -->
<appendix id="histcommands">
<title>History Commands</title>
<para>The Bash shell provides command-line tools for editing and
manipulating a user's <firstterm>command history</firstterm>. This
is primarily a convenience, a means of saving keystrokes.</para>
<para>Bash history commands:
<orderedlist>
<listitem><para><command>history</command></para></listitem>
<listitem><para><command>fc</command></para></listitem>
</orderedlist>
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>history</userinput>
<computeroutput> 1 mount /mnt/cdrom
2 cd /mnt/cdrom
3 ls
...</computeroutput>
</screen>
</para>
<para>Internal variables associated with Bash history commands:
<orderedlist>
<listitem><para>$HISTCMD</para></listitem>
<listitem><para>$HISTCONTROL</para></listitem>
<listitem><para>$HISTIGNORE</para></listitem>
<listitem><para>$HISTFILE</para></listitem>
<listitem><para>$HISTFILESIZE</para></listitem>
<listitem><para>$HISTSIZE</para></listitem>
<listitem><para>$HISTTIMEFORMAT (Bash, ver. 3.0 or later)</para></listitem>
<listitem><para>!!</para></listitem>
<listitem><para>!$</para></listitem>
<listitem><para>!#</para></listitem>
<listitem><para>!N</para></listitem>
<listitem><para>!-N</para></listitem>
<listitem><para>!STRING</para></listitem>
<listitem><para>!?STRING?</para></listitem>
<listitem><para>^STRING^string^</para></listitem>
</orderedlist>
</para>
<para>Unfortunately, the Bash history tools find no use in
scripting.</para>
<para><programlisting>#!/bin/bash
# history.sh
# A (vain) attempt to use the 'history' command in a script.
history # No output.
var=$(history); echo "$var" # $var is empty.
# History commands are, by default, disabled within a script.
# However, as dhw points out,
#+ set -o history
#+ enables the history mechanism.
set -o history
var=$(history); echo "$var" # 1 var=$(history)</programlisting>
</para>
<para>
<screen><prompt>bash$ </prompt><userinput>./history.sh</userinput>
<computeroutput>(no output)</computeroutput>
</screen>
</para>
<para>The <ulink
url="http://samrowe.com/wordpress/advancing-in-the-bash-shell/">Advancing
in the Bash Shell</ulink> site gives a good introduction to
the use of history commands in Bash.</para>
</appendix>
<!-- History Commands -->
<appendix id="sample-bashrc">
<title>Sample <filename>.bashrc</filename> and
<filename>.bash_profile</filename> Files</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><ulink url="mailto:emmanuel.rouat@wanadoo.fr">Emmanuel
Rouat</ulink> contributed the following very elaborate
<filename>.bashrc</filename> file, written for a Linux system.
He welcomes reader feedback on it.</para>
<para>Study the file carefully, and feel free to reuse code
snippets and functions from it in your own
<filename>.bashrc</filename> file or even in your scripts.</para>
<example id="bashrc">
<title>Sample <filename>.bashrc</filename> file</title>
<programlisting>&bashrc;</programlisting>
</example>
<para>And, here is a snippet from Andrzej Szelachowski's instructive
<filename>.bash_profile</filename> file.</para>
<example id="bashprof">
<title><filename>.bash_profile</filename> file</title>
<programlisting>&bashprof;</programlisting>
</example>
</appendix>
<!-- End Sample .bashrc File appendix -->
<appendix id="dosbatch">
<title>Converting DOS Batch Files to Shell Scripts</title>
<para><anchor id="dosbatch1"/></para>
<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 scripting ones.</para>
<table>
<title>Batch file keywords / variables / operators, and their shell equivalents</title>
<tgroup cols="3">
<thead>
<row>
<entry>Batch File Operator</entry>
<entry>Shell Script Equivalent</entry>
<entry>Meaning</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>%</option></entry>
<entry>$</entry>
<entry>command-line parameter prefix</entry>
</row>
<row>
<entry><option>/</option></entry>
<entry>-</entry>
<entry>command option flag</entry>
</row>
<row>
<entry><option>\</option></entry>
<entry>/</entry>
<entry>directory path separator</entry>
</row>
<row>
<entry><option>==</option></entry>
<entry>=</entry>
<entry>(equal-to) string comparison test</entry>
</row>
<row>
<entry><option>!==!</option></entry>
<entry>!=</entry>
<entry>(not equal-to) string comparison test</entry>
</row>
<row>
<entry><option>|</option></entry>
<entry>|</entry>
<entry>pipe</entry>
</row>
<row>
<entry><option>@</option></entry>
<entry>set <option>+v</option></entry>
<entry>do not echo current command</entry>
</row>
<row>
<entry><option>*</option></entry>
<entry>*</entry>
<entry>filename <quote>wild card</quote></entry>
</row>
<row>
<entry><option>&gt;</option></entry>
<entry>&gt;</entry>
<entry>file redirection (overwrite)</entry>
</row>
<row>
<entry><option>&gt;&gt;</option></entry>
<entry>&gt;&gt;</entry>
<entry>file redirection (append)</entry>
</row>
<row>
<entry><option>&lt;</option></entry>
<entry>&lt;</entry>
<entry>redirect <filename>stdin</filename></entry>
</row>
<row>
<entry><option>%VAR%</option></entry>
<entry>$VAR</entry>
<entry>environmental variable</entry>
</row>
<row>
<entry><option>REM</option></entry>
<entry>#</entry>
<entry>comment</entry>
</row>
<row>
<entry><option>NOT</option></entry>
<entry>!</entry>
<entry>negate following test</entry>
</row>
<row>
<entry><option>NUL</option></entry>
<entry><filename>/dev/null</filename></entry>
<entry><quote>black hole</quote> for burying command output</entry>
</row>
<row>
<entry><option>ECHO</option></entry>
<entry>echo</entry>
<entry>echo (many more option in Bash)</entry>
</row>
<row>
<entry><option>ECHO.</option></entry>
<entry>echo</entry>
<entry>echo blank line</entry>
</row>
<row>
<entry><option>ECHO OFF</option></entry>
<entry>set <option>+v</option></entry>
<entry>do not echo command(s) following</entry>
</row>
<row>
<entry><option>FOR %%VAR IN (LIST) DO</option></entry>
<entry>for var in [list]; do</entry>
<entry><quote>for</quote> loop</entry>
</row>
<row>
<entry><option>:LABEL</option></entry>
<entry>none (unnecessary)</entry>
<entry>label</entry>
</row>
<row>
<entry><option>GOTO</option></entry>
<entry>none (use a function)</entry>
<entry>jump to another location in the script</entry>
</row>
<row>
<entry><option>PAUSE</option></entry>
<entry>sleep</entry>
<entry>pause or wait an interval</entry>
</row>
<row>
<entry><option>CHOICE</option></entry>
<entry>case or select</entry>
<entry>menu choice</entry>
</row>
<row>
<entry><option>IF</option></entry>
<entry>if</entry>
<entry>if-test</entry>
</row>
<row>
<entry><option>IF EXIST <replaceable>FILENAME</replaceable></option></entry>
<entry>if [ -e filename ]</entry>
<entry>test if file exists</entry>
</row>
<row>
<entry><option>IF !%N==!</option></entry>
<entry>if [ -z "$N" ]</entry>
<entry>if replaceable parameter <quote>N</quote> not present</entry>
</row>
<row>
<entry><option>CALL</option></entry>
<entry>source or . (dot operator)</entry>
<entry><quote>include</quote> another script</entry>
</row>
<row>
<entry><option>COMMAND /C</option></entry>
<entry>source or . (dot operator)</entry>
<entry><quote>include</quote> another script (same as
CALL)</entry>
</row>
<row>
<entry><option>SET</option></entry>
<entry>export</entry>
<entry>set an environmental variable</entry>
</row>
<row>
<entry><option>SHIFT</option></entry>
<entry>shift</entry>
<entry>left shift command-line argument list</entry>
</row>
<row>
<entry><option>SGN</option></entry>
<entry>-lt or -gt</entry>
<entry>sign (of integer)</entry>
</row>
<row>
<entry><option>ERRORLEVEL</option></entry>
<entry>$?</entry>
<entry>exit status</entry>
</row>
<row>
<entry><option>CON</option></entry>
<entry><filename>stdin</filename></entry>
<entry><quote>console</quote> (<filename>stdin</filename>)</entry>
</row>
<row>
<entry><option>PRN</option></entry>
<entry><filename>/dev/lp0</filename></entry>
<entry>(generic) printer device</entry>
</row>
<row>
<entry><option>LPT1</option></entry>
<entry><filename>/dev/lp0</filename></entry>
<entry>first printer device</entry>
</row>
<row>
<entry><option>COM1</option></entry>
<entry><filename>/dev/ttyS0</filename></entry>
<entry>first serial port</entry>
</row>
</tbody>
</tgroup>
</table>
<para><anchor id="dosunixequiv"/></para>
<para>Batch files usually contain DOS commands. These must be
translated into their UNIX equivalents in order to convert a
batch file into a shell script.</para>
<table>
<title>DOS commands and their UNIX equivalents</title>
<tgroup cols="3">
<thead>
<row>
<entry>DOS Command</entry>
<entry>UNIX Equivalent</entry>
<entry>Effect</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>ASSIGN</option></entry>
<entry>ln</entry>
<entry>link file or directory</entry>
</row>
<row>
<entry><option>ATTRIB</option></entry>
<entry>chmod</entry>
<entry>change file permissions</entry>
</row>
<row>
<entry><option>CD</option></entry>
<entry>cd</entry>
<entry>change directory</entry>
</row>
<row>
<entry><option>CHDIR</option></entry>
<entry>cd</entry>
<entry>change directory</entry>
</row>
<row>
<entry><option>CLS</option></entry>
<entry>clear</entry>
<entry>clear screen</entry>
</row>
<row>
<entry><option>COMP</option></entry>
<entry>diff, comm, cmp</entry>
<entry>file compare</entry>
</row>
<row>
<entry><option>COPY</option></entry>
<entry>cp</entry>
<entry>file copy</entry>
</row>
<row>
<entry><option>Ctl-C</option></entry>
<entry>Ctl-C</entry>
<entry>break (signal)</entry>
</row>
<row>
<entry><option>Ctl-Z</option></entry>
<entry>Ctl-D</entry>
<entry>EOF (end-of-file)</entry>
</row>
<row>
<entry><option>DEL</option></entry>
<entry>rm</entry>
<entry>delete file(s)</entry>
</row>
<row>
<entry><option>DELTREE</option></entry>
<entry>rm -rf</entry>
<entry>delete directory recursively</entry>
</row>
<row>
<entry><option>DIR</option></entry>
<entry>ls -l</entry>
<entry>directory listing</entry>
</row>
<row>
<entry><option>ERASE</option></entry>
<entry>rm</entry>
<entry>delete file(s)</entry>
</row>
<row>
<entry><option>EXIT</option></entry>
<entry>exit</entry>
<entry>exit current process</entry>
</row>
<row>
<entry><option>FC</option></entry>
<entry>comm, cmp</entry>
<entry>file compare</entry>
</row>
<row>
<entry><option>FIND</option></entry>
<entry>grep</entry>
<entry>find strings in files</entry>
</row>
<row>
<entry><option>MD</option></entry>
<entry>mkdir</entry>
<entry>make directory</entry>
</row>
<row>
<entry><option>MKDIR</option></entry>
<entry>mkdir</entry>
<entry>make directory</entry>
</row>
<row>
<entry><option>MORE</option></entry>
<entry>more</entry>
<entry>text file paging filter</entry>
</row>
<row>
<entry><option>MOVE</option></entry>
<entry>mv</entry>
<entry>move</entry>
</row>
<row>
<entry><option>PATH</option></entry>
<entry>$PATH</entry>
<entry>path to executables</entry>
</row>
<row>
<entry><option>REN</option></entry>
<entry>mv</entry>
<entry>rename (move)</entry>
</row>
<row>
<entry><option>RENAME</option></entry>
<entry>mv</entry>
<entry>rename (move)</entry>
</row>
<row>
<entry><option>RD</option></entry>
<entry>rmdir</entry>
<entry>remove directory</entry>
</row>
<row>
<entry><option>RMDIR</option></entry>
<entry>rmdir</entry>
<entry>remove directory</entry>
</row>
<row>
<entry><option>SORT</option></entry>
<entry>sort</entry>
<entry>sort file</entry>
</row>
<row>
<entry><option>TIME</option></entry>
<entry>date</entry>
<entry>display system time</entry>
</row>
<row>
<entry><option>TYPE</option></entry>
<entry>cat</entry>
<entry>output file to <filename>stdout</filename></entry>
</row>
<row>
<entry><option>XCOPY</option></entry>
<entry>cp</entry>
<entry>(extended) file copy</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>Virtually all UNIX and shell operators and commands have
many more options and enhancements than their DOS and batch file
counterparts. Many DOS batch files rely on auxiliary utilities,
such as <command>ask.com</command>, a crippled counterpart to
<link linkend="readref">read</link>.</para>
<para>DOS supports only a very limited and incompatible subset of
filename <link linkend="globbingref">wild-card expansion</link>,
recognizing just the <token>*</token> and <token>?</token>
characters.</para>
</note>
<para>Converting a DOS batch file into a shell script is generally
straightforward, and the result ofttimes reads better than the
original.</para>
<example id="VIEWDAT">
<title>VIEWDATA.BAT: DOS Batch File</title>
<programlisting>&VIEWDAT;</programlisting>
</example>
<para>
The script conversion is somewhat of an improvement.
<footnote><para>Various readers have suggested modifications
of the above batch file to prettify it and make it more
compact and efficient. In the opinion of the <emphasis>ABS
Guide</emphasis> author, this is wasted effort. A Bash script
can access a DOS filesystem, or even an NTFS partition (with
the help of <ulink url="http://www.ntfs-3g.org">ntfs-3g</ulink>)
to do batch or scripted operations.</para></footnote>
</para>
<example id="viewdata">
<title><firstterm>viewdata.sh</firstterm>: 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 had a set of comprehensive
tutorials on the old-fashioned art of batch file
programming. Unfortunately the page has vanished without a
trace.</para>
</appendix>
<!-- End DOS Batch File Conversion appendix -->
<appendix id="exercises">
<title>Exercises</title>
<para>The exercises that follow test and extend your knowledge
of scripting. Think of them as a challenge, as an entertaining way
to take you further along the stony path toward UNIX wizardry.</para>
<para>
<literallayout>
On a dingy side street in a run-down section of Hoboken, New Jersey,
there sits a nondescript squat two-story brick building with an inscription
incised on a marble plate in its wall:
<computeroutput>Bash Scripting Hall of Fame</computeroutput>.
Inside, among various dusty uninteresting exhibits is a corroding,
cobweb-festooned brass plaque inscribed with a short, very short
list of those few persons who have successfully mastered the material
in the <firstterm>Advanced Bash Scripting Guide</firstterm>, as evidenced by their performance
on the following Exercise sections.
(Alas, the author of the <firstterm>ABS Guide</firstterm> is not represented among the exhibits.
This is possibly due to malicious rumors about <link linkend="nocreds">lack of credentials</link> and
<link linkend="ktour0">deficient scripting skills</link>.)
</literallayout>
</para>
<sect1 id="scriptanalysis">
<title>Analyzing Scripts</title>
<para>Examine the following script. Run it, then explain what it
does. Annotate the script and rewrite it in a more compact and
elegant manner.</para>
<para>
<programlisting>#!/bin/bash
MAX=10000
for((nr=1; nr&lt;$MAX; nr++))
do
let "t1 = nr % 5"
if [ "$t1" -ne 3 ]
then
continue
fi
let "t2 = nr % 7"
if [ "$t2" -ne 4 ]
then
continue
fi
let "t3 = nr % 9"
if [ "$t3" -ne 5 ]
then
continue
fi
break # What happens when you comment out this line? Why?
done
echo "Number = $nr"
exit 0</programlisting>
</para>
<para>---</para>
<para>Explain what the following script does. It is really just
a parameterized command-line pipe.</para>
<para>
<programlisting>#!/bin/bash
DIRNAME=/usr/bin
FILETYPE="shell script"
LOGFILE=logfile
file "$DIRNAME"/* | fgrep "$FILETYPE" | tee $LOGFILE | wc -l
exit 0</programlisting>
</para>
<para>---</para>
<para>Examine and explain the following script. For hints, you
might refer to the listings for <link
linkend="findref">find</link> and <link
linkend="statref">stat</link>.</para>
<para>
<programlisting>#!/bin/bash
# Author: Nathan Coulter
# This code is released to the public domain.
# The author gave permission to use this code snippet in the ABS Guide.
find -maxdepth 1 -type f -printf '%f\000' | {
while read -d $'\000'; do
mv "$REPLY" "$(date -d "$(stat -c '%y' "$REPLY") " '+%Y%m%d%H%M%S'
)-$REPLY"
done
}
# Warning: Test-drive this script in a "scratch" directory.
# It will somehow affect all the files there.</programlisting>
</para>
<para>---</para>
<para>A reader sent in the following code snippet.</para>
<para>
<programlisting>while read LINE
do
echo $LINE
done &lt; `tail -f /var/log/messages`</programlisting>
</para>
<para>He wished to write a script tracking changes to the system log
file, <filename>/var/log/messages</filename>. Unfortunately,
the above code block hangs and does nothing
useful. Why? Fix this so it does work. (Hint:
rather than <link linkend="redirref">redirecting the
<filename>stdin</filename> of the loop</link>, try a <link
linkend="piperef">pipe</link>.)</para>
<para>---</para>
<para>Analyze the following <quote>one-liner</quote> (here
split into two lines for clarity) contributed by Rory
Winston:</para>
<para>
<programlisting>export SUM=0; for f in $(find src -name "*.java");
do export SUM=$(($SUM + $(wc -l $f | awk '{ print $1 }'))); done; echo $SUM</programlisting>
</para>
<para>Hint: First, break the script up into bite-sized
sections. Then, carefully examine its use of <link
linkend="dblparens">double-parentheses</link> arithmetic,
the <link linkend="exportref">export</link> command,
the <link linkend="findref">find</link> command, the
<link linkend="wcref">wc</link> command, and <link
linkend="awkref">awk</link>.</para>
<para>---</para>
<para>Analyze <xref linkend="lifeslow"/>, and reorganize it in a
simplified and more logical style. See how many of the variables
can be eliminated, and try to optimize the script to speed up
its execution time.</para>
<para>Alter the script so that it accepts any ordinary ASCII
text file as input for its initial <quote>generation</quote>. The
script will read the first <parameter>$ROW*$COL</parameter>
characters, and set the occurrences of vowels as
<quote>living</quote> cells. Hint: be sure to translate the
spaces in the input file to underscore characters.</para>
</sect1>
<!-- End Analyzing Scripts section -->
<sect1 id="writingscripts">
<title>Writing Scripts</title>
<para><anchor id="writingscripts1"/></para>
<para>Write a script to carry out each of the following tasks.</para>
<variablelist id="exeasy">
<title><anchor id="exeasy1"/>EASY</title>
<varlistentry>
<term><command>Self-reproducing Script</command></term>
<listitem>
<para>Write a script that backs itself up, that is, copies
itself to a file named <filename>backup.sh</filename>.</para>
<para>Hint: Use the <link linkend="catref">cat</link> command
and the appropriate <link linkend="scrnameparam">positional
parameter</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Home Directory Listing</command></term>
<listitem>
<para>Perform a recursive directory listing on the user's home
directory and save the information to a file. Compress
the file, have the script prompt the user to insert
a USB flash drive, then press <keycap>ENTER</keycap>.
Finally, save the file to the flash drive after making
certain the flash drive has properly mounted by parsing
the output of <link linkend="dfref">df</link>. Note that
the flash drive must be <firstterm>unmounted</firstterm>
before it is removed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Converting <link linkend="forloopref1">for</link>
loops to <link linkend="whileloopref">while</link> and <link
linkend="untilloopref">until</link> loops</command></term>
<listitem>
<para>Convert the <firstterm>for loops</firstterm> in <xref
linkend="ex22"/> to <firstterm>while
loops</firstterm>. Hint: store the data in an <link
linkend="arrayref">array</link> and step through the array
elements.</para>
<para>Having already done the <quote>heavy lifting,</quote>
now convert the loops in the example to <firstterm> until
loops</firstterm>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Changing the line spacing of a text file</command></term>
<listitem>
<para>Write a script that reads each line of a target file, then
writes the line back to <filename>stdout</filename>, but with
an extra blank line following. This has the effect of
<emphasis>double-spacing</emphasis> the file.</para>
<para>Include all necessary code to check whether the script
gets the necessary command-line argument (a filename),
and whether the specified file exists.</para>
<para>When the script runs correctly, modify it to
<emphasis>triple-space</emphasis> the target file.</para>
<para>Finally, write a script to remove all blank lines from
the target file, <emphasis>single-spacing</emphasis> it.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Backwards Listing</command></term>
<listitem>
<para>Write a script that echoes itself to
<filename>stdout</filename>, but
<emphasis>backwards</emphasis>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Automatically Decompressing Files</command></term>
<listitem>
<para>Given a list of filenames as input, this script
queries each target file (parsing the output of the
<link linkend="fileref">file</link> command) for
the type of compression used on it. Then the script
automatically invokes the appropriate decompression command
(<command>gunzip</command>, <command>bunzip2</command>,
<command>unzip</command>, <command>uncompress</command>,
or whatever). If a target file is not compressed, the
script emits a warning message, but takes no other action
on that particular file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Unique System ID</command></term>
<listitem>
<para>Generate a <quote>unique</quote> 6-digit hexadecimal
identifier for your computer. Do <emphasis>not</emphasis>
use the flawed <link linkend="hostidref">hostid</link>
command. Hint: <command><link
linkend="md5sumref">md5sum</link>
<link
linkend="datafilesref1"><filename>/etc/passwd</filename></link></command>,
then select the first 6 digits of output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Backup</command></term>
<listitem>
<para>Archive as a <quote>tarball</quote>
(<filename>*.tar.gz</filename> file) all the files
in your home directory tree
(<filename>/home/your-name</filename>) that have
been modified in the last 24 hours. Hint: use <link
linkend="findref">find</link>.</para>
<para>Optional: you may use this as the basis of a
<firstterm>backup</firstterm> script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Checking whether a process is still running</command></term>
<listitem>
<para>Given a <link linkend="processidref">process ID</link>
(<firstterm>PID</firstterm>) as an argument, this script
will check, at user-specified intervals, whether
the given process is still running. You may use
the <link linkend="ppssref">ps</link> and <link
linkend="sleepref">sleep</link> commands.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Primes</command></term>
<listitem>
<para>Print (to <filename>stdout</filename>) all
prime numbers between 60000 and 63000. The output
should be nicely formatted in columns (hint:
use <link linkend="printfref">printf</link>).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Lottery Numbers</command></term>
<listitem>
<para>One type of lottery involves picking five
different numbers, in the range of 1 - 50. Write a
script that generates five pseudorandom numbers in this
range, <emphasis>with no duplicates</emphasis>. The
script will give the option of echoing the numbers to
<filename>stdout</filename> or saving them to a file,
along with the date and time the particular number set
was generated. (If your script consistently generates
<emphasis>winning</emphasis> lottery numbers, then you
can retire on the proceeds and leave shell scripting to
those of us who have to work for a living.)</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="exmedium">
<title><anchor id="exmedium1"/>INTERMEDIATE</title>
<varlistentry>
<term><command>Integer or String</command></term>
<listitem>
<para>Write a script <link linkend="functionref">function</link>
that determines if an argument passed to it is an integer
or a string. The function will return TRUE (0) if
passed an integer, and FALSE (1) if passed a string.</para>
<para>Hint: What does the following expression return
when <varname>$1</varname> is <emphasis>not</emphasis>
an integer?</para>
<para><varname>expr $1 + 0</varname></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command><link linkend="asciidef">ASCII</link>
to Integer</command></term>
<listitem>
<para>The <firstterm>atoi</firstterm> function in
<command>C</command> converts a string character to
an integer. Write a shell script function that performs
the same operation. Likewise, write a shell script function
that does the inverse, mirroring the <command>C</command>
<firstterm>itoa</firstterm> function which converts an
integer into an ASCII character.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Managing Disk Space</command></term>
<listitem>
<para>List, one at a time, all files larger than 100K in
the <filename class="directory">/home/username</filename>
directory tree. Give the user the option to delete or
compress the file, then proceed to show the next one. Write
to a logfile the names of all deleted files and the
deletion times.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Banner</command></term>
<listitem>
<para>Simulate the functionality of the deprecated <link
linkend="bannerref">banner</link> command in a script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Removing Inactive Accounts</command></term>
<listitem>
<para>Inactive accounts on a network server waste disk space and may
become a security risk. Write an administrative script
(to be invoked by <firstterm>root</firstterm> or the <link
linkend="cronref">cron daemon</link>) that checks
for and deletes user accounts that have not been accessed
within the last 90 days.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Enforcing Disk Quotas</command></term>
<listitem>
<para>Write a script for a multi-user system that checks users'
disk usage. If a user surpasses a preset limit
(500 MB, for example) in her <filename
class="directory">/home/username</filename>
directory, then the script automatically sends her a
<quote>pigout</quote> warning e-mail.</para> <para>The
script will use the <link linkend="duref">du</link>
and <link linkend="commmail1">mail</link> commands. As
an option, it will allow setting and enforcing quotas
using the <link linkend="quotaref">quota</link> and <link
linkend="setquotaref">setquota</link> commands.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Logged in User Information</command></term>
<listitem>
<para>For all logged in users, show their real names and the time
and date of their last login.</para>
<para>Hint: use <link linkend="whoref">who</link>,
<link linkend="lastlogref">lastlog</link>,
and parse <link
linkend="datafilesref1"><filename>/etc/passwd</filename></link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Safe Delete</command></term>
<listitem>
<para>Implement, as a script, a <quote>safe</quote> delete
command, <filename>sdel.sh</filename>. Filenames passed as
command-line arguments to this script are not deleted,
but instead <link linkend="gzipref">gzipped</link>
if not already compressed (use <link
linkend="fileref">file</link> to check), then moved
to a <filename class="directory">~/TRASH</filename>
directory. Upon invocation, the script checks the <filename
class="directory">~/TRASH</filename> directory for files
older than 48 hours and <link linkend="rmref">permanently
deletes</link> them. (An better alternative might be to
have a second script handle this, periodically invoked
by the <link linkend="cronref">cron daemon</link>.)</para>
<para><emphasis>Extra credit:</emphasis> Write the script
so it can handle files and directories <link
linkend="rmrecurs">recursively</link>. This would give it
the capability of <quote>safely deleting</quote> entire
directory structures.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Making Change</command></term>
<listitem>
<para>What is the most efficient way to make change for $1.68,
using only coins in common circulations (up to 25c)? It's
6 quarters, 1 dime, a nickel, and three cents.</para>
<para>Given any arbitrary command-line input in dollars and
cents ($*.??), calculate the change, using the minimum
number of coins. If your home country is not the United
States, you may use your local currency units instead. The
script will need to parse the command-line input, then
change it to multiples of the smallest monetary unit (cents
or whatever). Hint: look at <xref linkend="ex61"/>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Quadratic Equations</command></term>
<listitem>
<para>Solve a <firstterm>quadratic</firstterm> equation of the form
<parameter>Ax^2 + Bx + C = 0</parameter>. Have a script take
as arguments the coefficients, <userinput>A</userinput>,
<userinput>B</userinput>, and <userinput>C</userinput>,
and return the solutions to five decimal places.</para>
<para>Hint: pipe the coefficients to <link
linkend="bcref">bc</link>, using the well-known formula,
<parameter>x = ( -B +/- sqrt( B^2 - 4AC ) ) / 2A</parameter>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Table of Logarithms</command></term>
<listitem>
<para>Using the <link linkend="bcref">bc</link> and <link
linkend="printfref">printf</link> commands, print out a
nicely-formatted table of eight-place natural logarithms
in the interval between 0.00 and 100.00, in steps of
.01.</para>
<para>Hint: <firstterm>bc</firstterm> requires the
<option>-l</option> option to load the math library.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Unicode Table</command></term>
<listitem>
<para>Using <xref linkend="asciish"/> as a template,
write a script that prints to a file a complete
<link linkend="unicoderef">Unicode</link> table.</para>
<para>Hint: Use the <option>-e</option> option to
<link linkend="echoref">echo</link>:
<command>echo -e '\uXXXX'</command>, where
<replaceable>XXXX</replaceable>
is the Unicode numerical character designation.
This requires <link linkend="bash42">version 4.2</link>
or later of Bash.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Sum of Matching Numbers</command></term>
<listitem>
<para>Find the sum of all five-digit numbers (in the range
10000 - 99999) containing <emphasis>exactly two</emphasis>
out of the following set of digits: { 4, 5, 6 }. These may
repeat within the same number, and if so, they count once
for each occurrence.</para>
<para>Some examples of <firstterm>matching numbers</firstterm> are
42057, 74638, and 89515.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Lucky Numbers</command></term>
<listitem>
<para>A <firstterm>lucky number</firstterm> is one whose
individual digits add up to 7, in successive additions. For
example, 62431 is a <firstterm>lucky number</firstterm>
(6 + 2 + 4 + 3 + 1 = 16, 1 + 6 = 7). Find all the
<firstterm>lucky numbers</firstterm> between 1000 and
10000.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Craps</command></term>
<listitem>
<para>Borrowing the ASCII graphics from <xref linkend="petals"/>,
write a script that plays the well-known gambling game of
<firstterm>craps</firstterm>. The script will accept bets
from one or more players, roll the dice, and keep track of
wins and losses, as well as of each player's bankroll.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Tic-tac-toe</command></term>
<listitem>
<para>Write a script that plays the child's game of
<firstterm>tic-tac-toe</firstterm> against a human
player. The script will let the human choose whether
to take the first move. The script will follow
an optimal strategy, and therefore never lose. To simplify
matters, you may use ASCII graphics:</para>
<para><programlisting> o | x |
----------
| x |
----------
| o |
Your move, human (row, column)?</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Alphabetizing a String</command></term>
<listitem>
<para>Alphabetize (in ASCII order) an arbitrary string
read from the command-line.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Parsing</command></term>
<listitem>
<para>Parse <link
linkend="datafilesref1"><filename>/etc/passwd</filename></link>,
and output its contents in nice, easy-to-read tabular
form.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Logging Logins</command></term>
<listitem>
<para>Parse <filename>/var/log/messages</filename> to
produce a nicely formatted file of user logins and login
times. The script may need to run as
<firstterm>root</firstterm>. (Hint: Search for the string
<quote>LOGIN.</quote>)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Pretty-Printing a Data File</command></term>
<listitem>
<para>Certain database and spreadsheet packages use
save-files with the fields separated by commas, commonly
referred to as <firstterm>comma-separated values</firstterm>
or CSVs. Other applications often need to parse these
files.</para> <para>Given a data file with comma-separated
<link linkend="fieldref">fields</link>, of the form:
<programlisting>Jones,Bill,235 S. Williams St.,Denver,CO,80221,(303) 244-7989
Smith,Tom,404 Polk Ave.,Los Angeles,CA,90003,(213) 879-5612
...</programlisting>
Reformat the data and print it out to
<filename>stdout</filename> in labeled, evenly-spaced columns.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Justification</command></term>
<listitem>
<para>Given ASCII text input either from
<filename>stdin</filename> or a file, adjust
the word spacing to right-justify each line to a
user-specified line-width, then send the output to
<filename>stdout</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Mailing List</command></term>
<listitem>
<para>Using the <link linkend="commmail1">mail</link> command,
write a script that manages a simple mailing list. The
script automatically e-mails the monthly company newsletter,
read from a specified text file, and sends it to all the
addresses on the mailing list, which the script reads from
another specified file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Generating Passwords</command></term>
<listitem>
<para>Generate pseudorandom 8-character passwords, using
characters in the ranges [0-9], [A-Z], [a-z]. Each password
must contain at least two digits.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Monitoring a User</command></term>
<listitem>
<para>You suspect that one particular user on the network
has been abusing her privileges and possibly attempting to
hack the system. Write a script to automatically monitor
and log her activities when she's signed on. The log file
will save entries for the previous week, and delete those
entries more than seven days old.</para>
<para>You may use <link linkend="lastref">last</link>,
<link linkend="lastlogref">lastlog</link>, and <link
linkend="lastcommref">lastcomm</link> to aid your
surveillance of the suspected fiend.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Checking for Broken Links</command></term>
<listitem>
<para>Using <link linkend="lynxref">lynx</link> with the
<option>-traversal</option> option, write a script that
checks a Web site for broken links.</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist id="exdifficult">
<title><anchor id="exdifficult1"/>DIFFICULT</title>
<varlistentry>
<term><command>Testing Passwords</command></term>
<listitem>
<para>Write a script to check and validate passwords. The object
is to flag <quote>weak</quote> or easily guessed password
candidates.</para>
<para>A trial password will be input to the script as a
command-line parameter. To be considered acceptable,
a password must meet the following minimum qualifications:
<itemizedlist>
<listitem>
<para>Minimum length of 8 characters</para>
</listitem>
<listitem>
<para>Must contain at least one numeric character</para>
</listitem>
<listitem>
<para>Must contain at least one of the following
non-alphabetic characters: <token>@</token>,
<token>#</token>, <token>$</token>, <token>%</token>,
<token>&amp;</token>, <token>*</token>, <token>+</token>,
<token>-</token>, <token>=</token></para>
</listitem>
</itemizedlist></para>
<para>Optional:
<itemizedlist>
<listitem>
<para>Do a dictionary check on every sequence of at least
four consecutive alphabetic characters in the password under
test. This will eliminate passwords containing embedded
<quote>words</quote> found in a standard dictionary.</para>
</listitem>
<listitem>
<para>Enable the script to check all the passwords on your
system. These do not reside in
<link
linkend="datafilesref1"><filename>/etc/passwd</filename></link>.</para>
</listitem>
</itemizedlist></para>
<para>This exercise tests mastery of <link
linkend="regexref">Regular Expressions</link>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Cross Reference</command></term>
<listitem>
<para>Write a script that generates a
<firstterm>cross-reference</firstterm>
(<firstterm>concordance</firstterm>) on a target file.
The output will be a listing of all word occurrences in
the target file, along with the line numbers in which
each word occurs. Traditionally, <firstterm>linked
list</firstterm> constructs would be used in such
applications. Therefore, you should investigate <link
linkend="arrayref">arrays</link> in the course of
this exercise. <xref linkend="wf"/> is probably
<emphasis>not</emphasis> a good place to start.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="newtonsqrt"/><command>Square Root</command></term>
<listitem>
<para>Write a script to calculate square roots of numbers
using <firstterm>Newton's Method</firstterm>.</para>
<para>The algorithm for this, expressed as a snippet of Bash
<link linkend="pseudocoderef">pseudo-code</link> is:</para>
<para><programlisting># (Isaac) Newton's Method for speedy extraction
#+ of square roots.
guess = $argument
# $argument is the number to find the square root of.
# $guess is each successive calculated "guess" -- or trial solution --
#+ of the square root.
# Our first "guess" at a square root is the argument itself.
oldguess = 0
# $oldguess is the previous $guess.
tolerance = .000001
# To how close a tolerance we wish to calculate.
loopcnt = 0
# Let's keep track of how many times through the loop.
# Some arguments will require more loop iterations than others.
while [ ABS( $guess $oldguess ) -gt $tolerance ]
# ^^^^^^^^^^^^^^^^^^^^^^^ Fix up syntax, of course.
# "ABS" is a (floating point) function to find the absolute value
#+ of the difference between the two terms.
# So, as long as difference between current and previous
#+ trial solution (guess) exceeds the tolerance, keep looping.
do
oldguess = $guess # Update $oldguess to previous $guess.
# =======================================================
guess = ( $oldguess + ( $argument / $oldguess ) ) / 2.0
# = 1/2 ( ($oldguess **2 + $argument) / $oldguess )
# equivalent to:
# = 1/2 ( $oldguess + $argument / $oldguess )
# that is, "averaging out" the trial solution and
#+ the proportion of argument deviation
#+ (in effect, splitting the error in half).
# This converges on an accurate solution
#+ with surprisingly few loop iterations . . .
#+ for arguments > $tolerance, of course.
# =======================================================
(( loopcnt++ )) # Update loop counter.
done</programlisting></para>
<para>It's a simple enough recipe, and
<emphasis>seems</emphasis> at first glance easy enough to
convert into a working Bash script. The problem, though,
is that Bash has <link linkend="nofloatingpoint">no native
support for floating point numbers</link>. So, the script
writer needs to use <link linkend="bcref">bc</link> or
possibly <link linkend="awkref">awk</link> to convert the
numbers and do the calculations. It could get rather messy
. . .</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Logging File Accesses</command></term>
<listitem>
<para>Log all accesses to the files in <filename
class="directory">/etc</filename> during the course of
a single day. This information should include the filename,
user name, and access time. If any alterations to the
files take place, that will be flagged. Write this data
as tabular (tab-separated) formatted records in a logfile.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Monitoring Processes</command></term>
<listitem>
<para>Write a script to continually monitor all running
processes and to keep track of how many child processes each
parent spawns. If a process spawns more than five children,
then the script sends an e-mail to the system administrator
(or <firstterm>root</firstterm>) with all relevant
information, including the time, PID of the parent, PIDs
of the children, etc. The script appends a report to a log
file every ten minutes. </para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Strip Comments</command></term>
<listitem>
<para>Strip all comments from a shell script whose name
is specified on the command-line. Note that the initial
<link linkend="shabangref">#! line</link> must not be
stripped out.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Strip HTML Tags</command></term>
<listitem>
<para>Strip all the HTML tags from a specified HTML file, then
reformat it into lines between 60 and 75 characters
in length. Reset paragraph and block spacing, as
appropriate, and convert HTML tables to their approximate
text equivalent.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>XML Conversion</command></term>
<listitem>
<para>Convert an XML file to both HTML and text format.</para>
<para>Optional: A script that converts Docbook/SGML to XML.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><anchor id="cspammers"/><command>Chasing Spammers</command></term>
<listitem>
<para> Write a script that analyzes a spam e-mail by doing
DNS lookups on the IP addresses in the headers to identify
the relay hosts as well as the originating ISP. The
script will forward the unaltered spam message to the
responsible ISPs. Of course, it will be necessary to
filter out <emphasis>your own ISP's IP address</emphasis>,
so you don't end up complaining about yourself.</para>
<para>As necessary, use the appropriate <link
linkend="communinfo1">network analysis commands</link>.</para>
<para>For some ideas, see <xref linkend="isspammer"/> and <xref
linkend="isspammer2"/>.</para>
<para>Optional: Write a script that searches through a list of
e-mail messages and deletes the spam according to specified
filters.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Creating man pages</command></term>
<listitem>
<para>Write a script that automates the process of creating
<link linkend="manref">man pages</link>.</para>
<para>Given a text file which contains information to be
formatted into a <firstterm>man page</firstterm>, the
script will read the file, then invoke the appropriate
<link linkend="groffref">groff</link> commands to
output the corresponding <firstterm>man page</firstterm>
to <filename>stdout</filename>. The text file contains
blocks of information under the standard <firstterm>man
page</firstterm> headings, i.e., NAME, SYNOPSIS,
DESCRIPTION, etc.</para>
<para><xref linkend="maned"/> is an instructive first step.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Hex Dump</command></term>
<listitem>
<para>Do a hex(adecimal) dump on a binary file
specified as an argument to the script. The output should
be in neat tabular <link linkend="fieldref">fields</link>,
with the first field showing the address, each of the
next 8 fields a 4-byte hex number, and the final field
the ASCII equivalent of the previous 8 fields.</para>
<para>The obvious followup to this is to extend the hex
dump script into a disassembler. Using a lookup table,
or some other clever gimmick, convert the hex values into
80x86 op codes.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Emulating a Shift Register</command></term>
<listitem>
<para>Using <xref linkend="stackex"/> as an inspiration,
write a script that emulates a 64-bit shift register as
an <link linkend="arrayref">array</link>. Implement
functions to <firstterm>load</firstterm> the register,
<firstterm>shift left</firstterm>, <firstterm>shift
right</firstterm>, and <firstterm>rotate</firstterm>
it. Finally, write a function that interprets the register
contents as eight 8-bit ASCII characters.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Calculating Determinants</command></term>
<listitem>
<para>Write a script that calculates
determinants
<footnote>
<para>For all you clever types who failed intermediate algebra,
a <firstterm>determinant</firstterm> is a numerical value
associated with a multidimensional
<firstterm>matrix</firstterm> (<link
linkend="arrayref">array</link> of numbers).
<programlisting>For the simple case of a 2 x 2 determinant:
|a b|
|b a|
The solution is a*a - b*b, where "a" and "b" represent numbers.</programlisting>
</para></footnote>
by <link
linkend="recursionref0">recursively</link> expanding the
<firstterm>minors</firstterm>. Use a 4 x 4 determinant as
a test case.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Hidden Words</command></term>
<listitem>
<para>Write a <quote>word-find</quote> puzzle generator,
a script that hides 10 input words in a 10 x 10 array
of random letters. The words may be hidden across, down,
or diagonally.</para>
<para>Optional: Write a script that <emphasis>solves</emphasis>
word-find puzzles. To keep this from becoming too difficult,
the solution script will find only horizontal and vertical
words. (Hint: Treat each row and column as a string, and
search for substrings.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Anagramming</command></term>
<listitem>
<para> Anagram 4-letter input. For example, the
anagrams of <emphasis>word</emphasis> are:
<emphasis>do or rod row word</emphasis>. You may use
<filename>/usr/share/dict/linux.words</filename> as the
reference list.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Word Ladders</command></term>
<listitem>
<para>A <quote>word ladder</quote> is a sequence of words,
with each successive word in the sequence differing from
the previous one by a single letter.</para>
<para>For example, to <quote>ladder</quote> from
<emphasis>mark</emphasis> to
<emphasis>vase</emphasis>:</para>
<para>
<programlisting>
mark --> park --> part --> past --> vast --> vase
^ ^ ^ ^ ^</programlisting>
</para>
<para>Write a script that solves word ladder puzzles. Given
a starting and an ending word, the script will list all
intermediate steps in the <quote>ladder.</quote> Note
that <emphasis>all</emphasis> words in the sequence must
be legitimate dictionary words.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Fog Index</command></term>
<listitem>
<para>The <quote>fog index</quote> of a passage of text
estimates its reading difficulty, as a number corresponding
roughly to a school grade level. For example, a passage
with a fog index of 12 should be comprehensible to anyone
with 12 years of schooling.</para>
<para>The Gunning version of the fog index uses the following
algorithm.</para>
<orderedlist>
<listitem><para>Choose a section of the text at least
100 words in length.</para></listitem>
<listitem><para>Count the number of sentences (a portion of
a sentence truncated by the boundary of the text section
counts as one).</para></listitem>
<listitem>
<para>Find the average number of words per
sentence.</para>
<para>AVE_WDS_SEN = TOTAL_WORDS / SENTENCES</para>
</listitem>
<listitem>
<para>Count the number of <quote>difficult</quote>
words in the segment -- those containing at least
3 syllables. Divide this quantity by total words to
get the proportion of difficult words.</para>
<para>PRO_DIFF_WORDS = LONG_WORDS / TOTAL_WORDS</para>
</listitem>
<listitem>
<para>The Gunning fog index is the sum of the above two
quantities, multiplied by 0.4, then rounded to the
nearest integer.</para>
<para>G_FOG_INDEX = int ( 0.4 * ( AVE_WDS_SEN + PRO_DIFF_WORDS ) )</para>
</listitem>
</orderedlist>
<para>Step 4 is by far the most difficult portion of the
exercise. There exist various algorithms for estimating
the syllable count of a word. A rule-of-thumb formula
might consider the number of letters in a word and the
vowel-consonant mix.</para>
<para>A strict interpretation of the Gunning fog index does
not count compound words and proper nouns as
<quote>difficult</quote> words, but this would enormously
complicate the script.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Calculating PI using Buffon's Needle</command></term>
<listitem>
<para>The Eighteenth Century French mathematician de Buffon
came up with a novel experiment. Repeatedly drop a needle
of length <replaceable>n</replaceable> onto a wooden floor
composed of long and narrow parallel boards. The cracks
separating the equal-width floorboards are a fixed distance
<replaceable>d</replaceable> apart. Keep track of the
total drops and the number of times the needle intersects
a crack on the floor. The ratio of these two quantities
turns out to be a fractional multiple of PI.</para>
<para>In the spirit of <xref linkend="cannon"/>, write a
script that runs a Monte Carlo simulation of
<firstterm>Buffon's Needle</firstterm>. To simplify matters,
set the needle length equal to the distance between the
cracks, <parameter>n = d</parameter>.</para>
<para>Hint: there are actually two critical variables:
the distance from the center of the needle to the nearest
crack, and the inclination angle of the needle to that crack.
You may use <link linkend="bcref">bc</link> to handle
the calculations.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>Playfair Cipher</command></term>
<listitem>
<para>Implement the Playfair (Wheatstone) Cipher in a
script.</para>
<para>The Playfair Cipher encrypts text by substitution
of <firstterm>digrams</firstterm> (2-letter groupings).
It is traditional to use a 5 x 5 letter scrambled-alphabet
<firstterm>key square</firstterm> for the encryption and
decryption.</para>
<para>
<programlisting> C O D E S
A B F G H
I K L M N
P Q R T U
V W X Y Z
Each letter of the alphabet appears once, except "I" also represents
"J". The arbitrarily chosen key word, "CODES" comes first, then all
the rest of the alphabet, in order from left to right, skipping letters
already used.
To encrypt, separate the plaintext message into digrams (2-letter
groups). If a group has two identical letters, delete the second, and
form a new group. If there is a single letter left over at the end,
insert a "null" character, typically an "X."
THIS IS A TOP SECRET MESSAGE
TH IS IS AT OP SE CR ET ME SA GE
For each digram, there are three possibilities.
-----------------------------------------------
1) Both letters will be on the same row of the key square:
For each letter, substitute the one immediately to the right, in that
row. If necessary, wrap around left to the beginning of the row.
or
2) Both letters will be in the same column of the key square:
For each letter, substitute the one immediately below it, in that
row. If necessary, wrap around to the top of the column.
or
3) Both letters will form the corners of a rectangle within the key square:
For each letter, substitute the one on the other corner the rectangle
which lies on the same row.
The "TH" digram falls under case #3.
G H
M N
T U (Rectangle with "T" and "H" at corners)
T --&gt; U
H --&gt; G
The "SE" digram falls under case #1.
C O D E S (Row containing "S" and "E")
S --&gt; C (wraps around left to beginning of row)
E --&gt; S
=========================================================================
To decrypt encrypted text, reverse the above procedure under cases #1
and #2 (move in opposite direction for substitution). Under case #3,
just take the remaining two corners of the rectangle.
Helen Fouche Gaines' classic work, ELEMENTARY CRYPTANALYSIS (1939), gives a
fairly detailed description of the Playfair Cipher and its solution methods.</programlisting>
</para>
<para>This script will have three main sections</para>
<orderedlist id="playfairexref" numeration="upperroman">
<listitem><para>Generating the <firstterm>key square</firstterm>,
based on a user-input keyword.</para></listitem>
<listitem><para>Encrypting a <firstterm>plaintext</firstterm>
message.</para></listitem>
<listitem><para>Decrypting encrypted
text.</para></listitem>
</orderedlist>
<para>The script will make extensive use of <link
linkend="arrayref">arrays</link> and <link
linkend="functionref">functions</link>.
You may use <xref linkend="gronsfeld"/> as an
inspiration.</para>
</listitem>
</varlistentry>
</variablelist>
<para>--</para>
<para>Please do not send the author your solutions to these
exercises. There are more appropriate ways to impress him with
your cleverness, such as submitting bugfixes and suggestions
for improving the book.</para>
</sect1>
<!-- End Writing Scripts section -->
</appendix>
<!-- End Exercises appendix -->
<appendix id="revisionhistory">
<title>Revision History</title>
<synopsis>
This document first appeared as a 60-page HOWTO in the late spring
of 2000. Since then, it has gone through quite a number of updates
and revisions. This book could not have been written without the
assistance of the Linux community, and especially of the volunteers
of the <ulink url="http://www.tldp.org">Linux Documentation Project</ulink>.
</synopsis>
<para>Here is the e-mail to the LDP requesting permission to submit
version 0.1.</para>
<para><programlisting>From thegrendel@theriver.com Sat Jun 10 09:05:33 2000 -0700
Date: Sat, 10 Jun 2000 09:05:28 -0700 (MST)
From: "M. Leo Cooper" &lt;thegrendel@theriver.com&gt;
X-Sender: thegrendel@localhost
To: ldp-discuss@lists.linuxdoc.org
Subject: Permission to submit HOWTO
Dear HOWTO Coordinator,
I am working on and would like to submit to the LDP a HOWTO on the subject
of "Bash Scripting" (shell scripting, using 'bash'). As it happens,
I have been writing this document, off and on, for about the last eight
months or so, and I could produce a first draft in ASCII text format in
a matter of just a few more days.
I began writing this out of frustration at being unable to find a
decent book on shell scripting. I managed to locate some pretty good
articles on various aspects of scripting, but nothing like a complete,
beginning-to-end tutorial. Well, in keeping with my philosophy, if all
else fails, do it yourself.
As it stands, this proposed "Bash-Scripting HOWTO" would serve as a
combination tutorial and reference, with the heavier emphasis on the
tutorial. It assumes Linux experience, but only a very basic level
of programming skills. Interspersed with the text are 79 illustrative
example scripts of varying complexity, all liberally commented. There
are even exercises for the reader.
At this stage, I'm up to 18,000+ words (124k), and that's over 50 pages of
text (whew!).
I haven't mentioned that I've previously authored an LDP HOWTO, the
"Software-Building HOWTO", which I wrote in Linuxdoc/SGML. I don't know
if I could handle Docbook/SGML, and I'm glad you have volunteers to do
the conversion. You people seem to have gotten on a more organized basis
these last few months. Working with Greg Hankins and Tim Bynum was nice,
but a professional team is even nicer.
Anyhow, please advise.
Mendel Cooper
thegrendel@theriver.com</programlisting></para>
<table frame="none">
<title>Revision History</title>
<tgroup cols="3" colsep="0" rowsep="0">
<thead>
<row>
<entry>Release</entry>
<entry>Date</entry>
<entry>Comments</entry>
</row>
</thead>
<tbody>
<row>
<entry>0.1</entry>
<entry>14 Jun 2000</entry>
<entry>Initial release.</entry>
</row>
<row>
<entry><option>0.2</option></entry>
<entry>30 Oct 2000</entry>
<entry>Bugs fixed, plus much additional material and more
example scripts.</entry>
</row>
<row>
<entry><option>0.3</option></entry>
<entry>12 Feb 2001</entry>
<entry>Major update.</entry>
</row>
<row>
<entry><option>0.4</option></entry>
<entry>08 Jul 2001</entry>
<entry>Complete revision and expansion of the book.</entry>
</row>
<row>
<entry><option>0.5</option></entry>
<entry>03 Sep 2001</entry>
<entry>Major update: Bugfixes, material added,
sections reorganized.</entry>
</row>
<row>
<entry><option>1.0</option></entry>
<entry>14 Oct 2001</entry>
<entry>Stable release: Bugfixes, reorganization, material
added.</entry>
</row>
<row>
<entry><option>1.1</option></entry>
<entry>06 Jan 2002</entry>
<entry>Bugfixes, material and scripts added.</entry>
</row>
<row>
<entry><option>1.2</option></entry>
<entry>31 Mar 2002</entry>
<entry>Bugfixes, material and scripts added.</entry>
</row>
<row>
<entry><option>1.3</option></entry>
<entry>02 Jun 2002</entry>
<entry>TANGERINE release: A few bugfixes, much more material and
scripts added.</entry>
</row>
<row>
<entry><option>1.4</option></entry>
<entry>16 Jun 2002</entry>
<entry>MANGO release: A number of typos fixed, more
material and scripts.</entry>
</row>
<row>
<entry><option>1.5</option></entry>
<entry>13 Jul 2002</entry>
<entry>PAPAYA release: A few bugfixes, much more material and
scripts added.</entry>
</row>
<row>
<entry><option>1.6</option></entry>
<entry>29 Sep 2002</entry>
<entry>POMEGRANATE release: Bugfixes, more material,
one more script.</entry>
</row>
<row>
<entry><option>1.7</option></entry>
<entry>05 Jan 2003</entry>
<entry>COCONUT release: A couple of bugfixes, more material,
one more script.</entry>
</row>
<row>
<entry><option>1.8</option></entry>
<entry>10 May 2003</entry>
<entry>BREADFRUIT release: A number of bugfixes, more scripts and
material.</entry>
</row>
<row>
<entry><option>1.9</option></entry>
<entry>21 Jun 2003</entry>
<entry>PERSIMMON release: Bugfixes, and more material.</entry>
</row>
<row>
<entry><option>2.0</option></entry>
<entry>24 Aug 2003</entry>
<entry>GOOSEBERRY release: Major update.</entry>
</row>
<row>
<entry><option>2.1</option></entry>
<entry>14 Sep 2003</entry>
<entry>HUCKLEBERRY release: Bugfixes, and more material.</entry>
</row>
<row>
<entry><option>2.2</option></entry>
<entry>31 Oct 2003</entry>
<entry>CRANBERRY release: Major update.</entry>
</row>
<row>
<entry><option>2.3</option></entry>
<entry>03 Jan 2004</entry>
<entry>STRAWBERRY release: Bugfixes and more material.</entry>
</row>
<row>
<entry><option>2.4</option></entry>
<entry>25 Jan 2004</entry>
<entry>MUSKMELON release: Bugfixes.</entry>
</row>
<row>
<entry><option>2.5</option></entry>
<entry>15 Feb 2004</entry>
<entry>STARFRUIT release: Bugfixes and more material.</entry>
</row>
<row>
<entry><option>2.6</option></entry>
<entry>15 Mar 2004</entry>
<entry>SALAL release: Minor update.</entry>
</row>
<row>
<entry><option>2.7</option></entry>
<entry>18 Apr 2004</entry>
<entry>MULBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>2.8</option></entry>
<entry>11 Jul 2004</entry>
<entry>ELDERBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>3.0</option></entry>
<entry>03 Oct 2004</entry>
<entry>LOGANBERRY release: Major update.</entry>
</row>
<row>
<entry><option>3.1</option></entry>
<entry>14 Nov 2004</entry>
<entry>BAYBERRY release: Bugfix update.</entry>
</row>
<row>
<entry><option>3.2</option></entry>
<entry>06 Feb 2005</entry>
<entry>BLUEBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>3.3</option></entry>
<entry>20 Mar 2005</entry>
<entry>RASPBERRY release: Bugfixes, much material added.</entry>
</row>
<row>
<entry><option>3.4</option></entry>
<entry>08 May 2005</entry>
<entry>TEABERRY release: Bugfixes, stylistic revisions.</entry>
</row>
<row>
<entry><option>3.5</option></entry>
<entry>05 Jun 2005</entry>
<entry>BOXBERRY release: Bugfixes, some material added.</entry>
</row>
<row>
<entry><option>3.6</option></entry>
<entry>28 Aug 2005</entry>
<entry>POKEBERRY release: Bugfixes, some material added.</entry>
</row>
<row>
<entry><option>3.7</option></entry>
<entry>23 Oct 2005</entry>
<entry>WHORTLEBERRY release: Bugfixes, some material added.</entry>
</row>
<row>
<entry><option>3.8</option></entry>
<entry>26 Feb 2006</entry>
<entry>BLAEBERRY release: Bugfixes, some material added.</entry>
</row>
<row>
<entry><option>3.9</option></entry>
<entry>15 May 2006</entry>
<entry>SPICEBERRY release: Bugfixes, some material added.</entry>
</row>
<row>
<entry><option>4.0</option></entry>
<entry>18 Jun 2006</entry>
<entry>WINTERBERRY release: Major reorganization.</entry>
</row>
<row>
<entry><option>4.1</option></entry>
<entry>08 Oct 2006</entry>
<entry>WAXBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>4.2</option></entry>
<entry>10 Dec 2006</entry>
<entry>SPARKLEBERRY release: Important update.</entry>
</row>
<row>
<entry><option>4.3</option></entry>
<entry>29 Apr 2007</entry>
<entry>INKBERRY release: Bugfixes, material added.</entry>
</row>
<row>
<entry><option>5.0</option></entry>
<entry>24 Jun 2007</entry>
<entry>SERVICEBERRY release: Major update.</entry>
</row>
<row>
<entry><option>5.1</option></entry>
<entry>10 Nov 2007</entry>
<entry>LINGONBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>5.2</option></entry>
<entry>16 Mar 2008</entry>
<entry>SILVERBERRY release: Important update.</entry>
</row>
<row>
<entry><option>5.3</option></entry>
<entry>11 May 2008</entry>
<entry>GOLDENBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>5.4</option></entry>
<entry>21 Jul 2008</entry>
<entry>ANGLEBERRY release: Major update.</entry>
</row>
<row>
<entry><option>5.5</option></entry>
<entry>23 Nov 2008</entry>
<entry>FARKLEBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>5.6</option></entry>
<entry>26 Jan 2009</entry>
<entry>WORCESTERBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>6.0</option></entry>
<entry>23 Mar 2009</entry>
<entry>THIMBLEBERRY release: Major update.</entry>
</row>
<row>
<entry><option>6.1</option></entry>
<entry>30 Sep 2009</entry>
<entry>BUFFALOBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>6.2</option></entry>
<entry>17 Mar 2010</entry>
<entry>ROWANBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>6.3</option></entry>
<entry>30 Apr 2011</entry>
<entry>SWOZZLEBERRY release: Major update.</entry>
</row>
<row>
<entry><option>6.4</option></entry>
<entry>30 Aug 2011</entry>
<entry>VORTEXBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>6.5</option></entry>
<entry>05 Apr 2012</entry>
<entry>TUNGSTENBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>6.6</option></entry>
<entry>27 Nov 2012</entry>
<entry>YTTERBIUMBERRY release: Minor update.</entry>
</row>
<row>
<entry><option>10</option></entry>
<entry>10 Mar 2014</entry>
<entry>YTTERBIUMBERRY release: License change.</entry>
</row>
</tbody>
</tgroup>
</table>
</appendix> <!-- End Revision History appendix -->
<appendix id="mirrorsites">
<title>Download and Mirror Sites</title>
<para><anchor id="where_tarball"/></para>
<para>The latest update of this document, as an archived,
<link linkend="bzipref">bzip2-ed</link>
<quote>tarball</quote> including both the SGML source
and rendered HTML, may be downloaded from the <ulink
url="http://bash.deta.in/abs-guide-latest.tar.bz2">author's
home site</ulink>).
A <ulink
url="http://bash.deta.in/abs-guide.pdf">
pdf version</ulink> is also available (<ulink
url="http://www.mediafire.com/file/xi34ape1bifcnlb/abs-guide.pdf">mirror
site</ulink>).
There is likewise an <ulink
url="http://bash.deta.in/abs-guide.epub">
epub version</ulink>, courtesy of Craig Barnes and Michael Satke.
The <ulink
url="http://bash.deta.in/Change.log">change
log</ulink> gives a detailed revision history.
The <emphasis>ABS Guide</emphasis> even has <ulink
url="http://freecode.com/projects/advancedbashscriptingguide/">
its own <filename>freshmeat.net/freecode</filename> page</ulink>
to keep track of major updates, user comments, and popularity
ratings for the project.</para>
<para>The legacy hosting site for this document is the <ulink
url="http://www.tldp.org/LDP/abs/">Linux Documentation Project</ulink>,
which maintains many other Guides and HOWTOs as well.</para>
<para>Many thanks to Ronny Bangsund for donating <ulink
url="http://bash.deta.in/">server space</ulink> to host
this project.</para>
</appendix> <!-- Mirror Sites appendix -->
<appendix id="todolist">
<title>To Do List</title>
<itemizedlist>
<listitem>
<para>A comprehensive survey of <link
linkend="bashcompat">incompatibilities</link> between
Bash and the classic <link linkend="bashdef">Bourne
shell</link>.</para>
</listitem>
<listitem>
<para>Same as above, but for the Korn shell
(<firstterm>ksh</firstterm>).</para>
</listitem>
</itemizedlist>
</appendix> <!-- End Todo List appendix -->
<appendix id="copyright">
<title>Copyright</title>
<para>The <citetitle pubwork="book">Advanced Bash Scripting
Guide</citetitle> is herewith granted to the PUBLIC DOMAIN.
This has the following implications and consequences.
</para>
<para><programlisting>
A. All previous releases of the Advanced Bash Scripting Guide
are as well granted to the Public Domain.
A1. All printed editions, whether authorized by the author or not,
are as well granted to the Public Domain. This legally overrides
any stated intention or wishes of the publishers. Any statement
of copyright is void and invalid.
THERE ARE NO EXCEPTIONS TO THIS.
A2. Any release of the Advanced Bash Scripting Guide, whether in
electronic or print form is granted to the Public Domain by the
express directive of the author and previous copyright holder, Mendel
Cooper. No other person(s) or entities have ever held a valid copyright.
B. As a Public Domain document, unlimited copying and distribution rights
are granted. There can be NO restrictions. If anyone has published or will
in the future publish an original or modified version of this document,
then only additional original material may be copyrighted. The core
work will remain in the Public Domain.</programlisting></para>
<para>By law, distributors and publishers (including on-line
publishers) are prohibited from imposing any conditions,
strictures, or provisions on this document, any previous versions,
or any derivative versions. The author asserts
that he has <emphasis>not</emphasis> entered into any contractual
obligations that would alter the foregoing declarations.</para>
<para>Essentially, you may freely distribute this book or any
derivative thereof in electronic or printed form. If you have previously
purchased or are in possession of a printed copy of a current or
previous edition, you have the LEGAL RIGHT to copy and/or redistribute
it, regardless of any copyright notice. Any copyright notice is
void.</para>
<para><emphasis>Additionally, the author wishes to state his intention
that:</emphasis></para>
<para><programlisting>If you copy or distribute this book, kindly DO NOT
use the materials within, or any portion thereof, in a patent or copyright
lawsuit against the Open Source community, its developers, its
distributors, or against any of its associated software or documentation
including, but not limited to, the Linux kernel, Open Office, Samba,
and Wine. Kindly DO NOT use any of the materials within
this book in testimony or depositions as a plaintiff's "expert witness" in
any lawsuit against the Open Source community, any of its developers, its
distributors, or any of its associated software or documentation.
</programlisting></para>
<para>A Public Domain license essentially does not restrict ANY
legitimate distribution or use of this book. The author especially
encourages its (royalty-free!) use for classroom and instructional
purposes.</para>
<para>
To date, limited print rights (Lulu edition) have been granted
to one individual and to <emphasis>no one else</emphasis>. Neither
that individual nor Lulu holds or ever has held a valid copyright.</para>
<warning><para>It has come to the attention of the author that
<emphasis>unauthorized</emphasis> electronic and print
editions of this book are being sold commercially on <trademark
class="registered">itunes</trademark>, <emphasis>amazon.com</emphasis>
and elsewhere. These are illegal and pirated editions produced
without the author's permission, and readers of this book are
strongly urged not to purchase them. In fact, these pirated editions are
now legal, but necessarily fall into the Public Domain, and any
copyright notices contained within them are invalid and void.</para></warning>
<para>The author produced this book in a manner consistent with the
spirit of the <ulink url="http://www.tldp.org/manifesto.html">LDP
Manifesto</ulink>.</para>
<sidebar>
<para>Linux is a trademark registered to Linus Torvalds.</para>
<para>Fedora is a trademark registered to Red Hat.</para>
<para>Unix and UNIX are trademarks registered to the Open Group.</para>
<para>MS Windows is a trademark registered to the Microsoft Corp.</para>
<para>Solaris is a trademark registered to Oracle, Inc.</para>
<para>OSX is a trademark registered to Apple, Inc.</para>
<para>Yahoo is a trademark registered to Yahoo, Inc.</para>
<para>Pentium is a trademark registered to Intel, Inc.</para>
<para>Thinkpad is a trademark registered to Lenovo, Inc.</para>
<para>Scrabble is a trademark registered to Hasbro, Inc.</para>
<para>Librie, PRS-500, and PRS-505 are trademarks registered to
Sony, Inc.</para>
<para>All other commercial trademarks mentioned in the body of this work
are registered to their respective owners.</para>
</sidebar>
<para>Hyun Jin Cha has done a <ulink
url="http://kldp.org/HOWTO/html/Adv-Bash-Scr-HOWTO/index.html">Korean
translation</ulink> of version 1.0.11 of this book. Spanish,
Portuguese, <ulink
url="http://abs.traduc.org/">French</ulink>, German, <ulink
url="http://it.tldp.org/guide/abs/index.html">Italian</ulink>,
<ulink
url="http://gazette.linux.ru.net/rus/articles/index-abs-guide.html">Russian</ulink>,
<ulink url="http://premekvihan.net/bash">Czech</ulink>, <ulink
url="http://www.linuxsir.org/bbs/showthread.php?t=256887">Chinese</ulink>,
Indonesian, Dutch, Romanian, Bulgarian, and Turkish translations are also
available or in progress. If you wish to translate this document
into another language, please feel free to do so, subject to
the terms stated above. The author wishes to be notified of such
efforts.</para>
<sidebar><para>Those generous readers desiring to
make a donation to the author may contribute a small
amount via Paypal to my e-mail address,
<email>thegrendel.abs@gmail.com</email>.
(An <userinput>Honor Roll of Supporters</userinput>
is given at the beginning of the <ulink
url="http://bash.deta.in/Change.log">Change Log</ulink>.)
This is <emphasis>not</emphasis> a requirement.
The <firstterm>ABS Guide</firstterm> is a free and freely
distributed document for the use and enjoyment of the Linux
community. However, in these difficult times, showing support
for voluntary projects and especially to authors of limited
means is more critically important than ever.</para></sidebar>
</appendix> <!-- End Copyright appendix -->
<appendix id="asciitable">
<title>ASCII Table</title>
<para>Traditionally, a book of this sort has an <link
linkend="asciidef">ASCII</link> Table appendix.
This book does not. Instead, here are several short
scripts, each of which generates a complete ASCII table.</para>
<example id="asciish">
<title>A script that generates an ASCII table</title>
<programlisting>&asciish;</programlisting>
</example>
<example id="ascii2sh">
<title>Another ASCII table script</title>
<programlisting>&ascii2sh;</programlisting>
</example>
<example id="ascii3sh">
<title>A third ASCII table script, using
<firstterm>awk</firstterm></title>
<programlisting>&ascii3sh;</programlisting>
</example>
</appendix> <!-- End ASCII Table appendix -->
<index id="xrefindex"> <!-- Begin Index -->
&INDEX00;
</index> <!-- End Index -->
</book>
<!-- Keep this comment at the end of the file
Local variables:
mode: xml
sgml-indent-step:2
sgml-indent-data:t
End:
-->