Advanced Bash-Scripting Guide

An in-depth exploration of the art of shell scripting
Mendel Cooper


6.4
30 Aug 2011
Revision History
Revision 6.2
'ROWANBERRY' release
Revision 6.3
'SWOZZLEBERRY' release
Revision 6.4
'VORTEXBERRY' release

17 Mar 2010

Revised by: mc

27 Apr 2011

Revised by: mc

30


Advanced Bash-Scripting Guide

Table of Contents
Chapter 1. Shell Programming!
.........................................................................................................................1
Chapter 2. Starting Off With a Sha-Bang...........................................................


Advanced Bash-Scripting Guide

Table of Contents
Chapter 11. Loops and Branches..................................................................................................................138
11.1. Loops............................................................................................


Advanced Bash-Scripting Guide

Table of Contents
Chapter 22. Restricted Shells.........................................................................................................................386
Chapter 23. Process Substitution
................................................................


Advanced Bash-Scripting Guide

Table of Contents
Chapter 36. Miscellany
36.8.3. Writing Secure Shell Scripts.............................................................................................532
36.9. Portability Issues.......................................................................


Advanced Bash-Scripting Guide

Table of Contents
Appendix I. An Introduction to Programmable Completion.....................................................................790
Appendix J. Localization....................................................................................................


Chapter 1. Shell Programming!
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.
--Herbert Mayer
A working knowledge of shell scripting is essential to anyone wishing to become reasonabl


Advanced Bash-Scripting Guide
· Complex applications, where structured programming is a necessity (type-checking of variables,
function prototypes, etc.)
· Mission-critical applications upon which you are betting the future of the company
· Situations where security is important, where you need t


Chapter 2. Starting Off With a Sha-Bang
Shell programming is a 1950s juke box . . .
--Larry Wall
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 i


Advanced Bash-Scripting Guide
#
#
#
#+
#
#+

Warning:
------This script uses quite a number of features that will be explained
later on.
By the time you've finished the first half of the book,
there should be nothing mysterious about it.

LOG_DIR=/var/log
ROOT_UID=0
#
LINES=50
#
E_XCD=86
#
E_NOTROOT


Advanced Bash-Scripting Guide

tail -n $lines messages > mesg.temp # Save last section of message log file.
mv mesg.temp messages
# Becomes new log directory.

# cat /dev/null > messages
#* No longer needed, as the above method is safer.
cat /dev/null > wtmp # ': > wtmp' and '> wtmp' have the same e


Advanced Bash-Scripting Guide
been invoked with the correct number of parameters.
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_W


Part 2. Basics
Table of Contents
3. Special Characters
4. Introduction to Variables and Parameters
4.1. Variable Substitution
4.2. Variable Assignment
4.3. Bash Variables Are Untyped
4.4. Special Variable Types
5. Quoting
5.1. Quoting Variables
5.2. Escaping
6. Exit and Exit Status
7. Tests
7.1. Tes


Chapter 3. Special Characters
What makes a character special? If it has a meaning beyond its literal meaning, a meta-meaning, then we refer
to it as a special character.
Special Characters Found In Scripts and Elsewhere
#
Comments. Lines beginning with a # (with the exception of #!) are comments and


Advanced Bash-Scripting Guide
#+

^^
echo "File $filename exists."; cp $filename $filename.bak
else
#
^^
echo "File $filename not found."; touch $filename
fi; echo "File test complete."

Note that the ";" sometimes needs to be escaped.
;;
Terminator in a case option [double semicolon].
case "$variab


Advanced Bash-Scripting Guide
bash$ cp /home/bozo/current_work/junk/* .

Copy all the "junk" files to $PWD.
.
"dot" character match. When matching characters, as part of a regular expression, a "dot" matches a
single character.
"
partial quoting [double quote]. "STRING" preserves (from interpretatio


Advanced Bash-Scripting Guide
:
null command [colon]. This is the shell equivalent of a "NOP" (no op, a do-nothing operation). It
may be considered a synonym for the shell builtin true. The ":" command is itself a Bash builtin, and
its exit status is true (0).
:
echo $?

# 0

Endless loop:
while :
d


Advanced Bash-Scripting Guide
In combination with the >> redirection operator, has no effect on a pre-existing target file (: >>
target_file). If the file did not previously exist, creates it.
This applies to regular files, not pipes, symlinks, and certain special files.
May be used to begin a comme


Advanced Bash-Scripting Guide
In a double-parentheses construct, the ? can serve as an element of a C-style trinary operator.
condition?result-if-true:result-if-false
(( var0 = var1


Advanced Bash-Scripting Guide
a=123
( a=321; )
echo "a = $a"
# a = 123
# "a" within parentheses acts like a local variable.

array initialization.
Array=(element1 element2 element3)

{xxx,yyy,zzz,...}
Brace expansion.
echo \"{These,words,are,quoted}\"
# "These" "words" "are" "quoted"

# " prefix and


Advanced Bash-Scripting Guide
a=123
{ a=321; }
echo "a = $a"

# a = 321

(value inside code block)

# Thanks, S.C.

The code block enclosed in braces may have I/O redirected to and from it.

Example 3-1. Code blocks and I/O redirection
#!/bin/bash
# Reading lines in /etc/fstab.
File=/etc/fstab
{
rea


Advanced Bash-Scripting Guide
echo "Archive Listing:"
rpm -qpl $1
# Query listing.
echo
rpm -i --test $1 # Query whether rpm file can be installed.
if [ "$?" -eq $SUCCESS ]
then
echo "$1 can be installed."
else
echo "$1 cannot be installed."
fi
echo
# End code block.
} > "$1.test"
# Redirects output


Advanced Bash-Scripting Guide
As part of a regular expression, brackets delineate a range of characters to match.
$[ ... ]
integer expansion.
Evaluate integer expression between $[ ].
a=3
b=7
echo $[$a+$b]
echo $[$a*$b]

# 10
# 21

Note that this usage is deprecated, and has been replaced by the ((


Advanced Bash-Scripting Guide
[i]filename opens file filename for reading and writing, and assigns file descriptor i to it. If
filename does not exist, it is created.
process substitution.
(command)>


Advanced Bash-Scripting Guide
For an interesting note on the complexity of using UNIX pipes, see the UNIX FAQ, Part 3.
The output of a command or commands may be piped to a script.
#!/bin/bash
# uppercase.sh : Changes input to uppercase.
tr 'a-z' 'A-Z'
# Letter ranges must be quoted
#+ to prevent fi


Advanced Bash-Scripting Guide
done & # Run this loop in background.
# Will sometimes execute after second loop.
echo

# This 'echo' sometimes will not display.

for i in 11 12 13 14 15 16 17 18 19 20
do
echo -n "$i "
done
echo

# Second loop.

# This 'echo' sometimes will not display.

# ===========


Advanced Bash-Scripting Guide
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}
#
^

-The double-dash -- prefixes long (verbatim) options to commands.
sort --ignore-leading-blanks
Used with a Bas


Advanced Bash-Scripting Guide
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#

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 su


Advanced Bash-Scripting Guide

bash$ file #!/bin/bash
standard input:

Bourne-Again shell script text executable

Now the command accepts input from stdin and analyzes it.
The "-" can be used to pipe stdout to other commands. This permits such stunts as prepending lines
to a file.
Using diff to comp


Advanced Bash-Scripting Guide
$PATHNAME/-FILENAME.
If the value of a variable begins with a -, this may likewise create problems.
var="-n"
echo $var
# Has the effect of "echo -n", and outputs nothing.

previous working directory. A cd - command changes to the previous working directory. This uses
th


Advanced Bash-Scripting Guide
bash$ echo ~nonexistent-user
~nonexistent-user

~+
current working directory. This corresponds to the $PWD internal variable.
~previous working directory. This corresponds to the $OLDPWD internal variable.
=~
regular expression match. This operator was introduced with v


Advanced Bash-Scripting Guide
Ctl-H
Rubout (destructive backspace). Erases characters the cursor backs over while backspacing.
#!/bin/bash
# Embedding Ctl-H in a string.
a="^H^H"
echo "abcdef"
echo
echo -n "abcdef$a "
# Space at end ^
echo
echo -n "abcdef$a"
# No space at end

# Two Ctl-H's -- back


Advanced Bash-Scripting Guide
Carriage return.
#!/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 >&2
# The '-s' makes anything typed silent,
#


Advanced Bash-Scripting Guide
Ctl-S
Suspend (XOFF).
This freezes stdin in a terminal. (Use Ctl-Q to restore input.)
Ctl-T
Reverses the position of the character the cursor is on with the previous character (on the
command-line).
Ctl-U
Erase a line of input, from the cursor backward to beginning o


Advanced Bash-Scripting Guide
Definition: A field is a discrete chunk of data expressed as a string of consecutive characters.
Separating each field from adjacent fields is either whitespace or some other designated character
(often determined by the $IFS). In some contexts, a field may be called a


Chapter 4. Introduction to Variables and
Parameters
Variables are how programming and scripting languages represent data. A variable is nothing more than a
label, a name assigned to a location or set of locations in computer memory holding an item of data.
Variables appear in arithmetic operations a


Advanced Bash-Scripting Guide
#------------------------------------------------------------------------# No space permitted on either side of = sign when initializing variables.
# What happens if there is a space?
# "VARIABLE =value"
#
^
#% Script tries to run "VARIABLE" command with one argument, "


Advanced Bash-Scripting Guide
# May cause problems with older versions of "sh" . . .
# -------------------------------------------------------------echo; echo
numbers="one two three"
#
^
^
other_numbers="1 2 3"
#
^ ^
# If there is whitespace embedded within a variable,
#+ then quotes are necessary.


Advanced Bash-Scripting Guide

4.2. Variable Assignment
=
the assignment operator (no space before and after)
Do not confuse this with = and -eq, which test, rather than assign!
Note that = can be either an assignment or a test operator, depending on context.

Example 4-2. Plain Variable Assignment


Advanced Bash-Scripting Guide
echo $b
# Now, getting a little bit fancier (command substitution).
a=`echo Hello!`
# Assigns result of 'echo' command to 'a' ...
echo $a
# Note that including an exclamation mark (!) within a
#+ command substitution construct will not work from the command-line,
#+ sin


Advanced Bash-Scripting Guide
d=${c/BB/23}
echo "d = $d"
let "d += 1"
echo "d = $d"
echo

#
#
#
#
#

Substitute "23" for "BB".
This makes $d an integer.
d = 2334
2334 + 1
d = 2335

# What about null variables?
e=''
# ... Or e="" ... Or e=
echo "e = $e"
# e =
let "e += 1"
# Arithmetic operations allo


Advanced Bash-Scripting Guide
process.
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 child processes (the commands it
executes) i


Advanced Bash-Scripting Guide
# Strips out path name info (see 'basename')
echo
if [ -n "$1" ]
then
echo "Parameter #1 is $1"
fi

# Tested variable is quoted.
# Need quotes to escape #

if [ -n "$2" ]
then
echo "Parameter #2 is $2"
fi
if [ -n "$3" ]
then
echo "Parameter #3 is $3"
fi
# ...

if [ -n "


Advanced Bash-Scripting Guide
expected positional parameter.
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 eff


Advanced Bash-Scripting Guide
esac
exit $?

---

The shift command reassigns the positional parameters, in effect shifting them to the left one notch.
$1


Advanced Bash-Scripting Guide

$ 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 st


Chapter 5. Quoting
Quoting means just that, bracketing a string in quotes. This has the effect of protecting special characters in
the string from reinterpretation or expansion by the shell or shell script. (A character is "special" if it has an
interpretation other than its literal meaning. For exa


Advanced Bash-Scripting Guide
for a in $List
do
echo "$a"
done
# one
# two
# three

# Splits the variable in parts at whitespace.

echo "---"
for a in "$List"
do #
^
^
echo "$a"
done
# one two three

# Preserves whitespace in a single variable.

A more elaborate example:
variable1="a variable contai


Advanced Bash-Scripting Guide
echo
var2="\\\\\""
echo $var2
#
"
echo "$var2"
# \\"
echo
# But ... var2="\\\\"" is illegal. Why?
var3='\\\\'
echo "$var3"
# \\\\
# Strong quoting works, though.
exit

Single quotes (' ') operate similarly to double quotes, but do not permit referencing variables, since


Advanced Bash-Scripting Guide
means alert (beep or flash)
\0xx
translates to the octal ASCII equivalent of 0nn, where nn is a string of digits

The $' ... ' quoted string-expansion construct is a mechanism that uses escaped
octal or hex values to assign ASCII characters to variables, e.g., quote=$'\


Advanced Bash-Scripting Guide
# =================================================================== #
echo "Introducing the \$\' ... \' string-expansion construct . . . "
echo ". . . featuring more quotation marks."
echo $'\t \042 \t'
# Quote (") framed by tabs.
# Note that '\nnn' is an octal value.


Advanced Bash-Scripting Guide
echo "
d = show date/time"
echo "
q = quit"
echo "================================"
echo
# Convert the separate home-key to home-key_num_7:
if [ "$key" = $'\x1b\x4f\x48' ]; then
key=$'\x1b\x5b\x31\x7e'
#
Quoted string-expansion construct.
fi
# Convert the separate end-k


Advanced Bash-Scripting Guide
q)
echo Time to quit...
echo
exit 0
;;
*)
echo You pressed: \'"$key"\'
;;
esac
echo
echo "================================"
unset K1 K2 K3
read -s -N1 -p "Press a key: "
K1="$REPLY"
read -s -N2 -t 0.001
K2="$REPLY"
read -s -N1 -t 0.001
K3="$REPLY"
key="$K1$K2$K3"
done
e


Advanced Bash-Scripting Guide
echo "\z"
echo "\\z"

echo
echo
echo
echo
echo
echo
echo
echo

`echo
`echo
`echo
`echo
`echo
`echo
`echo
`echo

# \z
# \z

\z`
\\z`
\\\z`
\\\\z`
\\\\\\z`
\\\\\\\z`
"\z"`
"\\z"`

#
#
#
#
#
#
#
#
#

Command substitution
z
z
\z
\z
\z
\\z
\z
\z

# Here document
cat


Advanced Bash-Scripting Guide
Escaping a space can prevent word splitting in a command's argument list.
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 /sb


Advanced Bash-Scripting Guide
echo 'foo\
bar'
# Escape character \ taken literally because of strong quoting.
#foo\
#bar
# Examples suggested by Stéphane Chazelas.

Chapter 5. Quoting

50


Chapter 6. Exit and Exit Status
... there are dark corners in the Bourne shell, and
people use all of them.
--Chet Ramey
The exit command terminates a script, just as in a C program. It can also return a value, which is available to
the script's parent process.
Every command returns an exit status (


Advanced Bash-Scripting Guide
# Will exit with status of last command.

$? reads the exit status of the last command executed. After a function returns, $? gives the exit status of the
last command executed in the function. This is Bash's way of giving functions a "return value." [31]
Following the


Advanced Bash-Scripting Guide
echo $?
# 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
# Thanks, Stéphane Chazelas and Kristopher Newsome.

Certain exit status codes have reserved meaning


Chapter 7. Tests
Every reasonably complete programming language can test for a condition, then act according to the result of
the test. Bash has the test command, various bracket and parenthesis operators, and the if/then construct.

7.1. Test Constructs
· An if/then construct tests whether the exi


Advanced Bash-Scripting Guide
Again, note that the exit status of an arithmetic expression is not an error value.
var=-2 && (( var+=2 ))
echo $?

# 1

var=-2 && (( var+=2 )) && echo $var
# Will not echo $var!

·
An if can test any command, not just conditions enclosed within brackets.
if cmp a b &>


Advanced Bash-Scripting Guide
echo "Testing \"1\""
if [ 1 ]
# one
then
echo "1 is true."
else
echo "1 is false."
fi
# 1 is true.
echo
echo "Testing
if [ -1 ]
then
echo "-1 is
else
echo "-1 is
fi

\"-1\""
# minus one
true."
false."
# -1 is true.

echo
echo "Testing \"NULL\""
if [ ]
# NULL (empty cond


Advanced Bash-Scripting Guide
xyz=

# Initialized, but set to null value.

echo "Testing \"-n \$xyz\""
if [ -n "$xyz" ]
then
echo "Null variable is true."
else
echo "Null variable is false."
fi
# Null variable is false.

echo

# When is "false" true?
echo "Testing \"false\""
if [ "false" ]
# It seem


Advanced Bash-Scripting Guide
if [ -x "$filename" ]; then

Else if and elif
elif
elif is a contraction for else if. The effect is to nest an inner if/then construct within an outer one.
if [ condition1 ]
then
command1
command2
command3
elif [ condition2 ]
# Same as else if
then
command4
command5
els


Advanced Bash-Scripting Guide
fi
echo
if /usr/bin/test -z "$1"
# Equivalent to "test" builtin.
# ^^^^^^^^^^^^^
# Specifying full pathname.
then
echo "No command-line arguments."
else
echo "First command-line argument is $1."
fi
echo
if [ -z "$1" ]
# Functionally identical to above code blocks.
#
if


Advanced Bash-Scripting Guide
Arithmetic evaluation of octal / hexadecimal constants takes place automatically within a [[ ... ]] construct.
# [[ Octal and hexadecimal evaluation ]]
# Thank you, Moritz Gronbach, for pointing this out.

decimal=15
octal=017
hex=0x0f

# = 15 (decimal)
# = 15 (decimal)


Advanced Bash-Scripting Guide
Example 7-3. Arithmetic Tests using (( ))
#!/bin/bash
# arith-tests.sh
# Arithmetic tests.
# The (( ... )) construct evaluates and tests numerical expressions.
# Exit status opposite from [ ... ] construct!
(( 0 ))
echo "Exit status of \"(( 0 ))\" is $?."

# 1

(( 1 ))


Advanced Bash-Scripting Guide

7.2. File test operators
Returns true if...
-e
file exists
-a
file exists
This is identical in effect to -e. It has been "deprecated," [33] and its use is discouraged.
-f
file is a regular file (not a directory or device file)
-s
file is not zero size
-d
file is a dire


Advanced Bash-Scripting Guide
file is a symbolic link
-S
file is a socket
-t
file (descriptor) is associated with a terminal device
This test option may be used to check whether the stdin [ -t 0 ] or stdout [ -t 1 ] in a
given script is a terminal.
-r
file has read permission (for the user running t


Advanced Bash-Scripting Guide
file modified since it was last read
f1 -nt f2
file f1 is newer than f2
f1 -ot f2
file f1 is older than f2
f1 -ef f2
files f1 and f2 are hard links to the same file
!
"not" -- reverses the sense of the tests above (returns true if condition absent).

Example 7-4. Testin


Advanced Bash-Scripting Guide
##################
for directory in $directorys; do
if [ -d $directory ]
then linkchk $directory
else
echo "$directory is not a directory"
echo "Usage: $0 dir1 dir2 ..."
fi
done
exit $?

Example 31-1, Example 11-7, Example 11-3, Example 31-3, and Example A-1 also illust


Advanced Bash-Scripting Guide
(("$a"
is greater than (within double parentheses)
(("$a" > "$b"))
>=
is greater than or equal to (within double parentheses)
(("$a" >= "$b"))
string comparison
=
is equal to
if [ "$a" = "$b" ]
Note the whitespace framing the =.
if [ "$a"="$b" ] is not equivalent to th


Advanced Bash-Scripting Guide
Note that the "" needs to be escaped within a [ ] construct.
See Example 27-11 for an application of this comparison operator.
-z
string is null, that is, has zero length
String=''

# Zero-length ("null") string variable.

if [ -z "$String" ]
then
echo "\$String is null


Advanced Bash-Scripting Guide
if [ "$a" != "$b" ]
then
echo "$a is not equal to $b."
echo "(string comparison)"
#
"4" != "5"
# ASCII 52 != ASCII 53
fi
# In this particular instance, both "-ne" and "!=" work.
echo
exit 0

Example 7-6. Testing whether a string is null
#!/bin/bash
# str-test.sh: Testin


Advanced Bash-Scripting Guide

string1=initialized
if [ $string1 ]
# Again, $string1 stands unquoted.
then
echo "String \"string1\" is not null."
else
echo "String \"string1\" is null."
fi
# Again, gives correct result.
# Still, it is better to quote it ("$string1"), because . . .

string1="a = b"
i


Advanced Bash-Scripting Guide
# Uses the 'more' filter.
# May substitute 'less' if desired.
exit $?
# Script returns exit status of pipe.
# Actually "exit $?" is unnecessary, as the script will, in any case,
#+ return the exit status of the last command executed.

compound comparison
-a
logical and


Advanced Bash-Scripting Guide
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 ] && [ "$a" -lt 5 ]
then
echo "The value of \"a\" lies somewhere between 0 and 5."
fi

Example 37-4 demonstrates a nested if/t


Chapter 8. Operations and Related Topics
8.1. Operators
assignment
variable assignment
Initializing or changing the value of a variable
=
All-purpose assignment operator, which works for both arithmetic and string assignments.
var=27
category=minerals

# No spaces allowed after the "=".

Do not conf


Advanced Bash-Scripting Guide
This operator finds use in, among other things, generating numbers within a specific range (see
Example 9-11 and Example 9-15) and formatting program output (see Example 27-16 and Example
A-6). It can even be used to generate prime numbers, (see Example A-15). Modulo tu


Advanced Bash-Scripting Guide
echo; echo "GCD of $1 and $2 = $dividend"; echo

# Exercises :
# --------# 1) Check command-line arguments to make sure they are integers,
#+
and exit the script with an appropriate error message if not.
# 2) Rewrite the gcd () function to use local variables.
exit 0

+


Advanced Bash-Scripting Guide
echo -n "$n "
n=$[ $n + 1 ]
# Works even if "n" was initialized as a string.
#* Avoid this type of construct, since it is obsolete and nonportable.
# Thanks, Stephane Chazelas.
echo -n "$n "
# Now for C-style increment operators.
# Thanks, Frank Wang, for pointing this


Advanced Bash-Scripting Guide
bitwise operators. The bitwise operators seldom make an appearance in shell scripts. Their chief use seems to
be manipulating and testing values read from ports or sockets. "Bit flipping" is more relevant to compiled
languages, such as C and C++, which provide direct ac


Advanced Bash-Scripting Guide
||
OR
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 inside brackets
#+ of a [ ...


Advanced Bash-Scripting Guide
if [ "$a" -eq 98 -o "$b" -eq 47 ]
then
echo "Test #4 succeeds."
else
echo "Test #4 fails."
fi

a=rhino
b=crocodile
if [ "$a" = rhino ] && [ "$b" = crocodile ]
then
echo "Test #5 succeeds."
else
echo "Test #5 fails."
fi
exit 0

The && and || operators also find use in an


Advanced Bash-Scripting Guide
# Nothing out of the ordinary here.

# Octal: numbers preceded by '0' (zero)
let "oct = 032"
echo "octal number = $oct"
# Expresses result in decimal.
# --------- ------ -- -------

# 26

# Hexadecimal: numbers preceded by '0x' or '0X'
let "hex = 0x32"
echo "hexadecimal


Advanced Bash-Scripting Guide

8.3. The Double-Parentheses Construct
Similar to the let command, the (( ... )) construct permits arithmetic expansion and evaluation. In its simplest
form, a=$(( 5 + 3 )) would set a to 5 + 3, or 8. However, this double-parentheses construct is also a
mechanism for al


Advanced Bash-Scripting Guide
# ----------------# Chet Ramey seems to have snuck a bunch of undocumented C-style
#+ constructs into Bash (actually adapted from ksh, pretty much).
# In the Bash docs, Ramey calls (( ... )) shell arithmetic,
#+ but it goes far beyond that.
# Sorry, Chet, the secret is


Advanced Bash-Scripting Guide
&& -a
|| -o

AND
OR

logical, compound comparison
logical, compound comparison

?:
=

trinary operator
assignment

C-style
(do not confuse with equality
test)
times-equal, divide-equal,
mod-equal, etc.

*= /= %= += -= = &= combination assignment

,

comma

links a seque


Advanced Bash-Scripting Guide
#+
#+
#
#
#+
#+
#
#
#
#
#+
#

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.
=========


Part 3. Beyond the Basics
Table of Contents
9. Another Look at Variables
9.1. Internal Variables
9.2. Typing variables: declare or typeset
9.3. $RANDOM: generate random integer
10. Manipulating Variables
10.1. Manipulating Strings
10.2. Parameter Substitution
11. Loops and Branches
11.1. Loops
11.2.


Chapter 9. Another Look at Variables
Used properly, variables can add power and flexibility to scripts. This requires learning their subtleties and
nuances.

9.1. Internal Variables
Builtin variables:
variables affecting bash script behavior
$BASH
The path to the Bash binary itself
bash$ echo $BASH


Advanced Bash-Scripting Guide
# Bash version info:
for n in 0 1 2 3 4 5
do
echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
done
#
#
#
#
#
#

BASH_VERSINFO[0]
BASH_VERSINFO[1]
BASH_VERSINFO[2]
BASH_VERSINFO[3]
BASH_VERSINFO[4]
BASH_VERSINFO[5]

=
=
=
=
=
=

3
00
14
1
release
i386-redhat-linux-gnu

#


Advanced Bash-Scripting Guide
$FUNCNAME
Name of the current function
xyz23 ()
{
echo "$FUNCNAME now executing."
}

# xyz23 now executing.

xyz23
echo "FUNCNAME = $FUNCNAME"

# FUNCNAME =
# Null value outside a function.

See also Example A-50.
$GLOBIGNORE
A list of filename patterns to be excluded f


Advanced Bash-Scripting Guide
bash$ echo "$IFS"
(With $IFS set to default, a blank line displays.)

bash$ echo "$IFS" | cat -vte
^I$
$
(Show whitespace: here a single space, ^I [horizontal tab],
and newline, and display "$" at end-of-line.)

bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z


Advanced Bash-Scripting Guide
echo $var1
echo $var2
echo $var3

# a+b+c
# d-e-f
# g,h,i

# ======================================================== #
# However ...
# $IFS treats whitespace differently than other characters.
output_args_one_per_line()
{
for arg
do
echo "[$arg]"
done # ^
^
Embed withi


Advanced Bash-Scripting Guide
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, ls
[A-M]* would match both File1.txt and file1.txt. To revert to the customary
behavior of bracket matching,


Advanced Bash-Scripting Guide
bash$ echo $PIPESTATUS
0
bash$ ls -al | bogus_command
bash: bogus_command: command not found
bash$ echo ${PIPESTATUS[1]}
127
bash$ ls -al | bogus_command
bash: bogus_command: command not found
bash$ echo $?
127

The members of the $PIPESTATUS array hold the exit status


Advanced Bash-Scripting Guide
bash$ echo ${PIPESTATUS[@]}
0

The pipefail option may be useful in cases where $PIPESTATUS does not give the
desired information.
$PPID
The $PPID of a process is the process ID (pid) of its parent process. [41]
Compare this with the pidof command.
$PROMPT_COMMAND
A var


Advanced Bash-Scripting Guide
#+ is left as an exercise. (Hint: rm ./-weirdname or rm -- -weirdname)
echo
ls -al
# Any files left?
echo "Done."
echo "Old files deleted in $TargetDirectory."
echo
# Various other operations here, as necessary.
exit $?

$REPLY
The default value when a variable is not s


Advanced Bash-Scripting Guide
echo "This script has been running $SECONDS $units."
# On a slow or overburdened machine, the script may skip a count
#+ every once in a while.
sleep $INTERVAL
done
echo -e "\a"

# Beep!

exit 0

$SHELLOPTS
The list of enabled shell options, a readonly variable.
bash$ e


Advanced Bash-Scripting Guide
TIMER_INTERRUPT=14
TIMELIMIT=3 # Three seconds in this instance.
# May be set to different value.
PrintAnswer()
{
if [ "$answer" = TIMEOUT ]
then
echo $answer
else
# Don't want to mix up the two instances.
echo "Your favorite veggie is $answer"
kill $! # Kills no-longer


Advanced Bash-Scripting Guide
# timeout.sh
# Written by Stephane Chazelas,
#+ and modified by the document author.
INTERVAL=5

# timeout interval

timedout_read() {
timeout=$1
varname=$2
old_tty_settings=`stty -g`
stty -icanon min 0 time ${timeout}0
eval read $varname
# or just read $varname
stty "$


Advanced Bash-Scripting Guide
else
echo "variable = $variable"
fi
exit 0

$UID
User ID number
Current user's user identification number, as recorded in /etc/passwd
This is the current user's real id, even if she has temporarily assumed another identity through su.
$UID is a readonly variable, not su


Advanced Bash-Scripting Guide
tcsh% echo $TERM
rxvt
bash$ echo $LOGNAME
bozo
bash$ echo $SHELL
/bin/tcsh
bash$ echo $TERM
rxvt

Positional Parameters
$0, $1, $2, etc.
Positional parameters, passed from command line to script, passed to a function, or set to a variable
(see Example 4-5 and Example 15


Advanced Bash-Scripting Guide
index=1

# Reset count.
# What happens if you forget to do this?

echo "Listing args with \"\$@\":"
for arg in "$@"
do
echo "Arg #$index = $arg"
let "index+=1"
done
# $@ sees arguments as separate words.
echo "Arg list seen as separate words."
echo
index=1

# Reset coun


Advanced Bash-Scripting Guide
echo 'IFS unchanged, using "$*"'
c=0
for i in "$*"
# quoted
do echo "$((c+=1)): [$i]"
# This line remains the same in every instance.
# Echo args.
done
echo --echo 'IFS unchanged, using $*'
c=0
for i in $*
# unquoted
do echo "$((c+=1)): [$i]"
done
echo --echo 'IFS uncha


Advanced Bash-Scripting Guide
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo --echo 'IFS=":", using "$var" (var="$*")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo --echo 'IFS=":", using "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo --echo 'IFS=":", using $@'
c=0
for


Advanced Bash-Scripting Guide
# This example script by Stephane Chazelas,
# and slightly modified by the document author.

The $@ and $* parameters differ only when between double quotes.
Example 9-8. $* and $@ when $IFS is empty
#!/bin/bash
# If $IFS set, but empty,
#+ then "$*" and "$@" do not ech


Advanced Bash-Scripting Guide
echo -n "PID of \"$COMMAND1\":
${COMMAND1} &
echo $! >> "$LOG"
# PID of "sleep 100": 1506

" >> "$LOG"

# Thank you, Jacques Lederer, for suggesting this.

Using $! for job control:
possibly_hanging_job & { sleep ${TIMEOUT}; eval 'kill -9 $!' &> /dev/null; }
# Forces co


Advanced Bash-Scripting Guide
Process ID (PID) of the script itself. [44] The $$ variable often finds use in scripts to construct
"unique" temp file names (see Example 32-6, Example 16-31, and Example 15-27). This is usually
simpler than invoking mktemp.

9.2. Typing variables: declare or typeset
Th


Advanced Bash-Scripting Guide
declare -f function_name

A declare -f function_name in a script lists just the function named.
-x export
declare -x var3

This declares a variable as available for exporting outside the environment of the script itself.
-x var=$value
declare -x var3=373

The declare co


Advanced Bash-Scripting Guide
echo $FOO
}
bar

# Prints bar.

However . . .
foo (){
declare FOO="bar"
}
bar ()
{
foo
echo $FOO
}
bar

# Prints nothing.

# Thank you, Michael Iatrou, for pointing this out.

9.2.1. Another use for declare
The declare command can be helpful in identifying variables, en


Advanced Bash-Scripting Guide
#!/bin/bash
# $RANDOM returns a different random integer at each invocation.
# Nominal range: 0 - 32767 (signed 16-bit integer).
MAXCOUNT=10
count=1
echo
echo "$MAXCOUNT random numbers:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ]
# Generate 10 ($MAXCOUNT)


Advanced Bash-Scripting Guide
echo "Random number between $FLOOR and $RANGE --echo

$number"

# Generate binary choice, that is, "true" or "false" value.
BINARY=2
T=1
number=$RANDOM
let "number %= $BINARY"
# Note that
let "number >>= 14"
gives a better random distribution
#+ (right shifts out everyt


Advanced Bash-Scripting Guide
Denominations="2
3
4
5
6
7
8
9
10
Jack
Queen
King
Ace"
# Note variables spread over multiple lines.

suite=($Suites)
denomination=($Denominations)

# Read into array variable.

num_suites=${#suite[*]}
# Count how many elements.
num_denominations=${#denomination[*]}
echo


Advanced Bash-Scripting Guide
#+ angles of impact, and elasticity of the pegs.
# To what extent does this affect the accuracy of the simulation?
# ---------------------------------------------------------------PASSES=500
ROWS=10
RANGE=3
POS=0
RANDOM=$$

#
#
#
#
#
#+

Number of particle interactions


Advanced Bash-Scripting Guide
SHIFT=11
let "POS += $SHIFT"
(( Slots[$POS]++ ))

# Why 11, and not 10?
# Shift "zero position" to center.
# DEBUG: echo $POS

# echo -n "$POS "
}

Run () {
# Outer loop.
p=0
while [ "$p" -lt "$PASSES" ]
do
Play
(( p++ ))
POS=0
# Reset to zero. Why?
done
}

# ----------


Advanced Bash-Scripting Guide
#!/bin/bash
# random-between.sh
# Random number between two specified values.
# Script by Bill Gradwohl, with minor modifications by the document author.
# Corrections in lines 187 and 189 by Anthony Le Clezio.
# Used with permission.

randomBetween() {
# Generates a po


Advanced Bash-Scripting Guide
fi
# If min is itself not evenly divisible by $divisibleBy,
#+ then fix the min to be within range.
if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then
if [ ${min} -lt 0 ]; then
min=$((min/divisibleBy*divisibleBy))
else
min=$((((min/divisibleBy)+1)*divisibleBy))
fi


Advanced Bash-Scripting Guide
# Generate an array of expected answers and check to make sure we get
#+ at least one of each answer if we loop long enough.
declare -a answer
minimum=${min}
maximum=${max}
if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then
if [ ${minimum} -lt 0 ]; then
mi


Advanced Bash-Scripting Guide
for ((i=${minimum}; i


Advanced Bash-Scripting Guide
update_count $die1
let "throw += 1"
done
print_result
exit 0
#
#
#
#
#+

The scores should distribute fairly evenly, assuming RANDOM is fairly random.
With $MAXTHROWS at 600, all should cluster around 100, plus-or-minus 20 or so.
Keep in mind that RANDOM is a pseudorand


Advanced Bash-Scripting Guide
random_numbers

# gives a different number series.

echo; echo
# RANDOM=$$ seeds RANDOM from process id of script.
# It is also possible to seed RANDOM from 'time' or 'date' commands.
# Getting fancy...
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
# Pseud


Advanced Bash-Scripting Guide
#+

in the range between 10 and 100.

# 3) Same as exercise #2, above, but generate random integers this time.

The date command also lends itself to generating pseudorandom integer sequences.

Chapter 9. Another Look at Variables

118


Chapter 10. Manipulating Variables
10.1. Manipulating Strings
Bash supports a surprising number of string manipulation operations. Unfortunately, these tools lack a unified
focus. Some are a subset of parameter substitution, and others fall under the functionality of the UNIX expr
command. This resu


Advanced Bash-Scripting Guide
#+

such as ?, !, and ".

Length of Matching Substring at Beginning of String
expr match "$string" '$substring'
$substring is a regular expression.
expr "$string" : '$substring'
$substring is a regular expression.
stringZ=abcABC123ABCabc
#
|------|
#
12345678
echo `expr


Advanced Bash-Scripting Guide
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,


Advanced Bash-Scripting Guide
#
#

123456789......
1-based indexing.

echo `expr substr $stringZ 1 2`
echo `expr substr $stringZ 4 3`

# ab
# ABC

expr match "$string" '\($substring\)'
Extracts $substring at beginning of $string, where $substring is a regular expression.
expr "$string" : '\($substri


Advanced Bash-Scripting Guide
For example:
# 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


Advanced Bash-Scripting Guide
$OPERATION $file > "$filename.$SUFFIX"
# Redirect conversion to new filename.
rm -f $file
# Delete original files after converting.
echo "$filename.$SUFFIX" # Log what is happening to stdout.
done
exit 0
# Exercise:
# -------# As it stands, this script converts *all* th


Advanced Bash-Scripting Guide
#
#
#
#+
#
#+

Add flexibility by permitting use of *.ram and other filenames.
If you're really ambitious, expand the script
to do automatic downloads and conversions of streaming audio files.
Given a URL, batch download streaming audio files (using "wget")
and convert


Advanced Bash-Scripting Guide
${string/substring/replacement}
Replace first match of $substring with $replacement. [49]
${string//substring/replacement}
Replace all matches of $substring with $replacement.
stringZ=abcABC123ABCabc
echo ${stringZ/abc/xyz}

# xyzABC123ABCabc
# Replaces first match of '


Advanced Bash-Scripting Guide
String=23skidoo1
#
012345678
Bash
#
123456789
awk
# Note different string indexing system:
# Bash numbers first character of string as 0.
# Awk numbers first character of string as 1.
echo ${String:2:4} # position 3 (0-1-2), 4 characters long
# skid
# The awk equivalent


Advanced Bash-Scripting Guide
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"

${parameter-default}, ${parameter:-default}
If parameter not set, use default.
var1=1
var2=2
# var


Advanced Bash-Scripting Guide
echo "${variable-0}"
echo "${variable:-1}"
#
^

# (no output)
# 1

unset variable
echo "${variable-2}"
echo "${variable:-3}"

# 2
# 3

exit 0

The default parameter construct finds use in providing "missing" command-line arguments in scripts.
DEFAULT_FILENAME=generic.da


Advanced Bash-Scripting Guide
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
param6=123
a=${param6:+xyz}
echo "a = $a"

a=${para


Advanced Bash-Scripting Guide
: ${ZZXy23AB?"ZZXy23AB has not been set."}
# Since ZZXy23AB has not been set,
#+ then the script terminates with an error message.
# You can specify the error message.
# : ${variablename?"ERROR MESSAGE"}

# Same result with:
#
#
#

dummy_variable=${ZZXy23AB?}
dummy_vari


Advanced Bash-Scripting Guide
For an array, ${#array[*]} and ${#array[@]} give the number of elements in
the array.
Example 10-9. Length of a variable
#!/bin/bash
# length.sh
E_NO_ARGS=65
if [ $# -eq 0 ] # Must have command-line args to demo script.
then
echo "Please invoke this script with one or


Advanced Bash-Scripting Guide
echo `basename $PWD`
echo "${PWD##*/}"
echo
echo `basename $0`
echo $0
echo "${0##*/}"
echo
filename=test.data
echo "${filename##*.}"

# Basename of current working directory.
# Basename of current working directory.
# Name of script.
# Name of script.
# Name of script.


Advanced Bash-Scripting Guide
# Longest possible match, strips out last 12 characters
#
^^^^

abcd12345abc6789
|-------------|

# Remember, # and ## work from the left end (beginning) of string,
#
% and %% work from the right end.
echo
exit 0

Example 10-11. Renaming file extensions:
#!/bin/bash
# r


Advanced Bash-Scripting Guide
As above, if Replacement is omitted, then all occurrences of Pattern are replaced by nothing,
that is, deleted.

Example 10-12. Using pattern matching to parse arbitrary strings
#!/bin/bash
var1=abcd-1234-defg
echo "var1 = $var1"
t=${var1#*-*}
echo "var1 (with everythin


Advanced Bash-Scripting Guide
t=${path_name//o/O}
echo "$path_name with all o's capitalized = $t"
t=${path_name//o/}
echo "$path_name with all o's deleted = $t"
exit 0

${var/#Pattern/Replacement}
If prefix of var matches Pattern, then substitute Replacement for Pattern.
${var/%Pattern/Replacement}


Advanced Bash-Scripting Guide
echo "a = $a"
a=${!xyz@}
echo "a = $a"

#
#
#

a = xyz23 xyz24
Same as above.
a = xyz23 xyz24

echo "---"
abc23=something_else
b=${!abc*}
echo "b = $b"
#
c=${!b}
#
echo $c
#

b = abc23
Now, the more familiar type of indirect reference.
something_else

Chapter 10. Manipu


Chapter 11. Loops and Branches
What needs this iteration, woman?
--Shakespeare, Othello
Operations on code blocks are the key to structured and organized shell scripts. Looping and branching
constructs provide the tools for accomplishing this.

11.1. Loops
A loop is a block of code that iterates [51


Advanced Bash-Scripting Guide
done
echo; echo
for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto"
# All planets on same line.
# Entire 'list' enclosed in quotes creates a single variable.
# Why? Whitespace incorporated into the variable.
do
echo $planet
done
echo; echo "Whoo


Advanced Bash-Scripting Guide
/usr/sbin/chroot
/usr/bin/fakefile
/sbin/badblocks
/sbin/ypbind"
# List of files you are curious about.
# Threw in a dummy file, /usr/bin/fakefile.
echo
for file in $FILES
do
if [ ! -e "$file" ]
# Check if file exists.
then
echo "$file does not exist."; echo
continue
#


Advanced Bash-Scripting Guide
exit 0

Omitting the in [list] part of a for loop causes the loop to operate on $@ -- the positional
parameters. A particularly clever illustration of this is Example A-15. See also Example 15-17.

Example 11-5. Missing in [list] in a for loop
#!/bin/bash
# Invoke this


Advanced Bash-Scripting Guide
E_NOFILE=66
if [ $# -ne 2 ]
then
echo "Usage: `basename $0` search_string filename"
exit $E_BADARGS
fi
if [ ! -f "$2" ]
then
echo "File \"$2\" does not exist."
exit $E_NOFILE
fi

IFS=$'\012'

# Per suggestion of Anton Filippov.
# was: IFS="\n"
for word in $( strings "$2


Advanced Bash-Scripting Guide
#
#
#
#+
#

Discussion:
---------How is it that an ordinary user, or a script run by same,
can read /etc/passwd? (Hint: Check the /etc/passwd file permissions.)
Isn't this a security hole? Why or why not?

Yet another example of the [list] resulting from command substit


Advanced Bash-Scripting Guide
# Defaults to current working directory,
#+ if not otherwise specified.
# Equivalent to code block below.
# ---------------------------------------------------------# ARGS=1
# Expect one command-line argument.
#
# if [ $# -ne "$ARGS" ] # If not 1 arg...
# then
#
directo


Advanced Bash-Scripting Guide
Example 11-11. Symbolic links in a directory, saved to a file
#!/bin/bash
# symlinks.sh: Lists symbolic links in a directory.
OUTFILE=symlinks.list

# save file

directory=${1-`pwd`}
# Defaults to current working directory,
#+ if not otherwise specified.

echo "symbolic


Advanced Bash-Scripting Guide
echo; echo
# +==========================================+
# Now, let's do the same, using C-like syntax.
LIMIT=10
for ((a=1; a


Advanced Bash-Scripting Guide
fax make $2

#

Create fax-formatted files from text files.

for file in $(ls $2.0*)

# Concatenate the converted files.
# Uses wild card (filename "globbing")
#+ in variable list.

do
fil="$fil $file"
done
efax -d "$MODEM_PORT" -t "T$1" $fil
# Finally, do the work.
# T


Advanced Bash-Scripting Guide
var0=`expr $var0 + 1`

done

#
#
#
#

var0=$(($var0+1)) also works.
var0=$((var0 + 1)) also works.
let "var0 += 1"
also works.
Various other methods also work.

echo
exit 0

Example 11-15. Another while loop
#!/bin/bash
echo
# Equivalent to:
while [ "$var1" != "end" ]
#


Advanced Bash-Scripting Guide
Example 11-17. C-style syntax in a while loop
#!/bin/bash
# wh-loopc.sh: Count to 10 in a "while" loop.
LIMIT=10
a=1

# 10 iterations.

while [ "$a" -le $LIMIT ]
do
echo -n "$a "
let "a+=1"
done
# No surprises, so far.
echo; echo
# +=====================================


Advanced Bash-Scripting Guide
# Still going: t = 2
# Still going: t = 3
# Still going: t = 4

Similar to the if-test construct, a while loop can omit the test brackets.
while condition
do
command(s) ...
done

By coupling the power of the read command with a while loop, we get the handy while read co


Advanced Bash-Scripting Guide
until [ condition-is-true ] ; do

Example 11-18. until loop
#!/bin/bash
END_CONDITION=end
until [ "$var1" = "$END_CONDITION" ]
# Tests condition here, at top of loop.
do
echo "Input variable #1 "
echo "($END_CONDITION to exit)"
read var1
echo "variable #1 = $var1"
echo


Advanced Bash-Scripting Guide
# Beginning of outer loop.
for a in 1 2 3 4 5
do
echo "Pass $outer in outer loop."
echo "---------------------"
inner=1
# Reset inner loop counter.
# ===============================================
# Beginning of inner loop.
for b in 1 2 3 4 5
do
echo "Pass $inner in in


Advanced Bash-Scripting Guide
do
a=$(($a+1))
if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Excludes 3 and 11.
then
continue
# Skip rest of this particular loop iteration.
fi
echo -n "$a "
done

# This will not execute for 3 and 11.

# Exercise:
# Why does the loop print up to 20?
echo; echo
echo Printing N


Advanced Bash-Scripting Guide
if [ "$innerloop" -eq 3 ]
then
break # Try
break 2
to see what happens.
# ("Breaks" out of both inner and outer loops.)
fi
done
# -------------------------------------------------------echo
done
echo
exit 0

The continue command, similar to break, optionally takes a par


Advanced Bash-Scripting Guide
#+
#+
#+
#

in a directory. There are several machines that access
this directory, and I want to distribute the work over these
different boxen.
Then I usually nohup something like the following on every box:

while true
do
for n in .iso.*
do
[ "$n" = ".iso.opts" ] && c


Advanced Bash-Scripting Guide

11.4. Testing and Branching
The case and select 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.
Controlling program f


Advanced Bash-Scripting Guide
#+
#
#
#

[a-z] and [A-Z].
This no longer works in certain locales and/or Linux distros.
POSIX is more portable.
Thanks to Frank Wang for pointing this out.

#
#
#
#
#+
#

Exercise:
-------As the script stands, it accepts a single keystroke, then terminates.
Change the


Advanced Bash-Scripting Guide
# Add info for Smith & Zane later.
* )
# Default option.
# Empty input (hitting RETURN) fits here, too.
echo
echo "Not yet in database."
;;
esac
echo
#
#
#
#+

Exercise:
-------Change the script so it accepts multiple inputs,
instead of terminating after displaying just


Advanced Bash-Scripting Guide
shift
done

# Check next set of parameters.

# From Stefano Falsetto's "Log2Rot" script,
#+ part of his "rottlog" package.
# Used with permission.

Example 11-26. Using command substitution to generate the case variable
#!/bin/bash
# case-cmd.sh: Using command substitut


Advanced Bash-Scripting Guide
match_string $b $d
echo $?

# match
# 0

exit 0

Example 11-28. Checking for alphabetic input
#!/bin/bash
# isalpha.sh: Using a "case" structure to filter a string.
SUCCESS=0
FAILURE=-1
isalpha () # Tests whether *first character* of input string is alphabetic.
{
if [ -


Advanced Bash-Scripting Guide
fi
else
echo "\"$*\" begins with a non-alpha character."
# Also "non-alpha" if no argument passed.
fi
echo
}
digit_check () # Front-end to isdigit ().
{
if isdigit "$@"
then
echo "\"$*\" contains only digits [0 - 9]."
else
echo "\"$*\" has at least one non-digit charact


Advanced Bash-Scripting Guide
done
This prompts the user to enter one of the choices presented in the variable list. Note that select uses
the $PS3 prompt (#? ) by default, but this may be changed.

Example 11-29. Creating menus using select
#!/bin/bash
PS3='Choose your favorite vegetable: ' # Sets


Advanced Bash-Scripting Guide
echo "Yuck!"
echo
break
done
}
choice_of beans rice carrots radishes tomatoes spinach
#
$1
$2
$3
$4
$5
$6
#
passed to choice_of() function
exit 0

See also Example 37-3.

Chapter 11. Loops and Branches

163


Chapter 12. Command Substitution
Command substitution reassigns the output of a command [54] or even multiple commands; it literally plugs
the command output into another context. [55]
The classic form of command substitution uses backquotes (`...`). Commands within backquotes (backticks)
generate c


Advanced Bash-Scripting Guide
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)
echo "Hit a key "
stty -icanon -echo

# Save old te


Advanced Bash-Scripting Guide
# It is not necessary to explicitly assign a variable.
echo "`


Advanced Bash-Scripting Guide
Notice that a buffer overrun does not occur. This is one instance where an interpreted language, such as
Bash, provides more protection from programmer mistakes than a compiled language.
Command substitution permits setting a variable to the output of a loop. The key to


Advanced Bash-Scripting Guide
bash$ sh hello.sh
Hello, world.

The $(...) form has superseded backticks for command substitution.
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=$(


Advanced Bash-Scripting Guide
#

(

echo
echo "${#Anagrams[*]}
echo
echo ${Anagrams[0]}
echo ${Anagrams[1]}

array assignment

)

# echo "${Anagrams[*]}"

7+ letter anagrams found"
# First anagram.
# Second anagram.
# Etc.
# To list all the anagrams in a single line . . .

# Look ahead to the "Array


Chapter 13. Arithmetic Expansion
Arithmetic expansion provides a powerful tool for performing (integer) arithmetic operations in scripts.
Translating a string into a numerical expression is relatively straightforward using backticks, double
parentheses, or let.
Variations
Arithmetic expansion with b


Chapter 14. Recess Time
This bizarre little intermission gives the reader a chance to relax and maybe laugh a bit.

Fellow Linux user, greetings! You are reading something which
will bring you luck and good fortune. Just e-mail a copy of
this document to 10 of your friends. Before making the copies,


Part 4. Commands
Mastering the commands on your Linux machine is an indispensable prelude to writing effective shell scripts.
This section covers the following commands:
· . (See also source)
· ac
· adduser
· agetty
· agrep
· ar
· arch
· at
· autoload
· awk (See also Using awk for math ope


Advanced Bash-Scripting Guide
· complete
· compress
· coproc
· cp
· cpio
· cron
· crypt
· csplit
· cu
· cut
· date
· dc
· dd
· debugfs
· declare
· depmod
· df
· dialog
· diff
· diff3
· diffstat
· dig
· dirname
· dirs
· disown
· dmesg
· doexec
· dos2unix
· du
· dump
· d


Advanced Bash-Scripting Guide
· find
· finger
· flex
· flock
· fmt
· fold
· free
· fsck
· ftp
· fuser
· getfacl
· getopt
· getopts
· gettext
· getty
· gnome-mount
· grep
· groff
· groupmod
· groups (Related topic: the $GROUPS variable)
· gs
· gzip
· halt
· hash
· hdparm
· h


Advanced Bash-Scripting Guide
· lastlog
· ldd
· less
· let
· lex
· lid
· ln
· locate
· lockfile
· logger
· logname
· logout
· logrotate
· look
· losetup
· lp
· ls
· lsdev
· lsmod
· lsof
· lspci
· lsusb
· ltrace
· lynx
· lzcat
· lzma
· m4
· mail
· mailstats
· mailto
· m


Advanced Bash-Scripting Guide
· mount
· msgfmt
· mv
· nc
· netconfig
· netstat
· newgrp
· nice
· nl
· nm
· nmap
· nohup
· nslookup
· objdump
· od
· openssl
· passwd
· paste
· patch (Related topic: diff)
· pathchk
· pax
· pgrep
· pidof
· ping
· pkill
· popd
· pr
· printenv


Advanced Bash-Scripting Guide
· rm
· rmdir
· rmmod
· route
· rpm
· rpm2cpio
· rsh
· rsync
· runlevel
· run-parts
· rx
· rz
· sar
· scp
· script
· sdiff
· sed
· seq
· service
· set
· setfacl
· setquota
· setserial
· setterm
· sha1sum
· shar
· shopt
· shred
· shutdown
· s


Advanced Bash-Scripting Guide
· sx
· sync
· sz
· tac
· tail
· tar
· tbl
· tcpdump
· tee
· telinit
· telnet
· Tex
· texexec
· time
· times
· tmpwatch
· top
· touch
· tput
· tr
· traceroute
· true
· tset
· tsort
· tty
· tune2fs
· type
· typeset
· ulimit
· umask
· umount


Advanced Bash-Scripting Guide
· uudecode
· uuencode
· uux
· vacation
· vdir
· vmstat
· vrfy
·w
· wait
· wall
· watch
· wc
· wget
· whatis
· whereis
· which
· who
· whoami
· whois
· write
· xargs
· yacc
· yes
· zcat
· zdiff
· zdump
· zegrep
· zfgrep
· zgrep
· zip
Table o


Chapter 15. Internal Commands and Builtins
A builtin is a command contained within the Bash tool set, literally built in. This is either for performance
reasons -- builtins execute faster than external commands, which usually require forking off [57] a separate
process -- or because a particular bui


Advanced Bash-Scripting Guide
#!/bin/bash
echo "This line uses the \"echo\" builtin."
/bin/echo "This line uses the /bin/echo system command."

A keyword is a reserved word, token or operator. Keywords have a special meaning to the shell, and indeed
are the building blocks of the shell's syntax. As


Advanced Bash-Scripting Guide
# 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 "--


Advanced Bash-Scripting Guide
Older versions of Bash may not support printf.
Example 15-2. printf in action
#!/bin/bash
# printf demo
declare -r PI=3.14159265358979
declare -r DecimalConstant=31373

# Read-only variable, i.e., a constant.

Message1="Greetings,"
Message2="Earthling."
echo
printf "Pi


Advanced Bash-Scripting Guide
# Thanks, S.C.

See also Example 36-15.
read
"Reads" the value of a variable from stdin, that is, interactively fetches input from the keyboard.
The -a option lets read get array variables (see Example 27-6).

Example 15-3. Variable assignment, using read
#!/bin/bash
#


Advanced Bash-Scripting Guide
# ------------------------------------------------------------------- #
echo
echo "========================="
echo

# This example is similar to the "reply.sh" script.
# However, this one shows that $REPLY is available
#+ even after a 'read' to a variable in the convent


Advanced Bash-Scripting Guide
# Data entry terminates with the first .
echo
exit 0

The read command has some interesting options that permit echoing a prompt and even reading
keystrokes without hitting ENTER.
# Read a keypress without hitting ENTER.
read -s -n1 -p "Hit a key " keypress
echo; echo "


Advanced Bash-Scripting Guide
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Right-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Left-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq


Advanced Bash-Scripting Guide
esac
exit $?
# ========================================= #
# Antonio Macchi has a simpler alternative.
#!/bin/bash
while true
do
read -sn1 a
test "$a" == `echo -en "\e"` || continue
read -sn1 a
test "$a" == "[" || continue
read -sn1 a
case "$a" in
A) echo "up";;
B) echo


Advanced Bash-Scripting Guide
# 3) The final variable gets the remainder of the line.
# 4) If there are more variables to be set than whitespace-terminated strings
#
on the first line of the file, then the excess variables remain empty.
echo "------------------------------------------------"
# How t


Advanced Bash-Scripting Guide
Example 15-8. Problems reading from a pipe
#!/bin/sh
# readpipe.sh
# This example contributed by Bjon Eriksson.
### shopt -s lastpipe
last="(null)"
cat $0 |
while read line
do
echo "{$line}"
last=$line
done
echo
echo "++++++++++++++++++++++"
printf "\nAll done, last: $l


Advanced Bash-Scripting Guide
The familiar cd change directory command finds use in scripts where execution of a command
requires being in a specified directory.
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

[from the previously cited example by Alan Cox]
The -P (physic


Advanced Bash-Scripting Guide
# Now, do some stuff in directory 'dir1'.
pushd $dir2
echo "Now in directory `pwd`."
# Now, do some stuff in directory 'dir2'.
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."
# Now, do some more stuff in directory 'dir1'


Advanced Bash-Scripting Guide
echo "7-- = $a"
# 7-- = 6
# Of course, ++a, etc., also allowed . . .
echo

# Trinary operator.
# Note that $a is 6, see above.
let "t = a


Advanced Bash-Scripting Guide
a='$b'
b='$c'
c=d
echo $a
eval echo $a
eval eval echo $a

#
#
#
#
#
#

$b
First level.
$c
Second level.
d
Third level.

# Thank you, E. Choroba.

Example 15-11. Showing the effect of eval
#!/bin/bash
# Exercising "eval" ...
y=`eval ls -l`
echo $y
echo
echo "$y"

# Simil


Advanced Bash-Scripting Guide
#!/bin/bash
# arr-choice.sh
# Passing arguments to a function to select
#+ one particular variable out of a group.
arr0=( 10 11 12 13 14 15 )
arr1=( 20 21 22 23 24 25 )
arr2=( 30 31 32 33 34 35 )
#
0 1 2 3 4 5

Element number (zero-indexed)

choose_array ()
{
eval array


Advanced Bash-Scripting Guide
#+ of \$$
#+ as an indirect variable reference.
(( param ++ ))
done

# On to the next.

exit $?
# =================================================
$ sh echo-params.sh first
Command-line parameter $1
Command-line parameter $2
Command-line parameter $3
Command-line param


Advanced Bash-Scripting Guide
setvar_rot_13()
# "rot13" scrambling
{
local varname=$1 varvalue=$2
eval $varname='$(echo "$varvalue" | tr a-z n-za-m)'
}

setvar_rot_13 var "foobar"
echo $var

# Run "foobar" through rot13.
# sbbone

setvar_rot_13 var "$var"

# Run "sbbone" through rot13.
# Back to ori


Advanced Bash-Scripting Guide
echo $_
# +++++
# Flags set in script.
echo $# hB
#
Anomalous behavior?
echo
echo "Positional parameters after set \`uname -a\` :"
# $1, $2, $3, etc. reinitialized to result of `uname -a`
echo "Field #1 of 'uname -a' = $1"
echo "Field #2 of 'uname -a' = $2"
echo "Field


Advanced Bash-Scripting Guide
#
#
#+
#

What happens if you don't? Try it.
And, why use the new IFS -- a colon -- in line 17,
to append to the loop variable?
What is the purpose of this?

exit 0
$ ./revposparams.sh
### k0 =
### k = a b
### k0 = a b
### k = c a b
### k0 = c a b
### k = d e c a b
3
d


Advanced Bash-Scripting Guide
echo "second parameter = $second_param"
echo "remaining parameters = $remaining_params"

# two
# three four five

echo; echo
# Again.
set -- $variable
first_param=$1
second_param=$2
echo "first parameter = $first_param"
echo "second parameter = $second_param"

# one
# t


Advanced Bash-Scripting Guide
The export [60] command makes available variables to all child processes of the running script or
shell. One important use of the export command is in startup files, to initialize and make accessible
environmental variables to subsequent user processes.
Unfortunately, t


Advanced Bash-Scripting Guide

bash$ var=(a b); export var; echo ${var[0]}
a

A variable to be exported may require special treatment. See Example L-2.
declare, typeset
The declare and typeset commands specify and/or restrict properties of variables.
readonly
Same as declare -r, sets a variable as r


Advanced Bash-Scripting Guide
shift $(($OPTIND - 1))
# Move argument pointer to next.
# All this is not nearly as complicated as it looks .

Example 15-21. Using getopts to read the options/arguments passed to a script
#!/bin/bash
# ex33.sh: Exercising getopts and OPTIND
#
Script modified 10/09/03 a


Advanced Bash-Scripting Guide
exit $?
#
As Bill Gradwohl states,
# "The getopts mechanism allows one to specify: scriptname -mnop -mnop
#+ but there is no reliable way to differentiate what came
#+ from where by using OPTIND."
# There are, however, workarounds.

Script Behavior
source, . (dot comman


Advanced Bash-Scripting Guide
print_message ()
{
# Echoes any message passed to it.
if [ -z "$1" ]
then
return 1
# Error, if argument missing.
fi
echo
until [ -z "$1" ]
do
# Step through arguments passed to function.
echo -n "$1"
# Echo args one at a time, suppressing line feeds.
echo -n " "
# Inser


Advanced Bash-Scripting Guide
do
. $0

# Script "sources" itself, rather than calling itself.
# ./$0 (which would be true recursion) doesn't work here. Why?

done
#
#+
#+
#+
#
#
#
#+

What occurs here is not actually recursion,
since the script effectively "expands" itself, i.e.,
generates a new sec


Advanced Bash-Scripting Guide
#!/bin/bash
# self-exec.sh
# Note: Set permissions on this script to 555 or 755,
#
then call it with ./self-exec.sh or sh ./self-exec.sh.
echo
echo "This line appears ONCE in the script, yet it keeps echoing."
echo "The PID of this instance of the script is still $$."
#


Advanced Bash-Scripting Guide
A caller command can also return caller information from a script sourced within another script.
Analogous to a function, this is a "subroutine call."
You may find this command useful in debugging.
Commands
true
A command that returns a successful (zero) exit status, bu


Advanced Bash-Scripting Guide
bash$ type '['
[ is a shell builtin
bash$ type -a '['
[ is a shell builtin
[ is /usr/bin/[

bash$ type type
type is a shell builtin

The type command can be useful for testing whether a certain command exists.
hash [cmds]
Records the path name of specified commands -- i


Advanced Bash-Scripting Guide
The fg command switches a job running in the background into the foreground. The bg command
restarts a suspended job, and runs it in the background. If no job number is specified, then the fg or bg
command acts upon the currently running job.
wait
Suspend script executi


Advanced Bash-Scripting Guide
ls -l &
echo "Done."
bash$ ./test.sh
Done.
[bozo@localhost test-scripts]$ total 1
-rwxr-xr-x
1 bozo
bozo
_

34 Oct 11 15:09 test.sh

As Walter Brameld IV explains it:

As far as I can tell, such scripts don't actually hang. It ju
seems that they do because the backgroun


Advanced Bash-Scripting Guide
Example 15-27. A script that kills itself
#!/bin/bash
# self-destruct.sh
kill $$

# Script kills its own process here.
# Recall that "$$" is the script's PID.

echo "This line will not echo."
# Instead, the shell sends a "Terminated" message to stdout.
exit 0

# Normal


Advanced Bash-Scripting Guide
compiled object file. [67].
autoload
This is a port to Bash of the ksh autoloader. With autoload in place, a function with an autoload
declaration will load from an external file at its first invocation. [68] This saves system resources.
Note that autoload is not a part


Chapter 16. External Filters, Programs and
Commands
Standard UNIX commands make shell scripts more versatile. The power of scripts comes from coupling
system commands and shell directives with simple programming constructs.

16.1. Basic Commands
The first commands a novice learns
ls
The basic file "


Advanced Bash-Scripting Guide
else
IMAGE_DIRECTORY=$1
fi
# Create a "table of contents" file.
ls -lRF $IMAGE_DIRECTORY > $IMAGE_DIRECTORY/$CONTENTSFILE
# The "l" option gives a "long" file listing.
# The "R" option makes the listing recursive.
# The "F" option marks the file types (directories get a


Advanced Bash-Scripting Guide
This is line 2.
This is line 1.

bash$ rev file1.txt
.1 enil si sihT
.2 enil si sihT

cp
This is the file copy command. cp file1 file2 copies file1 to file2, overwriting file2 if
it already exists (see Example 16-6).
Particularly useful are the -a archive flag (for copy


Advanced Bash-Scripting Guide
bash$ rm ./-badname

When used with the recursive flag -r, this command removes files all the way down
the directory tree from the current directory. A careless rm -rf * can wipe out a big
chunk of a directory structure.
rmdir
Remove directory. The directory must be emp


Advanced Bash-Scripting Guide
#+ but you can't read, write, or execute the symlinks.
# These restrictions do not apply to root.

chattr
Change file attributes. This is analogous to chmod above, but with different options and a different
invocation syntax, and it works only on ext2/ext3 filesystems.


Advanced Bash-Scripting Guide
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


Advanced Bash-Scripting Guide
Carries out COMMAND on each file that find matches. The command sequence terminates with ; (the
";" is escaped to make certain the shell passes it to find literally, without interpreting it as a special
character).
bash$ find ~/ -name '*.txt'
/home/bozo/.kde/share/apps/


Advanced Bash-Scripting Guide
Example 16-3. Badname, eliminate file names in current directory containing bad characters
and whitespace.
#!/bin/bash
# badname.sh
# Delete filenames in current directory containing bad characters.
for filename in *
do
badname=`echo "$filename" | sed -n /[\+\{\;\"\\\=\


Advanced Bash-Scripting Guide
inum=`ls -i | grep "$1" | awk '{print $1}'`
# inum = inode (index node) number of file
# ----------------------------------------------------------------------# Every file has an inode, a record that holds its physical address info.
# -----------------------------------


Advanced Bash-Scripting Guide
bash$ ls -l | xargs
total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1 -rw-rw-r-- 1 bozo bozo 0 Jan...

bash$ find ~/mail -type f | xargs grep "Linux"
./misc:User-Agent: slrn/0.9.8.1 (Linux)
./sent-mail-jul-2005: hosted by the Linux Documentation Project.
./sent-mail-j


Advanced Bash-Scripting Guide
# 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.

Example 16-5. Logfile


Advanced Bash-Scripting Guide
E_NOARGS=85
if [ -z "$1" ]
# Exit if no argument given.
then
echo "Usage: `basename $0` directory-to-copy-to"
exit $E_NOARGS
fi
ls
#
#
#
#
#
#
#
#+
#+
#
#
#+
#+

. | xargs -i -t cp ./{} $1
^^ ^^
^^
-t is "verbose" (output command-line to stderr) option.
-i is "replace s


Advanced Bash-Scripting Guide
# Can grep "$PROCESS_NAME" be replaced by pidof "$PROCESS_NAME"?
# --------------------------------------------------------------exit $?
# The "killall" command has the same effect as this script,
#+ but using it is not quite as educational.

Example 16-8. Word frequenc


Advanced Bash-Scripting Guide
expr 3 + 5
returns 8
expr 5 % 3
returns 2
expr 1 / 0
returns the error message, expr: division by zero
Illegal arithmetic operations not allowed.
expr 5 \* 3
returns 15
The multiplication operator must be escaped when used in an arithmetic expression with
expr.
y=`expr


Advanced Bash-Scripting Guide
x=24
y=25
b=`expr $x = $y`
echo "b = $b"
echo

# Test equality.
# 0 ( $x -ne $y )

a=3
b=`expr $a \> 10`
echo 'b=`expr $a \> 10`, therefore...'
echo "If a > 10, b = 0 (false)"
echo "b = $b"
# 0 ( 3 ! -gt 10 )
echo
b=`expr $a \< 10`
echo "If a < 10, b = 1 (true)"
echo "b


Advanced Bash-Scripting Guide
#
==
==
#+ trigger substring match.
echo "The digits at the beginning of \"$a\" are \"$b\"."
echo
exit 0

The : (null) operator can substitute for match. For example, b=`expr $a : [0-9]*` is
the exact equivalent of b=`expr match $a [0-9]*` in the above listing.
#!/bin/b


Advanced Bash-Scripting Guide
# From Peter Knowles' "booklistgen.sh" script
#+ for converting files to Sony Librie/PRS-50X format.
# (http://booklistgensh.peterknowles.com)

Perl, sed, and awk have far superior string parsing facilities. A short sed or awk "subroutine" within a script
(see Section 3


Advanced Bash-Scripting Guide
#!/bin/bash
# date-calc.sh
# Author: Nathan Coulter
# Used in ABS Guide with permission (thanks!).
MPHR=60
HPD=24

# Minutes per hour.
# Hours per day.

diff () {
printf '%s' $(( $(date -u -d"$TARGET" +%s) $(date -u -d"$CURRENT" +%s)))
#
%d = day of month.
}

CURRENT=$(


Advanced Bash-Scripting Guide
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, fo


Advanced Bash-Scripting Guide
The at job control command executes a given set of commands at a specified time. Superficially, it
resembles cron, however, at is chiefly useful for one-time execution of a command set.
at 2pm January 15 prompts for a set of commands to execute at that time. These comma


Advanced Bash-Scripting Guide
The hwclock command accesses or adjusts the machine's hardware clock. Some options require root
privileges. The /etc/rc.d/rc.sysinit startup file uses hwclock to set the system time from
the hardware clock at bootup.
The clock command is a synonym for hwclock.

16.4. Te


Advanced Bash-Scripting Guide
of a document needs to be examined.

Example 16-12. Word Frequency Analysis
#!/bin/bash
# wf.sh: Crude word frequency analysis on a text file.
# This is a more efficient version of the "wf2.sh" script.

# Check for input file on command-line.
ARGS=1
E_BADARGS=85
E_NOFIL


Advanced Bash-Scripting Guide
# 1)
#+
# 2)
#+

Add 'sed' commands to filter out other punctuation,
such as semicolons.
Modify the script to also filter out multiple spaces and
other whitespace.

bash$ cat testfile
This line occurs only once.
This line occurs twice.
This line occurs twice.
This line


Advanced Bash-Scripting Guide
# Thanks, Oleg Philon for suggesting this.

cut -d ' ' -f2,3 filename is equivalent to awk -F'[ ]' '{ print $2, $3 }'
filename
It is even possible to specify a linefeed as a delimiter. The trick is to actually embed a
linefeed (RETURN) in the command sequence.
bash$ cut


Advanced Bash-Scripting Guide
#!/bin/bash
# script-detector.sh: Detects scripts within a directory.
TESTCHARS=2
SHABANG='#!'

# Test first 2 characters.
# Scripts begin with a "sha-bang."

for file in * # Traverse all the files in current directory.
do
if [[ `head -c$TESTCHARS "$file"` = "$SHABANG"


Advanced Bash-Scripting Guide
# head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p'
# ----------------------------------> |
# Assume output up to "sed" --------> |
# is 0000000 1198195154\n
#
#
#+
#
#
#
#
#+
#
#
#+

sed begins reading characters: 0000000 1198195154\n.
Here it finds a newline c


Advanced Bash-Scripting Guide
Example 16-15. Using tail to monitor the system log
#!/bin/bash
filename=sys.log
cat /dev/null > $filename; echo "Creating / cleaning out file."
# Creates file if it does not already exist,
#+ and truncates it to zero length if it does.
# : > filename
and
> filename als


Advanced Bash-Scripting Guide
The -l option lists only the files in which matches were found, but not the matching lines.
The -r (recursive) option searches files in the current working directory and all subdirectories below
it.
The -n option lists the matching lines, together with line numbers.
bas


Advanced Bash-Scripting Guide
GREP_OPTS="-H -A 5 --color"
TARGETSTR="^From"

# Show file, plus extra context lines
#+ and display "From" in color.
# "From" at beginning of line.

for file in $MAILDIR
# No quoting of variable.
do
grep $GREP_OPTS "$TARGETSTR" "$file"
#
^^^^^^^^^^
# Again, do not quote


Advanced Bash-Scripting Guide
echo "Usage: `basename $0` pattern"
exit $E_BADARGS
fi
echo
for file in *
# Traverse all files in $PWD.
do
output=$(sed -n /"$1"/p $file) # Command substitution.
if [ ! -z "$output" ]
# What happens if "$output" is not quoted?
then
echo -n "$file: "
echo "$output"
fi
#


Advanced Bash-Scripting Guide
#!/bin/bash
# cw-solver.sh
# This is actually a wrapper around a one-liner (line 46).
# Crossword puzzle and anagramming word game solver.
# You know *some* of the letters in the word you're looking for,
#+ so you need a list of all valid words
#+ with the known letters


Advanced Bash-Scripting Guide
wellington
workingman
workingmen

egrep -- extended grep -- is the same as grep -E. This uses a somewhat different, extended set of
Regular Expressions, which can make the search a bit more flexible. It also allows the boolean | (or)
operator.
bash $ egrep 'matches|Matc


Advanced Bash-Scripting Guide
echo "Examples: Abandon, Dictionary, Marking, etc."
exit $E_BADARGS
fi

if [ -z "$2" ]

# May specify different dictionary
#+ as an argument to this script.

then
dictfile=$DEFAULT_DICTFILE
else
dictfile="$2"
fi
# --------------------------------------------------------


Advanced Bash-Scripting Guide
To search bzipped files, use bzgrep.
look
The command look works like grep, but does a lookup on a "dictionary," a sorted word list. By
default, look searches for a match in /usr/dict/words, but a different dictionary file may be
specified.

Example 16-20. Checking word


Advanced Bash-Scripting Guide
wc
wc gives a "word count" on a file or I/O stream:
bash $ wc /usr/share/doc/sed-4.1.2/README
13 70 447 README
[13 lines 70 words 447 characters]

wc -w gives only the word count.
wc -l gives only the line count.
wc -c gives only the byte count.
wc -m gives only the cha


Advanced Bash-Scripting Guide
Either tr "A-Z" "*"


Advanced Bash-Scripting Guide
#

Hint: Use either the "case" or "select" command.

Example 16-22. lowercase: Changes all filenames in working directory to lowercase.
#!/bin/bash
#
# Changes every filename in working directory to all lowercase.
#
# Inspired by a script of John Dubois,
#+ which was tr


Advanced Bash-Scripting Guide
fi
NEWFILENAME=$1.unx
CR='\015'

#
#
#
#

Carriage return.
015 is octal ASCII code for CR.
Lines in a DOS text file end in CR-LF.
Lines in a UNIX text file end in LF only.

tr -d $CR < $1 > $NEWFILENAME
# Delete CR's and write to new file.
echo "Original DOS text file i


Advanced Bash-Scripting Guide
#
#
#
#
#
#
#

Try this script with something like:
"Nothing so needs reforming as other people's habits."
--Mark Twain
Output is:
"CFPHRCS QF CIIOQ MINFMBRCS EQ FPHIM GIFGUI'Q HETRPQ."
--BEML PZERC

# To reverse the encryption:
# cat "$@" | tr "$key" "A-Z"

# This simp


Advanced Bash-Scripting Guide
exit 0

See also Example 16-5.
A powerful alternative to fmt is Kamil Toman's par utility, available from
http://www.cs.berkeley.edu/~amc/Par/.
col
This deceptively named filter removes reverse line feeds from an input stream. It also attempts to
replace whitespace with


Advanced Bash-Scripting Guide
# 'nl' sees this as line 4 since it does not number blank lines.
# 'cat -n' sees the above line as number 6.
nl `basename $0`
echo; echo

# Now, let's try it with 'cat -n'

cat -n `basename $0`
# The difference is that 'cat -n' numbers the blank lines.
# Note that 'nl -


Advanced Bash-Scripting Guide
Ghostscript (gs) is a GPL-ed Postscript interpreter.
texexec
Utility for processing TeX and pdf files. Found in /usr/bin on many Linux distros, it is actually a
shell wrapper that calls Perl to invoke Tex.
texexec --pdfarrange --result=Concatenated.pdf *pdf
#
#+
#
#

Co


Advanced Bash-Scripting Guide
exit $?

# See also the "maned.sh" script.

See also Example A-39.
lex, yacc
The lex lexical analyzer produces programs for pattern matching. This has been replaced by the
nonproprietary flex on Linux systems.

The yacc utility creates a parser based on a set of specifi


Advanced Bash-Scripting Guide
The mailshar command is a Bash script that uses shar to concatenate multiple files into a single one
for e-mailing. This script supports compression and uuencoding.
ar
Creation and manipulation utility for archives, mainly used for binary object file libraries.
rpm
The


Advanced Bash-Scripting Guide
#!/bin/bash
# Copying a directory tree using cpio.
# Advantages of using 'cpio':
#
Speed of copying. It's faster than 'tar' with pipes.
#
Well suited for copying special files (named pipes, etc.)
#+ that 'cp' may choke on.
ARGS=2
E_BADARGS=65
if [ $# -ne "$ARGS" ]
then


Advanced Bash-Scripting Guide
# Exercise:
# Add check for whether 1) "target-file" exists and
#+
2) it is an rpm archive.
# Hint:
Parse output of 'file' command.

pax
The pax portable archive exchange toolkit facilitates periodic file backups and is designed to be
cross-compatible between various fl


Advanced Bash-Scripting Guide
Yet another compression (squeeze) utility, a filter that works only on sorted ASCII word lists. It uses
the standard invocation syntax for a filter, sq < input-file > output-file. Fast, but not nearly as
efficient as gzip. The corresponding uncompression filter is unsq,


Advanced Bash-Scripting Guide
#!/bin/bash
# strip-comment.sh: Strips out the comments (/* COMMENT */) in a C program.
E_NOARGS=0
E_ARGERROR=66
E_WRONG_FILE_TYPE=67
if [ $# -eq "$E_NOARGS" ]
then
echo "Usage: `basename $0` C-program-file" >&2 # Error message to stderr.
exit $E_ARGERROR
fi
# Test for


Advanced Bash-Scripting Guide
*"C program text"*) sed -e "s%/\*%${WEIRD}%g;s%\*/%${WEIRD}%g" "$1" \
| tr '\377\n' '\n\377' \
| sed -ne 'p;n' \
| tr -d '\n' | tr '\377' '\n';;
*) usage;;
esac
#
#
#
#
#
#
#+
#+

This is still fooled by things like:
printf("/*");
or
/* /* buggy embedded comment */
To h


Advanced Bash-Scripting Guide
#
#+
#
#
#
#

Note: For this to work, you must create a "whatis" database
with /usr/sbin/makewhatis.
You may wish to redirect output of this script, like so:
./what.sh >>whatis.db
or view it a page at a time on stdout,
./what.sh | less

See also Example 11-3.
vdir
Show


Advanced Bash-Scripting Guide
user:accountant:rwgroup::rwmask::rwother::r--

readlink
Disclose the file that a symbolic link points to.
bash$ readlink /usr/bin/awk
../../bin/gawk

strings
Use the strings command to find printable strings in a binary or data file. It will list sequences of
printable


Advanced Bash-Scripting Guide
# Translate output of 'strings' command with multiple passes of 'tr'.
# "tr A-Z a-z" converts to lowercase.
# "tr '[:space:]'" converts whitespace characters to Z's.
# "tr -cs '[:alpha:]' Z" converts non-alphabetic characters to Z's,
#+ and squeezes multiple consecutive


Advanced Bash-Scripting Guide
patch: flexible versioning utility. Given a difference file generated by diff, patch can upgrade a
previous version of a package to a newer version. It is much more convenient to distribute a relatively
small "diff" file than the entire body of a newly revised package.


Advanced Bash-Scripting Guide
two files, cmp merely shows at what point they differ.
Like diff, cmp 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.
Example 16-35. Using cmp to compare two files within


Advanced Bash-Scripting Guide
-12 suppresses both columns 1 and 2, etc.
This command is useful for comparing "dictionaries" or word lists -- sorted text files with one word
per line.
Utilities
basename
Strips the path information from a file name, printing only the file name. The construction
basen


Advanced Bash-Scripting Guide
#
#
#
#
#
#

Line
Line
Line
Line
Line
Line

15
16
17
18
19
20

cat "$OUTPREFIX"* > "$0.copy"
rm "$OUTPREFIX"*

# Concatenate the chunks.
# Get rid of the chunks.

exit $?

Encoding and Encryption
sum, cksum, md5sum, sha1sum
These are utilities for generating checksums.


Advanced Bash-Scripting Guide
# Write directory name to first line of file.
md5sum "$directory"/* >> "$dbfile"
# Append md5 checksums and filenames.
}
check_database ()
{
local n=0
local filename
local checksum
# ------------------------------------------- #
# This file check should be unnecessary,


Advanced Bash-Scripting Guide
let "n+=1"
done


Advanced Bash-Scripting Guide
This is especially useful where MIME (multimedia) encoding is not available.
uudecode
This reverses the encoding, decoding uuencoded files back into the original binaries.

Example 16-39. Uudecoding encoded files
#!/bin/bash
# Uudecodes all uuencoded files in current wo


Advanced Bash-Scripting Guide
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


Advanced Bash-Scripting Guide
#
#
#+
#
#+

or something similar...
Creates a file of that name in the current working directory
with 600 file permissions.
A "umask 177" is therefore unnecessary,
but it's good programming practice anyhow.

make
Utility for building and compiling binary packages. This


Advanced Bash-Scripting Guide
bash$ host surfacemail.com
surfacemail.com. has address 202.92.42.236

ipcalc
Displays IP information for a host. With the -h option, ipcalc does a reverse DNS lookup, finding the
name of the host (server) from the IP address.
bash$ ipcalc -h 202.92.42.236
HOSTNAME=surf


Advanced Bash-Scripting Guide
#!/bin/bash
# spam-lookup.sh: Look up abuse contact to report a spammer.
# Thanks, Michael Zick.
# Check for command-line arg.
ARGCOUNT=1
E_WRONGARGS=65
if [ $# -ne "$ARGCOUNT" ]
then
echo "Usage: `basename $0` domain-name"
exit $E_WRONGARGS
fi

dig +short $1.contacts.a


Advanced Bash-Scripting Guide
# Tested with version: 9.2.4rc5
# Uses functions.
# Uses IFS to parse strings by assignment into arrays.
# And even does something useful: checks e-mail blacklists.
# Use the domain.name(s) from the text body:
# http://www.good_stuff.spammer.biz/just_ignore_everything_e


Advanced Bash-Scripting Guide
# Need to get the IP address from the name.
echo 'Get address of: '$1
ip_adr=$(dig +short $1)
dns_reply=${ip_adr:-' no answer '}
echo ' Found address: '${dns_reply}
# A valid reply is at least 4 digits plus 3 dots.
if [ ${#ip_adr} -gt 6 ]
then
echo
declare query
# Parse


Advanced Bash-Scripting Guide
# 1) Check arguments to script,
#
and exit with appropriate error message if necessary.
# 2) Check if on-line at invocation of script,
#
and exit with appropriate error message if necessary.
# 3) Substitute generic variables for "hard-coded" BHL domains.
# 4) Set a time


Advanced Bash-Scripting Guide
bash$ finger
Login Name
bozo
Bozo Bozeman
bozo
Bozo Bozeman
bozo
Bozo Bozeman

Tty
tty1
ttyp0
ttyp1

Idle
8

Login Time
Office
Jun 25 16:59
Jun 25 16:59
Jun 25 17:07

Office Phone
(:0)
(:0.0)
(:0.0)

bash$ finger bozo
Login: bozo
Name: Bozo Bozeman
Directory: /home/bozo


Advanced Bash-Scripting Guide
--cu: Call Up a remote system and connect as a simple terminal. It is a sort of dumbed-down version of
telnet. This command is part of the uucp package.
telnet
Utility and protocol for connecting to a remote host.
The telnet protocol contains security holes and should t


Advanced Bash-Scripting Guide
# Exercises:
# --------#
# 1) Add a test to ensure the user running the script is on-line.
#
(Hint: parse the output of 'ps -ax' for "ppp" or "connect."
#
# 2) Modify this script to fetch the local weather report,
#+
taking the user's zip code as an argument.

See also


Advanced Bash-Scripting Guide

#
#
#
#

General rsync options
-r: recursive download
-t: reserve time
-v: verbose

OPTS="-rtv --delete-excluded --delete-after --partial"
# rsync include pattern
# Leading slash causes absolute path name match.
INCLUDE=(
"/4/i386/kde-i18n-Chinese*"
#
^
^
# Quoting is


Advanced Bash-Scripting Guide
exit $E_RETURN
fi
echo "Process $PID not found. Start new process . . ."
fi
}

# Set overall file update range starting from root or $URL,
#+ according to above patterns.
set_range () {
include=
exclude=
for p in "${INCLUDE[@]}"; do
include="$include --include \"$p\""
d


Advanced Bash-Scripting Guide
elif [ "$pkg_name" != "$previous" ]; then
# A new pkg found.
echo $pre_file >> $TMP
# Output latest file.
previous=$pkg_name
# Save current.
pre_date=$cur_date
pre_file=$cur_file
elif [ "$cur_date" -gt "$pre_date" ]; then
# If same pkg, but newer,
pre_date=$cur_date
#+


Advanced Bash-Scripting Guide
if [ "$RET" -eq 0 ]; then
/usr/bin/logger -t ${0##*/} "Fedora update mirrored successfully."
else
/usr/bin/logger -t ${0##*/} \
"Fedora update mirrored with failure code: $RET"
fi
exit $RET

See also Example A-32.
Using rcp, rsync, and similar utilities with security im


Advanced Bash-Scripting Guide
# A basic, write stdout (local) command.
ls -l
# Now the same basic command on a remote machine.
# Pass a different 'USERNAME' 'HOSTNAME' if desired:
USER=${USERNAME:-$(whoami)}
HOST=${HOSTNAME:-$(hostname)}
# Now excute the above command-line on the remote host,
#+ wit


Advanced Bash-Scripting Guide
This stripped-down command-line mail client works fine as a command embedded in a script.

Example 16-45. A script that mails itself
#!/bin/sh
# self-mailer.sh: Self-mailing script
adr=${1:-`whoami`}
# Default to current user, if not specified.
# Typing 'self-mailer.sh


Advanced Bash-Scripting Guide

16.7. Terminal Control Commands
Command affecting the console or terminal
tput
Initialize terminal and/or fetch information about it from terminfo data. Various options permit certain
terminal operations: tput clear is the equivalent of clear; tput reset is the equival


Advanced Bash-Scripting Guide
Echoes commands necessary to set $TERM and $TERMCAP to duplicate the size (dimensions) of the
current terminal.
bash$ resize
set noglob;
setenv COLUMNS '80';
setenv LINES '24';
unset noglob;

script
This utility records (saves to a file) all the user keystrokes at the c


Advanced Bash-Scripting Guide
then
printf %5d $n
fi
#
^ Five positions per number suffices.
done
#
For a higher $CEILING, adjust upward, as necessary.
echo
exit

bc
Bash can't handle floating point calculations, and it lacks operators for certain important mathematical
functions. Fortunately, bc gal


Advanced Bash-Scripting Guide

top=$(echo "scale=9; $principal*$interest_rate^$term" | bc)
#
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
Standard formula for figuring interest.
echo; echo "Please be patient. This may take a while."
let "months = $term - 1"
# ===================================================


Advanced Bash-Scripting Guide

# Exercises:
#
1) Filter
#
2) Filter
#
3) If you
#+
expand

input to permit commas in principal amount.
input to permit interest to be entered as percent or decimal.
are really ambitious,
this script to print complete amortization tables.

Example 16-48. Base Conversio


Advanced Bash-Scripting Guide
0*)
ibase=8;;
# octal
[1-9]*)
ibase=10;;
# decimal
*)
Msg "illegal number $i - ignored"
continue;;
esac
# Remove prefix, convert hex digits to uppercase (bc needs this).
number=`echo "$i" | sed -e 's:^0[bBxX]::' | tr '[a-f]' '[A-F]'`
# ==> Uses ":" as sed separator, rat


Advanced Bash-Scripting Guide
commands to bc.
variable=`bc


Advanced Bash-Scripting Guide
{
# c = sqrt( a^2 + b^2 )
hyp=$(bc -l


Advanced Bash-Scripting Guide
DIMENSION=10000

# Length of each side of the plot.
# Also sets ceiling for random integers generated.

MAXSHOTS=1000

# Fire this many shots.
# 10000 or more would be better, but would take too long.
# Scaling factor.

PMULTIPLIER=4.0

declare -r M_PI=3.141592654
# Act


Advanced Bash-Scripting Guide
then
echo -n "SPLASH!
((splashes++))
else
echo -n "THUD!
((thuds++))
fi

"

"

Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc)
# Multiply ratio by 4.0.
echo -n "PI ~ $Pi"
echo
done
echo
echo "After $shots shots, PI looks like approximately
$Pi"
# Tends to run a


Advanced Bash-Scripting Guide
#!/bin/bash
# hexconvert.sh: Convert a decimal number to hexadecimal.
E_NOARGS=85 # Command-line arg missing.
BASE=16
# Hexadecimal.
if [ -z "$1" ]
then
# Need a command-line argument.
echo "Usage: $0 number"
exit $E_NOARGS
fi
# Exercise: add argument validity checking.


Advanced Bash-Scripting Guide
if [ -z $1 ]
then
echo "Usage: $0 number"
exit $E_NOARGS
fi
if [ "$1" -lt "$MIN" ]
then
echo "Number to factor must be $MIN or greater."
exit $E_TOOSMALL
fi
# Exercise: Add type checking (to reject non-integer arg).
echo "Factors of $1:"
# ------------------------------


Advanced Bash-Scripting Guide
exit
# Exercise: Rewrite this script using 'bc' rather than awk.
#
Which method is more intuitive?

16.9. Miscellaneous Commands
Command that fit in no special category
jot, seq
These utilities emit a sequence of integers, with a user-selectable increment.
The default s


Advanced Bash-Scripting Guide
echo; echo
BEGIN=75
END=80
for a in `seq $BEGIN $END`
# Giving "seq" two arguments starts the count at the first one,
#+ and continues until it reaches the second.
do
echo -n "$a "
done
# 75 76 77 78 79 80
echo; echo
BEGIN=45
INTERVAL=5
END=80
for a in `seq $BEGIN $INTE


Advanced Bash-Scripting Guide
echo
echo Usage: `basename $0` file letters
echo Note: `basename $0` arguments are case sensitive.
echo Example: `basename $0` foobar.txt G n U L i N U x.
echo
}
# Checks number of arguments.
if [ $# -lt $MINARGS ]; then
echo
echo "Not enough arguments."
echo
show_help


Advanced Bash-Scripting Guide
Example 16-56. Using getopt to parse command-line options
#!/bin/bash
# Using getopt
# Try the following when invoking this script:
#
sh ex33a.sh -a
#
sh ex33a.sh -abc
#
sh ex33a.sh -a -b -c
#
sh ex33a.sh -d
#
sh ex33a.sh -dXYZ
#
sh ex33a.sh -d XYZ
#
sh ex33a.sh -abcd
#


Advanced Bash-Scripting Guide
The cron daemon invokes run-parts to run the scripts in the /etc/cron.* directories.
yes
In its default behavior the yes command feeds a continuous string of the character y followed by a
line feed to stdout. A control-C terminates the run. A different output string may


Advanced Bash-Scripting Guide
Note that banner has been dropped from many Linux distros.
printenv
Show all the environmental variables set for a particular user.
bash$ printenv | grep HOME
HOME=/home/bozo

lp
The lp and lpr commands send file(s) to the print queue, to be printed as hard copy. [81] T


Advanced Bash-Scripting Guide
# Used in ABS Guide with permission (thanks!).
mkfifo pipe1
mkfifo pipe2

# Yes, pipes can be given names.
# Hence the designation "named pipe."

(cut -d' ' -f1 | tr "a-z" "A-Z") >pipe2 $filename.uppercase
#
lcase
# For lower case conversion

Some basic options to dd a


Advanced Bash-Scripting Guide
How many blocks of data to skip in INFILE before starting to copy. This is useful when the
INFILE has "garbage" or garbled data in its header or when it is desirable to copy only a
portion of the INFILE.
seek=BLOCKS
How many blocks of data to skip in OUTFILE before sta


Advanced Bash-Scripting Guide
To demonstrate just how versatile dd is, let's use it to capture keystrokes.

Example 16-59. Capturing Keystrokes
#!/bin/bash
# dd-keypress.sh: Capture keystrokes without needing to press ENTER.

keypresses=4

# Number of keypresses to capture.

old_tty_setting=$(stty -


Advanced Bash-Scripting Guide
Example 16-60. Securely deleting a file
#!/bin/bash
# blot-out.sh: Erase "all" traces of a file.
#
#+
#
#+

This script overwrites a target file alternately
with random bytes, then zeros before finally deleting it.
After that, even examining the raw disk sectors by conv


Advanced Bash-Scripting Guide
done

rm -f $file
sync

# Finally, delete scrambled and shredded file.
# Flush buffers a final time.

echo "File \"$file\" blotted out and deleted."; echo

exit 0
#
#+
#
#+

This is a fairly secure, if inefficient and slow method
of thoroughly "shredding" a file.
The "s


Advanced Bash-Scripting Guide
Disassembly of section .init:
080490bc :
80490bc:
55
80490bd:
89 e5
. . .

push
mov

%ebp
%esp,%ebp

mcookie
This command generates a "magic cookie," a 128-bit (32-character) pseudorandom hexadecimal
number, normally used as an authorization "signature" by the X server.


Advanced Bash-Scripting Guide
Example 16-62. Converting meters to miles
#!/bin/bash
# unit-conversion.sh

convert_units () # Takes as arguments the units to convert.
{
cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}')
# Strip off everything except the actual conversion factor.
echo "$c


Advanced Bash-Scripting Guide
The zenity utility is adept at displaying GTK+ dialog widgets and very suitable for scripting purposes.
doexec
The doexec command enables passing an arbitrary list of arguments to a binary executable. In
particular, passing argv[0] (which corresponds to $0 in a script)


Chapter 17. System and Administrative Commands
The startup and shutdown scripts in /etc/rc.d illustrate the uses (and usefulness) of many of these
comands. These are usually invoked by root and used for system maintenance or emergency filesystem
repairs. Use with caution, as some of these commands m


Advanced Bash-Scripting Guide
bash$ echo $UID
501

The id command shows the effective IDs only when they differ from the real ones.
Also see Example 9-5.
lid
The lid (list ID) command shows the group(s) that a given user belongs to, or alternately, the users
belonging to a given group. May be invoke


Advanced Bash-Scripting Guide
However . . .
bash$ su
Password: ......
bash# whoami
root
bash# logname
bozo

While logname prints the name of the logged in user, whoami gives the name of the
user attached to the current process. As we have just seen, sometimes these are not the
same.
su
Runs a progra


Advanced Bash-Scripting Guide

username=bozo
NEWPASSWORD=security_violation
# Check if bozo lives here.
grep -q "$username" /etc/passwd
if [ $? -ne $SUCCESS ]
then
echo "User $username does not exist."
echo "No password changed."
exit $E_NOSUCHUSER
fi
echo "$NEWPASSWORD" | passwd --stdin "$username"


Advanced Bash-Scripting Guide
Echoes the name (filename) of the current user's terminal. Note that each separate xterm window
counts as a different terminal.
bash$ tty
/dev/pts/1

stty
Shows and/or changes terminal settings. This complex command, used in a script, can control
terminal behavior and t


Advanced Bash-Scripting Guide
A creative use of stty is detecting a user keypress (without hitting ENTER).

Example 17-4. Keypress detection
#!/bin/bash
# keypress.sh: Detect a user keypress ("hot keys").
echo
old_tty_settings=$(stty -g)
stty -icanon
Keypress=$(head -c1)

# Save old settings (why?).


Advanced Bash-Scripting Guide
In non-canonical ("raw") mode, every key hit (including special editing keys such as ctl-H) sends a
character immediately to the controlling process.
The Bash prompt disables both icanon and echo, since it replaces the basic terminal line editor with its
own more elabor


Advanced Bash-Scripting Guide
This is an acronym for "write all," 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 Examp


Advanced Bash-Scripting Guide
This command will fail if the user invoking it does not have read permission for the
/var/log/lastlog file.
lsof
List open files. This command outputs a detailed table of all currently open files and gives
information about their owner, size, the processes associated wi


Advanced Bash-Scripting Guide
servers.
bash$ nc localhost.localdomain 25
220 localhost.localdomain ESMTP Sendmail 8.13.1/8.13.1;
Thu, 31 Mar 2005 15:41:35 -0700

A real-life usage example.

Example 17-5. Checking a remote server for identd
#! /bin/sh
## Duplicate DaveG's ident-scan thingie using net


Advanced Bash-Scripting Guide
kill -HUP $PROC
RP=`expr ${RP} + 1`
shift
done
exit $?
#
#

Notes:
-----

# Try commenting out line 30 and running this script
#+ with "localhost.localdomain 25" as arguments.
# For more of Hobbit's 'nc' example scripts,
#+ look in the documentation:
#+ the /usr/share/d


Advanced Bash-Scripting Guide
bash$ du -ach
1.0k
./wi.sh
1.0k
./tst.sh
1.0k
./random.file
6.0k
.
6.0k
total

df
Shows filesystem usage in tabular form.
bash$ df
Filesystem
/dev/hda5
/dev/hda8
/dev/hda7

1k-blocks
273262
222525
1408796

Used Available Use% Mounted on
92607
166547 36% /
123951
87085 5


Advanced Bash-Scripting Guide
#+ 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
echo
echo
echo
echo
echo

"File
"File
"File
"File
"File
"File

name:
owner:
size:
inode:
type:
access rights:

$file_nam


Advanced Bash-Scripting Guide
bash$ hostid
7f0100

This command allegedly fetches a "unique" serial number for a particular system.
Certain product registration procedures use this number to brand a particular user
license. Unfortunately, hostid only returns the machine network address in
hexadecima


Advanced Bash-Scripting Guide
The size [/path/to/binary] command gives the segment sizes of a binary executable or archive file.
This is mainly of use to programmers.
bash$ size /bin/bash
text
data
bss
495971
22496
17392

dec
535859

hex filename
82d33 /bin/bash

System Logs
logger
Appends a user-ge


Advanced Bash-Scripting Guide
2214
2215
2216
4849

tty4
tty5
tty6
pts/2

Ss+
Ss+
Ss+
S+

0:00
0:00
0:00
0:00

/sbin/mingetty tty4
/sbin/mingetty tty5
/sbin/mingetty tty6
grep mingetty

bash$ pgrep mingetty
2212 mingetty
2213 mingetty
2214 mingetty
2215 mingetty
2216 mingetty

Compare the action of p


Advanced Bash-Scripting Guide
#!/bin/bash
# kill-process.sh
NOPROCESS=2
process=xxxyyyzzz # Use nonexistent process.
# For demo purposes only...
# ... don't want to actually kill any actual process with this script.
#
# If, for example, you wanted to use this script to logoff the Internet,
#
process


Advanced Bash-Scripting Guide
/mnt/usbdrive:

1772c(bozo)

bash$ kill -9 1772
bash$ umount /mnt/usbdrive

The fuser command, invoked with the -n option identifies the processes accessing a port. This is
especially useful in combination with nmap.
root# nmap localhost.localdomain
PORT
STATE SERVICE
2


Advanced Bash-Scripting Guide
root# /sbin/service iptables stop
Flushing firewall rules:
Setting chains to policy ACCEPT: filter
Unloading iptables modules:

[
[
[

OK ]
OK ]
OK ]

Network
nmap
Network mapper and port scanner. This command scans a server to locate open ports and the services
associa


Advanced Bash-Scripting Guide
# ...
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/ .*//')
# Th


Advanced Bash-Scripting Guide

# --- start-tunnel.sh --LOCAL_IP="192.168.1.17"
REMOTE_IP="10.0.5.33"
OTHER_IFACE="192.168.0.100"
REMOTE_NET="192.168.3.0/24"
/sbin/ip tunnel add netb mode gre remote $REMOTE_IP \
local $LOCAL_IP ttl 255
/sbin/ip addr add $OTHER_IFACE dev netb
/sbin/ip link set netb up


Advanced Bash-Scripting Guide
Dump ip packet traffic between hosts bozoville and caduceus:
bash$ tcpdump ip host bozoville and caduceus

Of course, the output of tcpdump can be parsed with certain of the previously discussed text
processing utilities.
Filesystem
mount
Mount a filesystem, usually on


Advanced Bash-Scripting Guide
The newer Linux distros have deprecated mount and umount. The successor, for command-line
mounting of removable storage devices, is gnome-mount. It can take the -d option to mount a device
file by its listing in /dev.
For example, to mount a USB flash drive:
bash$ gnome


Advanced Bash-Scripting Guide
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi
# Use with extreme caution!
# If something goes wrong, you may wipe out your current filesystem.

NEWDISK=/dev/hdb
MOUNTPOINT=/mnt/newdisk

fdisk $NEWDISK
mke2fs -cv $NEWDISK1


Advanced Bash-Scripting Guide
Filesystem check, repair, and debug command set.
fsck: a front end for checking a UNIX filesystem (may invoke other utilities). The actual filesystem
type generally defaults to ext2.
e2fsck: ext2 filesystem checker.
debugfs: ext2 filesystem debugger. One of the uses of


Advanced Bash-Scripting Guide
mkbootdisk
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 --iso option, which uses mkisofs to create a
bootable ISO9660 filesystem image suitable for burning a bo


Advanced Bash-Scripting Guide
Normally, applications create and check for lock files in the /var/lock directory. [89] A script can
test for the presence of a lock file by something like the following.
appname=xyzip
# Application "xyzip" created lock file "/var/lock/xyzip.lock".
if [ -e "/var/lock/$a


Advanced Bash-Scripting Guide
# Run it at your own peril -- it WILL freeze your system.
while true
do
$0 &

#

Endless loop.

done

#
#+
#+
#

This script invokes itself . . .
forks an infinite number of times . . .
until the system freezes up because all resources exhausted.
This is the notorious "


Advanced Bash-Scripting Guide
bash$ lsmod
Module
autofs
opl3
serial_cs
sb
uart401
sound
soundlow
soundcore
ds
i82365
pcmcia_core

Size Used by
9456
2 (autoclean)
11376
0
5456
0 (unused)
34752
0
6384
0 [sb]
58368
0 [opl3 sb uart401]
464
0 [sound]
2800
6 [sb sound]
6448
2 [serial_cs]
22928
2
45984
0 [


Advanced Bash-Scripting Guide
Or even ...
#!/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.
...

ldd
Show shared lib dependencies for an executable file.
bash$ ldd /bin/ls
libc.so.


Advanced Bash-Scripting Guide
for i in /var/lock/subsys/*; do
# --> Standard for/in loop, but since "do" is on same line,
# --> it is necessary to add ";".
# Check if the script is there.
[ ! -f $i ] && continue
# --> This is a clever use of an "and list", equivalent to:
# --> if [ ! -f "$i" ]; then


Part 5. Advanced Topics
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 "push the envelope" in various ways and examine boundary conditions (what happens
when we move into uncharted territory?).
Table of Contents


Advanced Bash-Scripting Guide
37.1. Bash, version 2
37.2. Bash, version 3
37.3. Bash, version 4

Part 5. Advanced Topics

347


Chapter 18. Regular Expressions
. . . the intellectual activity associated with
software development is largely one of gaining
insight.
--Stowe Boyd
To fully utilize the power of shell scripting, you need to master Regular Expressions. Certain commands and
utilities commonly used in scripts, such as


Advanced Bash-Scripting Guide
"^$" matches blank lines.
·
Brackets -- [...] -- enclose a set of characters to match in a single RE.
"[xyz]" matches any one of the characters x, y, or z.
"[c-n]" matches any one of the characters in the range c to n.
"[B-Pk-y]" matches any one of the characters in th


Advanced Bash-Scripting Guide
Run

grep "1133*"

This
This
This
This
This
This
This
This

line
line
line
line
line
line
line
line

bash$
Run
This
This
This
This

contains
contains
contains
contains
contains
contains
contains
contains

grep
grep
line
line
line
line

on this file.

the number
the numb


Advanced Bash-Scripting Guide
· The -- | -- "or" RE operator matches any of a set of alternate characters.
bash$ egrep 're(a|e)d' misc.txt
People who read seem to be better informed than those who do not.
The clarinet produces sound by the vibration of its reed.

Some versions of sed, ed, and ex su


Advanced Bash-Scripting Guide

18.2. Globbing
Bash itself cannot recognize Regular Expressions. Inside scripts, it is commands and utilities -- such as sed
and awk -- that interpret RE's.
Bash does carry out filename expansion [97] -- a process known as globbing -- but this does not use the
standard


Advanced Bash-Scripting Guide
It is possible to modify the way Bash interprets special characters in globbing. A set -f command
disables globbing, and the nocaseglob and nullglob options to shopt change globbing behavior.
See also Example 11-4.

Chapter 18. Regular Expressions

353


Chapter 19. Here Documents
Here and now, boys.
--Aldous Huxley, Island
A here document is a special-purpose code block. It uses a form of I/O redirection to feed a command list to
an interactive program or a command, such as ftp, cat, or the ex text editor.
COMMAND


Advanced Bash-Scripting Guide
Even such unlikely candidates as the vi text editor lend themselves to here documents.

Example 19-2. dummyfile: Creates a 2-line dummy file
#!/bin/bash
# Noninteractive use of 'vi' to edit a file.
# Emulates 'sed'.
E_BADARGS=85
if [ -z "$1" ]
then
echo "Usage: `basenam


Advanced Bash-Scripting Guide
Analogous to "ex scripts" are cat scripts.

Example 19-3. Multi-line message using cat
#!/bin/bash
# 'echo' is fine for printing single line messages,
#+ but somewhat problematic for for message blocks.
#
A 'cat' here document overcomes this limitation.
cat


Advanced Bash-Scripting Guide
This is line 4 of the message.
This is the last line of the message.
ENDOFMESSAGE
# The output of the script will be flush left.
# Leading tab in each line will not show.
# Above 5 lines of "message" prefaced by a tab, not spaces.
# Spaces not affected by


Advanced Bash-Scripting Guide
#!/bin/bash
# upload.sh
#
#+
#
#
#

Upload file pair (Filename.lsm, Filename.tar.gz)
to incoming directory at Sunsite/UNC (ibiblio.org).
Filename.tar.gz is the tarball itself.
Filename.lsm is the descriptor file.
Sunsite requires "lsm" file, otherwise will bounce contri


Advanced Bash-Scripting Guide
Endofmessage
#
#
#+
#
#

No parameter substitution when the "limit string" is quoted or escaped.
Either of the following at the head of the here document would have
the same effect.
cat


Advanced Bash-Scripting Guide
a=7
b=3
let "c = $a * $b"
echo "c = $c"
exit 0
EOF
) > $OUTFILE
# ----------------------------------------------------------# Quoting the 'limit string' prevents variable expansion
#+ within the body of the above 'here document.'
# This permits outputting literal string


Advanced Bash-Scripting Guide
GetPersonalData


Advanced Bash-Scripting Guide
#

Note that the use of of colon, above, is optional.

echo "Just before commented-out code block."
# The lines of code between the double-dashed lines will not execute.
# ===================================================================
:


Advanced Bash-Scripting Guide

:


Advanced Bash-Scripting Guide
#!/bin/bash
echo "----------------------------------------------------------------------"
cat


Advanced Bash-Scripting Guide
It consists of nothing more than COMMAND


Advanced Bash-Scripting Guide
read -p "File: " file
# -p arg to 'read' displays prompt.
if [ ! -e "$file" ]
then
# Bail out if no such file.
echo "File $file not found."
exit $E_NOSUCHFILE
fi
read -p "Title: " title
cat - $file


Advanced Bash-Scripting Guide
unset date
elif (( body ))
then
(( match ))
# echo "$mail"
# Uncomment above line if you want entire body of message to display.
elif [[ $mail ]]; then
IFS=: read -r header value


Chapter 20. I/O Redirection
There are always three default files [101] open, stdin (the keyboard), stdout (the screen), and stderr
(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, scrip


Advanced Bash-Scripting Guide
# "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


Advanced Bash-Scripting Guide
read -n 4 &3
#
exec 3>&#
cat File
#
# Random access, by golly.

Read only 4 characters.
Write a decimal point there.
Close fd 3.
==> 1234.67890

|
# Pipe.
# General purpose process and command chaining tool.
# Similar to ">", but more general in effect.
# Useful for cha


Advanced Bash-Scripting Guide
# Redirecting only stderr to a pipe.
exec 3>&1
ls -l 2>&1 >&3 3>&- | grep bad 3>&#
^^^^
^^^^
exec 3>&-

# Save current "value" of stdout.
# Close fd 3 for 'grep' (but not 'ls').
# Now close it for the remainder of the script.

# Thanks, S.C.

For a more detailed introdu


Advanced Bash-Scripting Guide
exec N > filename affects the entire script or current shell. Redirection in the PID of the script or shell
from that point on has changed. However . . .
N > filename affects only the newly-forked process, not the entire script or shell.
Thank you, Ahmed Darwish, for po


Advanced Bash-Scripting Guide
echo "Usage: $0 input-file output-file"
exit $E_FILE_ACCESS
fi
# Will exit with same error
#+ even if input file ($1) not specified (why?).
if [ -z "$2" ]
then
echo "Need to specify output file."
echo "Usage: $0 input-file output-file"
exit $E_WRONG_ARGS
fi

exec 4&1
ex


Advanced Bash-Scripting Guide

exec 3 myfile.txt
while read line &echo "Number of lines read = $Lines"

# 8

echo
exit 0
# Lines below not seen by script.
$ cat myfile.txt
Line
Line
Line
Line
Line
Line
Line
Line

1.
2.
3.
4.
5.
6.
7.
8.

20.2. Redirecting Code Blocks
Blocks of code, such as while, u


Advanced Bash-Scripting Guide
read name
echo $name
let "count += 1"
done


Advanced Bash-Scripting Guide
read name
echo $name
let "count += 1"
done

# Reads from redirected stdin ($Filename).

# Loop reads from file $Filename
#+ because of line 20.

# The original version of this script terminated the "while" loop with
#+
done


Advanced Bash-Scripting Guide
#
# More concise is

line_count=$(wc -l < "$Filename")

for name in `seq $line_count`
# while [ "$name" != Smith ]
do
read name
echo $name
if [ "$name" = Smith ]
then
break
fi
done "$Savefile"
#
^^^^^^^^^^^^^^^^^^^^^^^^^^^

# Number of lines in target file.

# Redirect


Advanced Bash-Scripting Guide
TRUE=1
if [ "$TRUE" ]
then
read name
echo $name
fi


Advanced Bash-Scripting Guide
done&2
2) exec 3>&2
3) exec 3>&2
*) exec 3> /dev/null
esac

4> /dev/null
4>&2
4>&2
4> /dev/null

5> /dev/null;;
5> /dev/null;;
5>&2;;
5> /dev/null;;

FD_LOGVARS=6

Chapter 20. I/O Redirection

379


Advanced Bash-Scripting Guide
if [[ $LOG_VARS ]]
then exec 6>> /var/log/vars.log
else exec 6> /dev/null
fi

# Bury output.

FD_LOGEVENTS=7
if [[ $LOG_EVENTS ]]
then
# exec 7 >(exec gawk '{print strftime(), $0}' >> /var/log/event.log)
# Above line fails in versions of Bash more recent than 2.04. Why?


Chapter 21. Subshells
Running a shell script launches a new process, a subshell.

Definition: A subshell is a child process launched by a shell (or shell script).
A subshell is a separate instance of the command processor -- the shell that gives you the prompt at the
console or in an xterm window. J


Advanced Bash-Scripting Guide
( command1; command2; command3; ... )
A command list embedded between parentheses runs as a subshell.
Variables in a subshell are not visible outside the block of code in the subshell. They are not accessible to the
parent process, to the shell that launched the subshel


Advanced Bash-Scripting Guide
echo "-----------------"; echo
var=41

# Global variable.

( let "var+=1"; echo "\$var INSIDE subshell = $var" )

# 42

echo "\$var OUTSIDE subshell = $var"
# 41
# Variable operations inside a subshell, even to a GLOBAL variable
#+ do not affect the value of the variabl


Advanced Bash-Scripting Guide
done
# When script terminates, there is no need to 'cd' back to original directory,
#+ because 'cd $home' takes place in a subshell.
exit 0

A subshell may be used to set up a "dedicated environment" for a command group.
COMMAND1
COMMAND2
COMMAND3
(
IFS=:
PATH=/bin
unse


Advanced Bash-Scripting Guide
Example 21-3. Running parallel processes in subshells
(cat list1 list2 list3 | sort | uniq > list123) &
(cat list4 list5 list6 | sort | uniq > list456) &
# Merges and sorts both sets of lists simultaneously.
# Running in background ensures parallel execution.
#
# Same e


Chapter 22. Restricted Shells
Disabled commands in restricted shells
. Running a script or portion of a script in restricted mode disables certain commands that would
otherwise be available. This is a security measure intended to limit the privileges of the script user and
to minimize possible damag


Advanced Bash-Scripting Guide
SHELL="/bin/ash"
echo
echo "\$SHELL= $SHELL"
echo
echo
echo "Attempting to redirect output in restricted mode."
ls -l /usr/bin > bin.files
ls -l bin.files
# Try to list attempted file creation effort.
echo
exit 0

Chapter 22. Restricted Shells

387


Chapter 23. Process Substitution
Piping the stdout of a command into the stdin of another is a powerful technique. But, what if you need
to pipe the stdout of multiple commands? This is where process substitution comes in.
Process substitution feeds the output of a process (or processes) into the st


Advanced Bash-Scripting Guide
drwx------rw-rw-r--rw-rw-r--rw-rw-r--

72
1
1
1

bozo
bozo
bozo
bozo

bozo
bozo
bozo
bozo

4096
78
42
103

Mar
Mar
Mar
Mar

10
10
10
10

17:58
12:58
12:58
12:58

..
File0
File2
t2.sh

Process substitution can compare the contents of two directories -- to see which filen


Advanced Bash-Scripting Guide
# Thanks, Stéphane Chazelas

Here is a method of circumventing the problem of an echo piped to a while-read loop running in a subshell.

Example 23-1. Code block redirection without forking
#!/bin/bash
# wr-ps.bash: while-read loop with process substitution.
# This exa


Advanced Bash-Scripting Guide
index=0
while read line
do
outloop[$index]="$line"
((index++))
# It does *not* run in a subshell, so ...
done <


Advanced Bash-Scripting Guide
#+ 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 equivale


Chapter 24. Functions
Like "real" programming languages, Bash has functions, though in a somewhat limited implementation. A
function is a subroutine, a code block that implements a set of operations, a "black box" that performs a
specified task. Wherever there is repetitive code, when a task repeats


Advanced Bash-Scripting Guide

fun ()
{ # A somewhat more complex function.
i=0
REPEATS=30
echo
echo "And now the fun really begins."
echo
sleep $JUST_A_SECOND
# Hey, wait a second!
while [ $i -lt $REPEATS ]
do
echo "----------FUNCTIONS---------->"
echo "


Advanced Bash-Scripting Guide
#!/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


Advanced Bash-Scripting Guide
Function declarations can appear in unlikely places, even where a command would otherwise go.
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


Advanced Bash-Scripting Guide
What happens when different versions of the same function appear in a script?
#
#
#
#

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 f


Advanced Bash-Scripting Guide
then
echo "-Parameter #2 is \"$2\".-"
fi
return 0
}
echo
echo "Nothing passed."
func2
echo

# Called with no params

echo "Zero-length parameter passed."
func2 ""
# Called with zero-length param
echo
echo "Null parameter passed."
func2 "$uninitialized_param"
echo

# Cal


Advanced Bash-Scripting Guide
echo "============================================================"
echo
echo "Second call to function: command-line arg passed explicitly."
func $1
# Now it's seen!
exit 0

In contrast to certain other programming languages, shell scripts normally pass only value param


Advanced Bash-Scripting Guide
echo $1=$x
eval "$1=\"Some Different Text \""

# Assign new value.

}
Junk="Some Text"
echo $Junk "before"

# Some Text before

dereference Junk
echo $Junk "after"

# Some Different Text after

exit 0

Example 24-6. Again, dereferencing a parameter passed to a function


Advanced Bash-Scripting Guide
status may be used in the script by referencing it as $?. This mechanism effectively permits script
functions to have a "return value" similar to C functions.
return
Terminates a function. A return command [106] optionally takes an integer argument, which is
returned to


Advanced Bash-Scripting Guide
For a function to return a string or array, use a dedicated variable.
count_lines_in_etc_passwd()
{
[[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd))
# If /etc/passwd is readable, set REPLY to line count.
# Returns both a parameter value and status informatio


Advanced Bash-Scripting Guide
done
return $number
# Exercises:
# --------# 1) Explain how this function works.
#
Hint: division by successive subtraction.
# 2) Extend to range of the function.
#
Hint: use "echo" and command-substitution capture.
}

to_roman $num 100 C
num=$?
to_roman $num 90 LXXXX
n


Advanced Bash-Scripting Guide
echo $?

# Returns 255.

return_test 257
echo $?

# Error!
# Returns 1 (return code for miscellaneous error).

# ======================================================
return_test -151896
# Do large negative numbers work?
echo $?
# Will this return -151896?
# No! It ret


Advanced Bash-Scripting Guide
{
if [ -z "$2" ]
then
echo $E_PARAM_ERR
return
fi
if [ "$1" -eq "$2" ]
then
echo $EQUAL
return
else
if [ "$1" -gt "$2" ]
then
retval=$1
else
retval=$2
fi
fi
echo $retval

# Echoes (to stdout), rather than returning value.
# Why?

}

return_val=$(max2 33001 33997)
#
^^^^


Advanced Bash-Scripting Guide
# Parameter passed to function ($1 -- month number), then to awk.
# Awk sees this as "print $1 . . . print $12" (depending on month number)
# Template for passing a parameter to embedded awk script:
#
$'"${script_parameter}"'
# Needs error checking for correct parameter


Advanced Bash-Scripting Guide
# or
#
awk -F: '/PATTERN/ {print $5}'
# or
#
awk -F: '($1 == "username") { print $5 }' # real name from username
# However, it might not be as instructive.
exit 0

There is an alternate, and perhaps less confusing method of redirecting a function's stdin. This
involves


Advanced Bash-Scripting Guide
# Global and local variables inside a function.
func ()
{
local loc_var=23
# Declared as local variable.
echo
# Uses the 'local' builtin.
echo "\"loc_var\" in function = $loc_var"
global_var=999
# Not declared as local.
# Defaults to global.
echo "\"global_var\" in func


Advanced Bash-Scripting Guide
echo
function0 ()
{
echo "==INSIDE Function=="
echo "Global"
t0=$(exit 1)
echo $?
# 1
# As expected.
echo
echo "Local declared & assigned in same command."
local t1=$(exit 1)
echo $?
# 0
# Unexpected!
# Apparently, the variable assignment takes place before
#+ the local


Advanced Bash-Scripting Guide
while [ "$var" -ge 0 ]
do
echo "Recursion count = "$r_count" +-+ \$var = "$var""
(( var-- )); (( r_count++ ))
recurse "$var" # Function calls itself (recurses)
done
#+ until what condition is met?
}
recurse $RECURSIONS
exit $?

Example 24-14. Another simple demonstratio


Advanced Bash-Scripting Guide

MAX_ARG=5
E_WRONG_ARGS=85
E_RANGE_ERR=86

if [ -z "$1" ]
then
echo "Usage: `basename $0` number"
exit $E_WRONG_ARGS
fi
if [ "$1" -gt $MAX_ARG ]
then
echo "Out of range ($MAX_ARG is maximum)."
# Let's get real now.
# If you want greater range than this,
#+ rewrite it in


Advanced Bash-Scripting Guide
#
#
#
#
#
#

--------------------------------Fibo(0) = 0
Fibo(1) = 1
else
Fibo(j) = Fibo(j-1) + Fibo(j-2)
---------------------------------

MAXTERM=15
MINIDX=2

# Number of terms (+1) to generate.
# If idx is less than 2, then Fibo(idx) = idx.

Fibonacci ()
{
idx=$1
#


Advanced Bash-Scripting Guide
# The Tower of Hanoi is a mathematical puzzle attributed to
#+ Edouard Lucas, a nineteenth-century French mathematician.
#
# There are three vertical posts set in a base.
# The first post has a set of annular rings stacked on it.
# These rings are disks with a hole dril


Advanced Bash-Scripting Guide
1) case $(($1>0)) in
# Must have at least one disk.
1) # Nested case statement.
dohanoi $1 1 3 2
echo "Total moves = $Moves"
# 2^n - 1, where n = # of disks.
exit 0;
;;
*)
echo "$0: illegal value for number of disks";
exit $E_BADPARAM;
;;
esac
;;
*)
echo "usage: $0 N"
e


Chapter 25. Aliases
A Bash alias is essentially nothing more than a keyboard shortcut, an abbreviation, a means of avoiding
typing a long command sequence. If, for example, we include alias lm="ls -l | more" in the ~/.bashrc
file, then each lm [113] typed at the command-line will automatically be re


Advanced Bash-Scripting Guide
echo
if [ TRUE ]
then
alias rr="ls -l"
echo "Trying aliased \"rr\" within if/then statement:"
rr /usr/X11R6/bin/mk*
#* Error message results!
# Aliases not expanded within compound statements.
echo "However, previously expanded alias still recognized:"
ll /usr/X11R6/bin


Advanced Bash-Scripting Guide
drwxr-xr-x
-rwxr-xr-x

40 bozo
1 bozo

bozo
bozo

2048 Feb
199 Feb

6 14:04 ..
6 14:04 unalias.sh

./unalias.sh: llm: command not found

Chapter 25. Aliases

417


Chapter 26. List Constructs
The and list and or list constructs provide a means of processing a number of commands consecutively. These
can effectively replace complex nested if/then or even case statements.
Chaining together commands
and list
command-1 && command-2 && command-3 && ... command-n

Ea


Advanced Bash-Scripting Guide
#!/bin/bash
ARGS=1
E_BADARGS=85

# Number of arguments expected.
# Exit value if incorrect number of args passed.

test $# -ne $ARGS && \
#
^^^^^^^^^^^^ condition #1
echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS
#
^^
# If condition #1 tests true (wron


Advanced Bash-Scripting Guide
# AND LIST executes on true, OR LIST on false.
exit $?

If the first command in an or list returns true, it will execute.
# ==>
#+==>
#+==>
# ==>

The following snippets from the /etc/rc.d/init.d/single
script by Miquel van Smoorenburg
illustrate use of "and" and "or" l


Chapter 27. Arrays
Newer versions of Bash support one-dimensional arrays. Array elements may be initialized with the
variable[xx] notation. Alternatively, a script may introduce the entire array by an explicit declare -a
variable statement. To dereference (retrieve the contents of) an array element,


Advanced Bash-Scripting Guide
# Another way of assigning array variables...
# array_name=( XXX YYY ZZZ ... )
area2=( zero one two three four )
echo -n "area2[0] = "
echo ${area2[0]}
# Aha, zero-based indexing (first element of array is [0], not [1]).
echo -n "area2[1] = "
echo ${area2[1]}
# [1] is s


Advanced Bash-Scripting Guide
#!/bin/bash
# poem.sh: Pretty-prints one of the ABS Guide author's favorite poems.
# Lines of the poem (single stanza).
Line[1]="I do not know which to prefer,"
Line[2]="The beauty of inflections"
Line[3]="Or the beauty of innuendoes,"
Line[4]="The blackbird whistling"


Advanced Bash-Scripting Guide
#+ starting at position # 1 (2nd character).
echo "--------------"
echo ${#array[0]}

#
#
#
#
#

4
Length of first element of array.
4
Length of first element of array.
(Alternate notation)

echo ${#array[1]}

#
#
#

3
Length of second element of array.
Arrays in Bash h


Advanced Bash-Scripting Guide
echo ${arrayZ[@]:1}
#
^

# two three four five five
All elements following element[0].

echo ${arrayZ[@]:1:2}
#
^

# two three
Only the two elements after element[0].

echo "---------"

# Substring Removal
# Removes shortest match from front of string(s).
echo ${arrayZ[


Advanced Bash-Scripting Guide

replacement() {
echo -n "!!!"
}
echo ${arrayZ[@]/%e/$(replacement)}
#
^ ^^^^^^^^^^^^^^
# on!!! two thre!!! four fiv!!! fiv!!!
# The stdout of replacement() is the replacement string.
# Q.E.D: The replacement action is, in effect, an 'assignment.'
echo "----------------


Advanced Bash-Scripting Guide
echo
exit 0
# Exercise:
# -------# Modify this script so it lists itself
#+ in its original format,
#+ complete with whitespace, line breaks, etc.

In an array context, some Bash builtins have a slightly altered meaning. For example, unset deletes array
elements, or eve


Advanced Bash-Scripting Guide
echo
# Again, list all the elements in the array, but using a more elegant method.
echo ${colors[@]}
# echo ${colors[*]} also works.
echo
# The "unset" command deletes elements of an array, or entire array.
unset colors[1]
# Remove 2nd element of array.
# Same effect as


Advanced Bash-Scripting Guide
echo "Number of elements in array1 = ${#array1[*]}"
echo "Number of elements in array2 = ${#array2[*]}"
echo "Number of elements in array3 = ${#array3[*]}"
}

# 1
# 0
# 0

(Surprise!)

# ===================================================================
ListArray
# Try


Advanced Bash-Scripting Guide
# Just when you are getting the feel for this . . .
array6=( ${array0[@]#*new} )
echo # This one might surprise you.
echo "Elements in array6: ${array6[@]}"
array7=( ${array0[@]#new1} )
echo # After array6 this should not be a surprise.
echo "Elements in array7: ${array


Advanced Bash-Scripting Guide
exit

The relationship of ${array_name[@]} and ${array_name[*]} is analogous to that between $@ and $*. This
powerful array notation has a number of uses.

# Copying an array.
array2=( "${array1[@]}" )
# or
array2="${array1[@]}"
#
# However, this fails with "sparse" arr


Advanced Bash-Scripting Guide
# Each whitespace-separated "word" in the file
#+ has been assigned to an element of the array.
element_count=${#array1[*]}
echo $element_count

# 8

Clever scripting makes it possible to add array operations.

Example 27-8. Initializing arrays
#! /bin/bash
# array-assi


Advanced Bash-Scripting Guide
echo '- - testing: =${array[@]} - -'
times
declare -a bigThree=${bigOne[@]}
# No parentheses this time.
times
#
#+
#
#
#+
#+
#
#
#
#
#
#

Comparing the numbers shows that the second form, pointed out
by Stephane Chazelas, is faster.
As William Park explains:
The bigTwo


Advanced Bash-Scripting Guide
echo
echo
echo
echo
echo

-n
-n
-n
-n
-n

'eval '
"$2"
'=( ${'
"$1"
'[@]} )'

# Destination name
# Source name

# That could all be a single command.
# Matter of style only.
}
declare -f CopyArray
CopyArray=CpArray_Mac

# Function "Pointer"
# Statement Builder

Hype()
{


Advanced Bash-Scripting Guide
# Slightly modified in formatting by M.C.

# Array operations are Bash-specific.
# Legacy UNIX /bin/sh lacks equivalents.

# Pipe the output of this script to 'more'
#+ so it doesn't scroll off the terminal.
# Or, redirect output to a file.

declare -a array1=( zero1 on


Advanced Bash-Scripting Guide
done
# Examine the modified second element.
echo
echo '- - Reassign and list second element - -'
declare -a subArray=${dest[1]}
cnt=${#subArray[@]}
echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
echo "Element [$i]: ${subArray[$i]}"
done
#
#+
#+
#+

T


Advanced Bash-Scripting Guide
#
#
#
#

At the end of the second pass, the next "heaviest" one has sunk next to bottom.
And so forth.
This means that each successive pass needs to traverse less of the array.
You will therefore notice a speeding up in the printing of the later passes.

exchange()
{
#


Advanced Bash-Scripting Guide
# ---------------------------------------------------------------------# Paulo Marcel Coelho Aragao suggests for-loops as a simpler altenative.
#
# for (( last = $number_of_elements - 1 ; last > 0 ; last-- ))
##
Fix by C.Y. Hunt
^
(Thanks!)
# do
#
for (( i = 0 ; i < las


Advanced Bash-Scripting Guide
Embedded arrays in combination with indirect references create some fascinating possibilities

Example 27-12. Embedded arrays and indirect references
#!/bin/bash
# embedded-arrays.sh
# Embedded arrays and indirect references.
# This script by Dennis Leeuw.
# Used with p


Advanced Bash-Scripting Guide
local ${!TEST2}
# Indirect reference (as above).
echo "Array element VAR1_1: $VAR1_1 from ARRAY21"
}
print
echo
exit 0
#
As the author of the script notes,
#+ "you can easily expand it to create named-hashes in bash."
#
(Difficult) exercise for the reader: implement thi


Advanced Bash-Scripting Guide
print_primes ()
{
# Print out the members of the Primes[] array tagged as prime.
i=$LOWER_LIMIT
until [ "$i" -gt "$UPPER_LIMIT" ]
do
if [ "${Primes[i]}" -eq "$PRIME" ]
then
printf "%8d" $i
# 8 spaces per number gives nice, even columns.
fi
let "i += 1"
done
}
sift () #


Advanced Bash-Scripting Guide
echo
exit 0

# -------------------------------------------------------- #
# Code below line will not execute, because of 'exit.'
# This improved version of the Sieve, by Stephane Chazelas,
#+ executes somewhat faster.
# Must invoke with command-line argument (limit of p


Advanced Bash-Scripting Guide
then
until (( ( t += i ) > ${UPPER_LIMIT} ))
do Primes[t]=; done
fi
done
# echo ${Primes[*]}
echo
# Change to original script for pretty-printing (80-col. display).
printf "%8d" ${Primes[*]}
echo; echo
exit $?

Compare these array-based prime number generators with alte


Advanced Bash-Scripting Guide
stack[$SP]=$1
return
}
pop()
{
Data=

# Pop item off stack.

if [ "$SP" -eq "$BP" ]
then
return
fi

# Stack empty?

# Empty out data item.

Data=${stack[$SP]}
let "SP += 1"
return
}

# This also keeps SP from getting past 100,
#+ i.e., prevents a runaway stack.

# Bump


Advanced Bash-Scripting Guide
exit 0
# =======================================================

# Exercises:
# --------# 1) Modify the "push()" function to permit pushing
#
+ multiple element on the stack with a single function call.
# 2) Modify the "pop()" function to permit popping
#
+ multiple el


Advanced Bash-Scripting Guide
let "n1 = $n - 1"
let "n2 = $n - 2"

# n-1
# n-2

t0=`expr $n - ${Q[n1]}`
t1=`expr $n - ${Q[n2]}`

# n - Q[n-1]
# n - Q[n-2]

T0=${Q[t0]}
T1=${Q[t1]}

# Q[n - Q[n-1]]
# Q[n - Q[n-2]]

Q[n]=`expr $T0 + $T1`
echo -n "${Q[n]} "

# Q[n - Q[n-1]] + Q[n - Q[n-2]]

if [ `expr


Advanced Bash-Scripting Guide
alpha[$index]=$i
# alpha[$row][$column]
let "rc += 1"
done
# Simpler would be
#+
declare -a alpha=( 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 )
#+ but this somehow lacks the "flavor" of a two-dimensional array.
}
print_alpha ()
{
local row=0
local index
echo
whi


Advanced Bash-Scripting Guide
{
#+ "balance" it on its lower lefthand corner.
local row
local column
for (( row = Rows; row > -Rows; row-- ))
do
# Step through the array backwards. Why?
for (( column = 0; column < Columns; column++ ))
do
if [ "$row"
then
let "t1 =
let "t2 =
else
let "t1 =
let "t2 =


Advanced Bash-Scripting Guide
-For more interesting scripts using arrays, see:
· Example 12-3
· Example 16-46
· Example A-22
· Example A-44
· Example A-41
· Example A-42

Chapter 27. Arrays

449


Chapter 28. Indirect References
We have seen that referencing a variable, $var, fetches its value. But, what about the value of a value? What
about $$var?
The actual notation is \$$var, usually preceded by an eval (and sometimes an echo). This is called an
indirect reference.

Example 28-1. Indirect


Advanced Bash-Scripting Guide
echo

# Now, let's try changing the second-order reference.
t=table_cell_3
table_cell_3=24
echo "\"table_cell_3\" = $table_cell_3"
# "table_cell_3" = 24
echo -n "dereferenced \"t\" = "; eval echo \$$t
# dereferenced "t" = 24
# In this simple case, the following also wor


Advanced Bash-Scripting Guide
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 variabl


Advanced Bash-Scripting Guide
# A multi-line awk script is invoked by
#
awk "
#
...
#
...
#
...
#
"

# Begin awk script.
# ------------------------------------------------awk "
{ total += \$${column_number} # Indirect reference
}
END {
print total
}
" "$filename"
# Note that awk doesn't need an eval


Chapter 29. /dev and /proc
A Linux or UNIX filesystem typically has the /dev and /proc special-purpose directories.

29.1. /dev
The /dev directory contains entries for the physical devices that may or may not be present in the hardware.
[115] Appropriately enough, these are called device files. As a


Advanced Bash-Scripting Guide
#+ appropriate action.

When executing a command on a /dev/tcp/$host/$port pseudo-device file, Bash opens a TCP
connection to the associated socket.

A socket is a communications node associated with a specific I/O port. (This is analogous to a hardware
socket, or recep


Advanced Bash-Scripting Guide
:


Advanced Bash-Scripting Guide
echo -n "$g$e2$d$c$d$c$a$g$n$g$e$n$g$e2$d$c$c$b$c$cis$n$cis$d \
$n$g$e2$d$c$d$c$a$g$n$g$e$n$g$a$d$c$b$a$b$c" > /dev/dsp
# dsp = Digital Signal Processor
exit

# A "bonny" example of a shell script!

29.2. /proc
The /proc directory is actually a pseudo-filesystem. The fi


Advanced Bash-Scripting Guide

bash$ cat /proc/loadavg
0.13 0.42 0.27 2/44 1119

bash$ cat /proc/apm
1.16 1.2 0x03 0x01 0xff 0x80 -1% -1 ?

bash$ cat /proc/acpi/battery/BAT0/info
present:
yes
design capacity:
43200 mWh
last full capacity:
36640 mWh
battery technology:
rechargeable
design voltage:
10


Advanced Bash-Scripting Guide
#!/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"


Advanced Bash-Scripting Guide
#!/bin/bash
# pid-identifier.sh:
# Gives complete path name to process associated with pid.
ARGNO=1 # Number of arguments the script expects.
E_WRONGARGS=65
E_BADPID=66
E_NOSUCHPROCESS=67
E_NOPERMISSION=68
PROCFILE=exe
if [ $# -ne $ARGNO ]
then
echo "Usage: `basename $0


Advanced Bash-Scripting Guide
#+ to the complete path name of the invoking process.
if [ -e "$exe_file" ] # If /proc/pid-number/exe exists,
then
#+ then the corresponding process exists.
echo "Process #$1 invoked by $exe_file."
else
echo "No such process running."
fi

# This elaborate script can *al


Advanced Bash-Scripting Guide
if [ ! -e "/proc/$pidno/$PROCFILENAME" ]
# While process running, then "status" file exists.
then
echo "Disconnected."
#
exit $NOTCONNECTED
fi
netstat -s | grep "packets received" # Get some connect statistics.
netstat -s | grep "packets delivered"

sleep $INTERVAL
echo


Chapter 30. Network Programming
The Net's a cross between an elephant and a
white elephant sale: it never forgets, and it's
always crap.
--Nemo
A Linux system has quite a number of tools for accessing, manipulating, and troubleshooting network
connections. We can incorporate some of these tools into


Advanced Bash-Scripting Guide
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo

SERVER_PROTOCOL = $SERVER_PROTOCOL
SERVER_PORT = $SERVER_PORT
REQUEST_METHOD = $REQUEST_METHOD
HTTP_ACCEPT = "$HTTP_ACCEPT"
PATH_INFO = "$PATH_INFO"
PATH_TRANSLATED = "$PATH_TRANSLATED"
SCRIPT_NAME =


Advanced Bash-Scripting Guide
#
#

... within a 60-second delay loop to bounce packets from DDOS attacks.

#
#
#
#+

Exercise:
-------Use the 'iptables' command to extend this script
to reject connection attempts from well-known spammer IP domains.

More examples of network programming:
1. Getting t


Chapter 31. Of Zeros and Nulls
Faultily faultless, icily regular, splendidly null
Dead perfection; no more.
--Alfred Lord Tennyson
/dev/zero ... /dev/null
Uses of /dev/null
Think of /dev/null as a black hole. It is essentially the equivalent of a write-only file. Everything
written to it disappears.


Advanced Bash-Scripting Guide
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.

Uses of /dev/zero
Like /dev/null, /dev/zero is a pseudo-device file, but it actually produces a stream of nulls
(binary zeros, not th


Advanced Bash-Scripting Guide
mkswap $FILE $blocks
# Designate it a swap file.
swapon $FILE
# Activate swap file.
retcode=$?
# Everything worked?
# Note that if one or more of these commands fails,
#+ then it could cause nasty problems.
###############################################################


Advanced Bash-Scripting Guide
dd if=/dev/zero of=$DEVICE count=$SIZE bs=$BLOCKSIZE

# Zero out RAM device.
# Why is this necessary?
mke2fs $DEVICE
# Create an ext2 filesystem on it.
mount $DEVICE $MOUNTPT
# Mount it.
chmod 777 $MOUNTPT
# Enables ordinary user to access ramdisk.
# However, must be ro


Chapter 32. Debugging
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.
--Brian Kernighan
The Bash shell contains no built-in debugger, and only bare-bones debugging-speci


Advanced Bash-Scripting Guide
What if the script executes, but does not work as expected? This is the all too familiar logic error.

Example 32-3. test24: another buggy script
#!/bin/bash
#
#+
#
#

This script is supposed to delete all filenames in current directory
containing embedded spaces.
It do


Advanced Bash-Scripting Guide
}
DEBUG=on
Whatever=whatnot
debecho $Whatever

# whatnot

DEBUG=
Whatever=notwhat
debecho $Whatever

# (Will not echo.)

2. Using the tee filter to check processes or data flows at critical points.
3. Setting option flags -n -v -x
sh -n scriptname checks for syntax erro


Advanced Bash-Scripting Guide
then
return $E_PARAM_ERR
fi

#+ to assert() function.
# No damage done.

lineno=$2
if [ ! $1 ]
then
echo "Assertion failed: \"$1\""
echo "File \"$0\", line $lineno"
# Give name of file and line number.
exit $E_ASSERT_FAILED
# else
#
return
#
and continue executing the s


Advanced Bash-Scripting Guide
trap 'echo "Control-C disabled."' 2
# Message when Control-C pressed.

Example 32-5. Trapping at exit
#!/bin/bash
# Hunting variables with a trap.
trap 'echo Variable Listing --- a = $a b = $b' EXIT
# EXIT is the name of the signal generated upon exit from a script.
#
#


Advanced Bash-Scripting Guide
tail -n $CHECK_LINES $LOGFILE> $TEMPFILE
# Saves last 100 lines of system log file as temp file.
# Necessary, since newer kernels generate many log messages at log on.
search=`grep $KEYWORD $TEMPFILE`
# Checks for presence of the "IP address" phrase,
#+ indicating a suc


Advanced Bash-Scripting Guide
# progress-bar2.sh
# Author: Graham Ewart (with reformatting by ABS Guide author).
# Used in ABS Guide with permission (thanks!).
# Invoke this script with bash. It doesn't work with sh.
interval=1
long_interval=10
{
trap "exit" SIGUSR1
sleep $interval; sleep $interval


Advanced Bash-Scripting Guide
VARIABLE-TRACE> $variable = ""
VARIABLE-TRACE> $variable = "29"
Just initialized $variable to 29.
VARIABLE-TRACE> $variable = "29"
VARIABLE-TRACE> $variable = "87"
Just multiplied $variable by 3.
VARIABLE-TRACE> $variable = "87"

Of course, the trap command has other us


Advanced Bash-Scripting Guide
#!/bin/bash
# child.sh
# Running multiple processes on an SMP box.
# This script is called by parent.sh.
# Author: Tedman Eng
temp=$RANDOM
index=$1
shift
let "temp %= 5"
let "temp += 4"
echo "Starting $index Time:$temp" "$@"
sleep ${temp}
echo "Ending $index"
kill -s SI


Advanced Bash-Scripting Guide
NUMPROC=$1
shift
PARAMETRI=( "$@" )

# Number of concurrent process
# Parameters of each process

function avvia() {
local temp
local index
temp=$RANDOM
index=$1
shift
let "temp %= $TEMPO"
let "temp += 1"
echo "Starting $index Time:$temp" "$@"
sleep ${temp}
echo "Ending


Advanced Bash-Scripting Guide
trap 2

# Reenables Control-C

Version 3 of Bash adds the following internal variables for use by the debugger.
1. $BASH_ARGC
Number of command-line arguments passed to script, similar to $#.
2. $BASH_ARGV
Final command-line parameter passed to script, equivalent ${!#}.


Chapter 33. Options
Options are settings that change shell and/or script behavior.
The set command enables options within a script. At the point in the script where you want the options to take
effect, use set -o option-name or, in short form, set -option-abbrev. These two forms are equivalent.
#!/b


Advanced Bash-Scripting Guide
It is also possible to enable script options from the command line. Some options that will not work with set
are available this way. Among these are -i, force script to run interactive.
bash -v script-name
bash -o verbose script-name
The following is a listing of some u


Advanced Bash-Scripting Guide
-u

nounset

-v
-x
--

verbose
xtrace
(none)
(none)

Chapter 33. Options

Attempt to use undefined variable outputs error message, and forces an
exit
Print each command to stdout before executing it
Similar to -v, but expands commands
End of options flag. All other argu


Chapter 34. Gotchas
Turandot: Gli enigmi sono tre, la morte una!
Caleph: No, no! Gli enigmi sono tre, una la vita!
--Puccini
Here are some (non-recommended!) scripting practices that will bring excitement into an otherwise dull life.
·
Assigning reserved words or characters to variable names.
case=


Advanced Bash-Scripting Guide
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.

·
Not terminating with a semicolon the final command in a code block within curly bracket


Advanced Bash-Scripting Guide
· Misusing string comparison operators.

Example 34-1. Numerical and string comparison are not equivalent
#!/bin/bash
# bad-op.sh: Trying to use a string comparison on integers.
echo
number=1
# The following while-loop has two errors:
#+ one blatant, and the other subt


Advanced Bash-Scripting Guide
· Quoting a variable containing whitespace prevents splitting. Sometimes this produces unintended
consequences.
·
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 c


Advanced Bash-Scripting Guide
· A script with DOS-type newlines (\r\n) will fail to execute, since #!/bin/bash\r\n is not
recognized, not the same as the expected #!/bin/bash\n. The fix is to convert the script to
UNIX-style newlines.
#!/bin/bash
echo "Here"
unix2dos $0
chmod 755 $0

# Script chang


Advanced Bash-Scripting Guide
Example 34-2. Subshell Pitfalls
#!/bin/bash
# Pitfalls of variables in a subshell.
outer_variable=outer
echo
echo "outer_variable = $outer_variable"
echo
(
# Begin subshell
echo "outer_variable inside subshell = $outer_variable"
inner_variable=inner # Set
echo "inner_va


Advanced Bash-Scripting Guide
echo "c = $c" # c = ccc
# Reassignment failed.
###
##
#
##+
###

However . . .
Uncommenting line 6:
shopt -s lastpipe
fixes the problem!
This is a new feature in Bash, version 4.2.

# -----------------------------# Try the following alternative.
var=`echo "one two three


Advanced Bash-Scripting Guide
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


Advanced Bash-Scripting Guide
· Using shell scripts for CGI programming may be problematic. Shell script variables are not
"typesafe," and this can cause undesirable behavior as far as CGI is concerned. Moreover, it is
difficult to "cracker-proof" shell scripts.
· Bash does not handle the double s


Chapter 35. Scripting With Style
Get into the habit of writing shell scripts in a structured and systematic manner. Even on-the-fly and "written
on the back of an envelope" scripts will benefit if you take a few minutes to plan and organize your thoughts
before sitting down and coding.
Herewith are


Advanced Bash-Scripting Guide
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
if [ -f "$L


Advanced Bash-Scripting Guide
Ender suggests using the exit codes in /usr/include/sysexits.h in shell scripts, though these
are primarily intended for C and C++ programming.
· Use standardized parameter flags for script invocation. Ender proposes the following set of flags.
-a
-b
-c
-d
-e
-h
-l
-m


Chapter 36. Miscellany
Nobody really knows what the Bourne shell's
grammar is. Even examination of the source code
is little help.
--Tom Duff

36.1. Interactive and non-interactive shells and scripts
An interactive shell reads commands from user input on a tty. Among other things, such a shell reads


Advanced Bash-Scripting Guide
else
# interactive
...
fi

Alternatively, the script can test for the presence of option "i" in the $- flag.
case $- in
*i*)
# interactive shell
;;
*)
# non-interactive shell
;;
# (Courtesy of "UNIX F.A.Q.," 1993)

However, John Lange describes an alternative method, us


Advanced Bash-Scripting Guide
Example 36-1. shell wrapper
#!/bin/bash
#
#
#
#
#
#
#
#
#
#
#

This simple script removes blank lines from a file.
No argument checking.
You might wish to add something like:
E_NOARGS=85
if [ -z "$1" ]
then
echo "Usage: `basename $0` target-file"
exit $E_NOARGS
fi

sed


Advanced Bash-Scripting Guide
else
echo "File \"$3\" does not exist."
exit $E_BADARGS
fi

# ----------------------------------------------# Here is where the heavy work gets done.
sed -e "s/$old_pattern/$new_pattern/g" $file_name
# ----------------------------------------------#
#+
#
#+
#

's' is, o


Advanced Bash-Scripting Guide
done
exit 0

#
#
#
#
#
#
#
#
#
#
#
#
#

Decimal
------33
34
35
36

Hex
--21
22
23
24

Character
--------!
"
#
$

7a
7b
7c
7d

z
{
|
}

. . .
122
123
124
125

# Redirect the output of this script to a file
#+ or pipe it to "more": sh pr-asc.sh | more

Example 36-5. A she


Advanced Bash-Scripting Guide
{ total += $'"${column_number}"'
}
END {
print total
}
' "$filename"
# ----------------------------# End awk script.

#
#+
#
#
#
#
#
#
#
#

It may not be safe to pass shell variables to an embedded awk script,
so Stephane Chazelas proposes the following alternative:
---


Advanced Bash-Scripting Guide
echo "Greetings from the Bash part of the script, $0."
# More Bash commands may follow here.
exit
# End of Bash part of the script.
# =======================================================
#!/usr/bin/perl
# This part of the script must be invoked with
#
perl -x bashand


Advanced Bash-Scripting Guide

36.4. Recursion: a script calling itself
Can a script recursively call itself? Indeed.

Example 36-8. A (useless) script that recursively calls itself
#!/bin/bash
# recurse.sh
#
#
#

Can a script recursively call itself?
Yes, but is this of any practical use?
(See the


Advanced Bash-Scripting Guide
if [ $# -eq $MINARGS ]; then
grep $1 "$DATAFILE"
# 'grep' prints an error message if $DATAFILE not present.
else
( shift; "$PROGNAME" $* ) | grep $1
# Script recursively calls itself.
fi
exit 0

# Script exits here.
# Therefore, it's o.k. to put
#+ non-hashmarked commen


Advanced Bash-Scripting Guide
then
mntusr=$(id -u) grpusr=$(id -g) sudo $0 $*
exit 0
fi
# We will only get here if we are being run by sudo.
/bin/mount $* -o uid=$mntusr,gid=$grpusr
exit 0
# Additional notes (from the author of this script):
# ------------------------------------------------# 1) Lin


Advanced Bash-Scripting Guide
echo
echo
tput
echo
echo
tput
echo
echo
tput
echo
echo

"vans, Roland"
-en '\E[47;35m'"\033[1mJ\033[0m"
sgr0
"ones, Mildred"
-en '\E[47;32m'"\033[1mS\033[0m"
sgr0
"mith, Julie"
-en '\E[47;31m'"\033[1mZ\033[0m"
sgr0
"ane, Morris"

# "[E]vans, Roland"
# Magenta

# Green


Advanced Bash-Scripting Guide
# Draw-box.sh: Drawing a box using ASCII characters.
# Script by Stefano Palmeri, with minor editing by document author.
# Minor edits suggested by Jim Angstadt.
# Used in the ABS Guide with permission.

##################################################################


Advanced Bash-Scripting Guide
fi
BOX_HEIGHT=`expr $3 - 1`
BOX_WIDTH=`expr $4 - 1`
T_ROWS=`tput lines`
T_COLS=`tput cols`

#
#+
#
#+

-1 correction needed because angle char "+"
is a part of both box height and width.
Define current terminal dimension
in rows and columns.

if [ $1 -lt 1 ] || [ $1 -gt


Advanced Bash-Scripting Guide
echo -ne "\E[0m"

#

Restore old colors.

P_ROWS=`expr $T_ROWS - 1`

#

Put the prompt at bottom of the terminal.

echo -e "\E[${P_ROWS};1H"
}

# Now, let's try drawing a box.
clear
# Clear the terminal.
R=2
# Row
C=3
# Column
H=10
# Height
W=45
# Width
col=1
# Color (r


Advanced Bash-Scripting Guide
echo -e '\E[COLOR1;COLOR2mSome text goes here.'
The "\E[" begins the escape sequence. The semicolon-separated numbers "COLOR1" and "COLOR2"
specify a foreground and a background color, according to the table below. (The order of the numbers does
not matter, since the fo


Advanced Bash-Scripting Guide
message=${1:-$default_msg}
color=${2:-$black}
echo -e "$color"
echo "$message"
Reset

# Defaults to default message.
# Defaults to black, if not specified.

# Reset to normal.

return
}

# Now, let's try it out.
# ---------------------------------------------------cecho


Advanced Bash-Scripting Guide
#+ using xterm and rxvt, and konsole.
# On a machine with an AMD 900 MHz processor,
#+ the average race time is 75 seconds.
# On faster computers the race time would be lower.
# So, if you want more suspense, reset the USLEEP_ARG variable.
#
# Script by Stefano Palmeri.


Advanced Bash-Scripting Guide
draw_horse_two(){
echo -n " "\\\\$MOVE_HORSE\\\\
}

# Define current terminal dimension.
N_COLS=`tput cols`
N_LINES=`tput lines`
# Need at least a 20-LINES X 80-COLUMNS terminal. Check it.
if [ $N_COLS -lt 80 ] || [ $N_LINES -lt 20 ]; then
echo "`basename $0` needs a 80


Advanced Bash-Scripting Guide
move_and_echo 5 43 "@...@.@...@..@@@@..@@@@.@@@@.."

# Set foreground and background colors to green.
echo -ne '\E[32;42m'
# Draw eleven green lines.
tput cup 5 0
for n in `seq 11`; do
echo $BLANK80
done
# Set foreground color to black.
echo -ne '\E[30m'
tput cup 5 0
#


Advanced Bash-Scripting Guide
# Calculate odds.
case $HANDICAP in
1) ODDS=`echo $HANDICAP \* 0.25 + 1.25 | bc`
echo $ODDS > odds_${HN}
;;
2 | 3) ODDS=`echo $HANDICAP \* 0.40 + 1.25 | bc`
echo $ODDS > odds_${HN}
;;
4 | 5 | 6) ODDS=`echo $HANDICAP \* 0.55 + 1.25 | bc`
echo $ODDS > odds_${HN}
;;
7 | 8)


Advanced Bash-Scripting Guide
move_and_echo 18 1 Starting...
sleep 1
# Set the column of the finish line.
WINNING_POS=74
# Define the time the race started.
START_TIME=`date +%s`
# COL variable needed by following "while" construct.
COL=0
while [ $COL -lt $WINNING_POS ]; do
MOVE_HORSE=0
# Check if t


Advanced Bash-Scripting Guide
if [ $COL = 15 ]; then
echo $MOVE_HORSE >> fieldline15
fi
if [ `wc -l fieldline15 | cut -f1 -d " "` = 9 ]; then
print_odds
: > fieldline15
fi
# Define the leading horse.
HIGHEST_POS=`cat *position | sort -n | tail -1`
# Set background color to white.
echo -ne '\E[47m'
t


Advanced Bash-Scripting Guide
There is, however, a major problem with all this. ANSI escape sequences are emphatically non-portable.
What works fine on some terminal emulators (or the console) may work differently, or not at all, on
others. A "colorized" script that looks stunning on the script auth


Advanced Bash-Scripting Guide

36.7. Assorted Tips
36.7.1. Ideas for more powerful scripts
·
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,


Advanced Bash-Scripting Guide
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*.

This is a simplified variant of the Example 19-


Advanced Bash-Scripting Guide
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 ] || retur


Advanced Bash-Scripting Guide
#
# Exercise: Rewrite this function to change lowercase passed argument(s)
#
to uppercase ... toupper() [easy].
}

·
Use special-purpose comment headers to increase clarity and legibility in scripts.
## Caution.
rm -rf *.zzy

#+
#
#+
#+

## The "-rf" options to "rm" ar


Advanced Bash-Scripting Guide
done
bar_line="${bar_line}${BAR_CHAR_END}"
printf "%3d%% %s" $2 ${bar_line}
}
# Here is a sample of code that uses it.
MAX_PERCENT=100
for ((i=0; i/dev/null
# An integer is either equal to 0 or not equal to 0.

Chapter 36. Miscellany

523


Advanced Bash-Scripting Guide
# 2>/dev/null suppresses error message.
if [ $? -ne "$SUCCESS" ]
then
echo "Usage: `basename $0` integer-input"
exit $E_BADINPUT
fi
let "sum = $1 + 25"
echo "Sum = $sum"

# Would give error if $1 not integer.

# Any variable, not just a command-line parameter, can be te


Advanced Bash-Scripting Guide
firstchar=${string0:0:1}
string1=${string0:1}

# First character.
# 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 wi


Advanced Bash-Scripting Guide
Next in our bag of tricks are techniques for passing an array to a function, then "returning" an array
back to the main body of the script.
Passing an array involves loading the space-separated elements of the array into a variable with
command substitution. Getting an


Advanced Bash-Scripting Guide
· # NULL VALUE since the array is a variable local to the function.
echo
############################################
# And here is an even more explicit example:
ret_array ()
{
for element in {11..20}
do
echo "$element "
# Echo individual elements
done
#+ of what will


Advanced Bash-Scripting Guide
anagram "$LETTERSET" |
grep "$FILTER" |
grep '^is' |
grep -v 's$' |
grep -v 'ed$'
# Possible to add many
#
#+
#
#

# Find all anagrams of the letterset...
# With at least 7 letters,
# starting with 'is'
# no plurals
# no past tense verbs
combinations of conditions and f


Advanced Bash-Scripting Guide
fi

·
An if-grep test may not return expected results in an error case, when text is output to stderr, rather
that stdout.
if ls -l nonexistent_filename | grep -q 'No such file or directory'
then echo "File \"nonexistent_filename\" does not exist."
fi

Redirecting stde


Advanced Bash-Scripting Guide
The widtools (widget tools) package requires the XForms library to be installed. Additionally, the
Makefile 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, segfaul


Advanced Bash-Scripting Guide
echo "The variable input in the \"input box\" was: "$VARIABLE""

rm $OUTFILE

# Clean up by removing the temp file.
# Some applications may need to retain this file.

exit $?
# Exercise: Rewrite this script using the 'zenity' widget set.

The xmessage command is a simpl


Advanced Bash-Scripting Guide
Unfortunately, according to an article in the October, 2005 Linux Journal, 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.


Advanced Bash-Scripting Guide
· A Regular Expression matching operator
· Bash-specific builtins
· Coprocesses
See the Bash F.A.Q. for a complete listing.

36.9.1. A Test Suite
Let us illustrate some of the incompatibilities between Bash and the classic Bourne shell. Download and
install the "Heir


Advanced Bash-Scripting Guide
csub_test ()
{
echo "PASS"
}
test_csub=$default_option
# FAIL
test_csub=$(csub_test)
echo "Command substitution test: $test_csub"
echo
# Completing this script is an exercise for the reader.
# Add to the above similar tests for double parentheses,
#+ brace expansion, pr


Chapter 37. Bash, versions 2, 3, and 4
37.1. Bash, version 2
The current version of Bash, the one you have running on your machine, is most likely version 2.xx.yy,
3.xx.yy, or 4.xx.yy.
bash$ echo $BASH_VERSION
3.2.25(1)-release

The version 2 update of the classic Bash scripting language added array


Advanced Bash-Scripting Guide
t=table_cell_3
table_cell_3=24
echo "t = ${!t}"
table_cell_3=387
echo "Value of t changed to ${!t}"
# No 'eval' necessary.
#
#+
#
#+

# t = 24
# 387

This is useful for referencing members of an array or table,
or for simulating a multi-dimensional array.
An indexing op


Advanced Bash-Scripting Guide
echo
echo "Catalog number $catalog_number:"
# Now, retrieve value, using indirect referencing.
echo "There are ${!Inv} of [${!Val} ohm / ${!Pdissip} watt]\
resistors in stock." #
^
^
# As of Bash 4.2, you can replace "ohm" with \u2126 (using echo -e).
echo "These are lo


Advanced Bash-Scripting Guide
until [ "$i" -gt $UPPER_LIMIT ]
do
Deck[i]=$UNPICKED
# Set each card of "Deck" as unpicked.
let "i += 1"
done
echo
}
initialize_Suits ()
{
Suits[0]=C #Clubs
Suits[1]=D #Diamonds
Suits[2]=H #Hearts
Suits[3]=S #Spades
}
initialize_Cards ()
{
Cards=(2 3 4 5 6 7 8 9 10 J Q


Advanced Bash-Scripting Guide
do
pick_a_card
t=$?
if [ "$t" -ne $DUPE_CARD ]
then
parse_card $t
u=$cards_picked+1
# Change back to 1-based indexing (temporarily). Why?
let "u %= $CARDS_IN_SUIT"
if [ "$u" -eq 0 ]
# Nested if/then condition test.
then
echo
echo
fi
# Each hand set apart with a blank li


Advanced Bash-Scripting Guide
· A new, more generalized {a..z} brace expansion operator.
#!/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}
echo {e..m}
echo {z..a}
echo {25..30}
ec


Advanced Bash-Scripting Guide
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

·
The =~ Regular Expression matching operator within a double brackets te


Advanced Bash-Scripting Guide
The update to version 3 of Bash breaks a few scripts that worked under earlier versions. Test critical
legacy scripts to make sure they still work!
As it happens, a couple of the scripts in the Advanced Bash Scripting Guide had to be fixed up (see
Example 9-4, for insta


Advanced Bash-Scripting Guide
In fact, quoting in this context is not advisable as it may cause regex evaluation to fail.
Chet Ramey states in the Bash FAQ that quoting explicitly disables regex evaluation.
See also the Ubuntu Bug List and Wikinerds on Bash syntax.
Setting shopt -s compat31 in a scr


Advanced Bash-Scripting Guide
#

-A option declares associative array.

store_address ()
{
address[$1]="$2"
return $?
}

fetch_address ()
{
if [[ -z "${address[$1]}" ]]
then
echo "$1's address is not in database."
return $E_DB
fi
echo "$1's address is ${address[$1]}."
return $?
}

store_address "Luc


Advanced Bash-Scripting Guide
# The ;;& terminator continues to the next pattern test.
|
[[:alnum:]] ) echo "$1 is an alpha/numeric character.";;& # v
[[:alpha:]] ) echo "$1 is an alphabetic character.";;&
# v
[[:lower:]] ) echo "$1 is a lowercase alphabetic character.";;&
[[:digit:]] ) echo "$1 is


Advanced Bash-Scripting Guide
#
^^^^^^^
# 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,
#+


Advanced Bash-Scripting Guide
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' >&${cpname[1]}
myfd=${cpname[0]}
echo myfd=$myfd
### while read -u $myfd
### do
###
echo $REPLY;
### done
ec


Advanced Bash-Scripting Guide
var1=MixedCaseVARIABLE
echo "$var1"
# Same effect as
declare -c var2
var2=originally_lowercase
echo "$var2"
# NOT the same effect as

# mixedcasevariable
echo $var1 | tr A-Z a-z
# Changes only initial char to uppercase.
# Originally_lowercase
echo $var2 | tr a-z A-Z

·


Advanced Bash-Scripting Guide
# 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

· The new ** globbing operator matches filenames and directories recursively.
#!/bin/bash4
# filelist.bash4
shopt -s glob


Advanced Bash-Scripting Guide
} # $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"

Editorial comment
Associative arrays? Coprocesses? Whatever happened to the lean and me


Advanced Bash-Scripting Guide
multi_line_var=$( cat


Advanced Bash-Scripting Guide
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[tradem


Advanced Bash-Scripting Guide
#!/bin/bash
# neg-array.sh
# Requires Bash, version -ge 4.2.
array=( zero one two three four five )
#
0
1
2
3
4
5
#
-6
-5 -4
-3
-2
-1

# Six-element array.

# Negative array indices now permitted.
echo ${array[-1]}
# five
echo ${array[-2]}
# four
# ...
echo ${array[-6]}


Advanced Bash-Scripting Guide
#
Position within string:
0123....6543210
echo ${stringZ:3:-6}
#
ABC123
#
^
# Index 3 chars forward from beginning and 6 chars backward from end,
#+ and extract everything in between.
# ${string:offset-from-front:offset-from-end}
# When the "length" parameter is negativ


Chapter 38. Endnotes
38.1. Author's Note
doce ut discas
(Teach, that you yourself may learn.)
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 w


Advanced Bash-Scripting Guide
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. Please do not waste the
author's time. You will get neither help nor sympathy. [137]


Advanced Bash-Scripting Guide
Paulo Marcel Coelho Aragao offered many corrections, both major and minor, and contributed quite a number
of helpful suggestions.
I would like to especially thank Patrick Callahan, Mike Novak, and Pal Domokos for catching bugs, pointing
out ambiguities, and for suggesti


Advanced Bash-Scripting Guide
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, Serg


Bibliography
Those who do not understand UNIX are
condemned to reinvent it, poorly.
--Henry Spencer

Edited by Peter Denning, Computers Under Attack: Intruders, Worms, and Viruses, ACM Press, 1990,
0-201-53067-8.
This compendium contains a couple of articles on shell script viruses.
*

Ken Burtch, L


Advanced Bash-Scripting Guide
Still considered a standard reference, though somewhat dated, and a bit "wooden" stylistically speaking. [138]
In fact, this book was the ABS Guide author's first exposure to UNIX shell scripting, lo these many years ago.
For more information, see the Kochan-Wood websit


Advanced Bash-Scripting Guide
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 Regular Expressions tutorial from the Dougherty and
Robbins book, above. The comprehensive coverage of UN


Advanced Bash-Scripting Guide
Bill Rosenblatt, Learning the Korn Shell, O'Reilly and Associates, 1993, 1-56592-054-6.
This well-written book contains some excellent pointers on shell scripting in general.
*

Paul Sheer, LINUX: Rute User's Tutorial and Exposition, 1st edition, , 2002, 0-13-033351-4.


Advanced Bash-Scripting Guide
Fioretti, Marco, "Scripting for X Productivity," Linux Journal, Issue 113, September, 2003, pp. 86-9.

Ben Okopnik's well-written introductory Bash scripting articles in issues 53, 54, 55, 57, and 59 of the Linux
Gazette, and his explanation of "The Deep, Dark Secrets o


Advanced Bash-Scripting Guide
Very nice sed, awk, and regular expression tutorials at The UNIX Grymoire.

The GNU sed and gawk manuals. As you recall, gawk is the enhanced GNU version of awk.

Many interesting sed scripts at the seder's grab bag.

Tips and tricks at Linux Reviews.

Trent Fisher's gr


Advanced Bash-Scripting Guide
Tim Waugh's xmlto is an elaborate Bash script for converting Docbook XML documents to other formats.

Philip Patterson's logforbash logging/debugging script.

Of historical interest are Colin Needham's original International Movie Database (IMDB) reader polling
scripts,


Appendix A. Contributed Scripts
These scripts, while not fitting into the text of this document, do illustrate some interesting shell programming
techniques. They are useful, too. Have fun analyzing and running them.

Example A-1. mailformat: Formatting an e-mail message
#!/bin/bash
# mail-format.sh


Advanced Bash-Scripting Guide
Example A-2. rn: A simple-minded file renaming utility
This script is a modification of Example 16-22.
#! /bin/bash
# rn.sh
# Very simpleminded filename "rename" utility (based on "lowercase.sh").
#
# The "ren" utility, by Vladimir Lanin (lanin@csd2.nyu.edu),
#+ does a


Advanced Bash-Scripting Guide
# Substitutes underscores for blanks in all the filenames in a directory.
ONE=1
number=0
FOUND=0

# For getting singular/plural right (see below).
# Keeps track of how many files actually renamed.
# Successful return value.

for filename in *
#Traverse all files in dire


Advanced Bash-Scripting Guide

ftp -n $Server


Advanced Bash-Scripting Guide
*)
esac

echo "$OF not erased.";;

echo
# Exercise:
# Change the above "case" statement to also accept "yes" and "Yes" as input.
exit 0

Example A-6. Collatz series
#!/bin/bash
# collatz.sh
#
#
#
#
#
#
#
#
#
#
#
#+
#+
#+
#
#
#+
#

The notorious "hailstone" or Collatz se


Advanced Bash-Scripting Guide
let "h = h*3 + 1"
fi

# Multiply by 3 and add 1.

COLUMNS=10
# Output 10 values per line.
let "line_break = i % $COLUMNS"
if [ "$line_break" -eq 0 ]
then
echo
fi
done
echo
# For more information on this strange mathematical function,
#+ see _Computers, Pattern, Chaos, a


Advanced Bash-Scripting Guide
Parse_Date ()
{
month=${1%%/**}
dm=${1%/**}
day=${dm#*/}
let "year = `basename $1`"
}

# Parse date from command-line params.

# Day and month.
# Not a filename, but works just the same.

check_date ()
# Checks for invalid date(s) passed.
{
[ "$day" -gt "$DIM" ] || [ "$


Advanced Bash-Scripting Guide
{
if [ "$1" -lt 0 ]
then
let "value = 0 - $1"
else
let "value = $1"
fi

#
#
#+
#+
#+
#+

Uses global "value" variable.
If negative
then
change sign,
else
leave it alone.

}

if [ $# -ne "$ARGS" ]
then
Param_Error
fi
Parse_Date $1
check_date $day $month $year
strip_leadi


Advanced Bash-Scripting Guide
# Modification of /usr/sbin/mkdict (/usr/sbin/cracklib-forman) script.
# Original script copyright 1993, by Alec Muffett.
#
# This modified script included in this document in a manner
#+ consistent with the "LICENSE" document of the "Crack" package
#+ that the original


Advanced Bash-Scripting Guide

ARGCOUNT=1
E_WRONGARGS=90

# Need name as argument.

if [ $# -ne "$ARGCOUNT" ]
then
echo "Usage: `basename $0` name"
exit $E_WRONGARGS
fi

assign_value ()
{
val1=bfpv
val2=cgjkqsxz
val3=dt
val4=l
val5=mn
val6=r

# Assigns numerical value
#+ to letters of name.
# 'b,f,p


Advanced Bash-Scripting Guide
let "char_pos += 1"
name1=${name:$char_pos}

# Bump character position to 2nd letter of name.

# ++++++++++++++++++++++++++ Exception Patch ++++++++++++++++++++++++++++++
# Now, we run both the input name and the name shifted one char
#+ to the right through the value-a


Advanced Bash-Scripting Guide
#
#
#
#
#
#
#+
#+

The U.S. Census and certain other governmental agencies use soundex,
as do genealogical researchers.
For more information,
see the "National Archives and Records Administration home page",
http://www.nara.gov/genealogy/soundex/soundex.html

# Exercise


Advanced Bash-Scripting Guide
############################################
# Abort script if "startfile" not specified
#+ and
#+ default file "gen0" not present.
E_NOSTARTFILE=86
if [ ! -e "$startfile" ]
then
echo "Startfile \""$startfile"\" missing!"
exit $E_NOSTARTFILE
fi
#########################


Advanced Bash-Scripting Guide
local rowcheck
for ((i=0; i


Advanced Bash-Scripting Guide

IsAlive ()
{
GetCount "$1" $2
local nhbd=$?

#
#
#+
#

Test whether cell is alive.
Takes array, cell number, and
state of cell as arguments.
Get alive cell count in neighborhood.

if [ "$nhbd" -eq "$BIRTH" ]
then
return $ALIVE
fi

# Alive in any case.

if [ "$3" = "."


Advanced Bash-Scripting Guide
let "count += 1"
fi

# Increment count.

fi
let "row = $r - 1"
# Count top row.
IsValid $t_top $row
if [ $? -eq "$TRUE" ]
then
if [ ${array[$t_top]} = "$ALIVE1" ] # Redundancy here.
then
# Can it be optimized?
let "count += 1"
fi
fi
let "row = $r + 1"
# Count bottom row


Advanced Bash-Scripting Guide
# Set variable to pass as parameter to "display" function.
avar=`echo ${array[@]}`
# Convert array back to string variable.
display "$avar"
# Display it.
echo; echo
echo "Generation $generation - $alive alive"
if [ "$alive" -eq 0 ]
then
echo
echo "Premature exit: no mor


Advanced Bash-Scripting Guide
# ------ Main loop for displaying subsequent generations -----while [ "$generation" -le "$GENERATIONS" ]
do
Cur="$avar"
next_gen "$Cur"
let "generation += 1"
sleep $DELAY
done
# ==============================================================
echo
# }
exit 0

# CEOF:EOF


Advanced Bash-Scripting Guide
The following script is by Mark Moraes of the University of Toronto. See the file Moraes-COPYRIGHT for
permissions and restrictions. This file is included in the combined HTML/source tarball of the ABS Guide.

Example A-12. behead: Removing mail and news message headers


Advanced Bash-Scripting Guide
Example A-13. password: Generating random 8-character passwords
#!/bin/bash
# May need to be invoked with #!/bin/bash2 on older machines.
#
# Random password generator for Bash 2.x +
#+ by Antek Sawicki ,
#+ who generously gave usage permission to the ABS Guide author.


Advanced Bash-Scripting Guide
# ==> Comments added by author of this document.

HERE=`uname -n`
# ==> hostname
THERE=bilbo
echo "starting remote backup to $THERE at `date +%r`"
# ==> `date +%r` returns time in 12-hour format, i.e. "08:08:34 PM".
# make sure /pipe really is a pipe and not a plain fil


Advanced Bash-Scripting Guide
return
fi
for i; do
#
echo "-n=$n i=$i-"
(( i * i > n )) && break
(( n % i )) && continue
Primes $n $@
return
done
Primes $n $@ $n

# "i" set to "@", previous values of $n.
# Optimization.
# Sift out non-primes using modulo operator.
# Recursion inside loop.

#
#
#+
#


Advanced Bash-Scripting Guide
while [ $zz != $1 ]
do
echo -n "| "

zz=`expr $zz + 1`
done

#
directory level.
# Keep track of inner nested loop.
# ==> Display vertical connector symbol,
# ==> with 2 spaces & no line feed
#
in order to indent.
# ==> Increment zz.

if [ -L "$dir" ] ; then # ==> If dir


Advanced Bash-Scripting Guide
##########
TOP=5
MAXRECURS=5
E_BL=80
E_DIR=81

##########

THIS IS CONFIGURABLE
#
#
#
#

##########

Top 5 biggest (sub)directories.
Max 5 subdirectories/recursions deep.
Blank line already returned.
Directory not specified.

DON'T CHANGE ANYTHING BELOW THIS LINE

PID=$


Advanced Bash-Scripting Guide
}
###
###
###

main program

###
###
###

rootdir="$@"
[ -d "$rootdir" ] ||
{ echo "$SELF: Usage: $SELF " >&2; exit $E_DIR; }
# We should be called with a directory name.
echo "Building inventory list, please wait ..."
# Show "please wait" message.
du -akx "$rootdir" 1>


Advanced Bash-Scripting Guide
###;;;autoload
==> Autoloading of function commented out.
function strcat ()
{
local s1_val s2_val
s1_val=${!1}
# indirect variable expansion
s2_val=${!2}
eval "$1"=\'"${s1_val}${s2_val}"\'
# ==> eval $1='${s1_val}${s2_val}' avoids problems,
# ==> if one of the variable


Advanced Bash-Scripting Guide
return 1
}
#:docstring strncmp:
# Usage: strncmp $s1 $s2 $n
#
# Like strcmp, but makes the comparison by examining a maximum of n
# characters (n less than or equal to zero yields equality).
#:end docstring:
###;;;autoload
function strncmp ()
{
if [ -z "${3}" -o "${3}"


Advanced Bash-Scripting Guide
#
# Strcspn returns the length of the maximum initial segment of string s1,
# which consists entirely of characters not from string s2.
#:end docstring:
###;;;autoload
function strcspn ()
{
# Unsetting IFS allows whitspace to be handled as normal chars.
local IFS=
local


Advanced Bash-Scripting Guide
}
#:docstring strtrunc:
# Usage: strtrunc $n $s1 {$s2} {$...}
#
# Used by many functions like strncmp to truncate arguments for comparison.
# Echoes the first n characters of each string s1 s2 ... on stdout.
#:end docstring:
###;;;autoload
function strtrunc ()
{
n=$1 ;


Advanced Bash-Scripting Guide
Example A-19. Directory information
#! /bin/bash
# directory-info.sh
# Parses and lists directory information.
# NOTE: Change lines 273 and 353 per "README" file.
# Michael Zick is the author of this script.
# Used here with his permission.
#
#
#
#
#
#
#
#

Controls
If


Advanced Bash-Scripting Guide
NOTE: the 'path' may be multiple fields:
/home/mszick/core
/proc/982/fd/0 -> /dev/null
/proc/982/fd/1 -> /home/mszick/.xsession-errors
/proc/982/fd/13 -> /tmp/tmpfZVVOCs (deleted)
/proc/982/fd/7 -> /tmp/kde-mszick/ksycoca
/proc/982/fd/8 -> socket:[11586]
/proc/982/fd/9


Advanced Bash-Scripting Guide
*)
esac

return 1 ;;

# NOTE: the (ls) command is NOT quoted (")
T=( $(ls --inode --ignore-backups --almost-all --directory \
--full-time --color=none --time=status --sort=none \
--format=long $1) )
case $of in
# Assign T back to the array whose name was passed as $2
0)


Advanced Bash-Scripting Guide
Next line index pair for Line-Number+1.
IndexListDoc

IndexList()
{
local
local
local
local

-a
-a
-i
-i

LIST
-i INDEX=( 0 0 )
Lidx Lcnt
if=0 of=0

# Local of listname passed
# Local of index to return
# Default to variable names

case "$#" in
# Simplistic option testi


Advanced Bash-Scripting Guide
esac
INDEX[${#INDEX[*]}]=$inode
INDEX[${#INDEX[*]}]=$name
INDEX[0]=${INDEX[0]}+1
# One more "line" found
# echo "Line: ${INDEX[0]} Type: $ft Links: $m Inode: \
# ${LIST[$inode]} Name: ${LIST[$name]}"
else
((Lidx+=1))
fi
done
case "$of" in
0) eval $2=\( \"\$\{INDEX\[@\]\


Advanced Bash-Scripting Guide
2)
*)
esac

esac ;;
: ;;
return 1 ;;

# Poor man's "continue"

case $if in
0) eval T1=\( \"\$\{$1\[@\]\}\" \)
T2=( $(echo ${T1[@]} | md5sum -) )
;;
1) T2=( $(md5sum $1) )
;;
esac
case ${#T2[@]} in
0) return 1 ;;
1) return 1 ;;
2) case ${T2[1]:0:1} in
# SanScrit-2.0.5
\*


Advanced Bash-Scripting Guide

[12]

[13]

atime of files changed by exec, read, pipe, utime, mknod (mmap?)
atime of directories changed by addition/deletion of files
Time of last modification
mtime of files changed by write, truncate, utime, mknod
mtime of directories changed by addtition/deletion


Advanced Bash-Scripting Guide
Element
Element
Element
Element

7: 2146050
8: 4096
9: 1311552
10: 1276425

StatFieldsDoc

#
#

LocateFile [-l] FileName Location-Array-Name
LocateFile [-l] -of FileName Location-Array-FileName

LocateFile()
{
local -a LOC LOC1 LOC2
local lk="" of=0
case "$#" in
0) retu


Advanced Bash-Scripting Guide
#
}

Element 14: ef53

Filesystem type

# And then there was some test code
ListArray() # ListArray Name
{
local -a Ta
eval Ta=\( \"\$\{$1\[@\]\}\" \)
echo
echo "-*-*- List of Array -*-*-"
echo "Size of array $1: ${#Ta[*]}"
echo "Contents of array $1:"
for (( i=0 ; i


Advanced Bash-Scripting Guide
#
#
#
#

Hash:
Hash function library
Author: Mariusz Gniazdowski
Date: 2005-04-07

# Functions making emulating hashes in Bash a little less painful.

#
#
#
#
#+
#
#+
#
#
#
#
#
#
#

Limitations:
* Only global variables are supported.
* Each hash instance generates one


Advanced Bash-Scripting Guide
# Params:
# 1 - hash1
# 2 - key1
# 3 - hash2
# 4 - key2
function hash_copy {
eval "${Hash_config_varname_prefix}${1}_${2}\
=\"\$${Hash_config_varname_prefix}${3}_${4}\""
}

# Emulates: hash[keyN-1]=hash[key2]=...hash[key1]
#
# Copies first key to rest of keys.
#
# Param


Advanced Bash-Scripting Guide
# 3 - echo params (like -n for example)
function hash_echo_ref {
eval "echo $3 \"${Hash_config_varname_prefix}${1}_${2}\""
}

# Emulates something similar to: $$hash[key](param1, param2, ...)
#
# Params:
# 1 - hash
# 2 - key
# 3,4, ... - Function parameters
function has


Advanced Bash-Scripting Guide
Example A-21. Colorizing text using hash functions
#!/bin/bash
# hash-example.sh: Colorizing text.
# Author: Mariusz Gniazdowski
. Hash.lib

# Load the library of functions.

hash_set
hash_set
hash_set
hash_set
hash_set
hash_set
hash_set
hash_set
hash_set
hash_set
hash


Advanced Bash-Scripting Guide
An example illustrating the mechanics of hashing, but from a different point of view.

Example A-22. More on hash functions
#!/bin/bash
# $Id: ha.sh,v 1.2 2005/04/21 23:24:26 oliver Exp $
# Copyright 2005 Oliver Beckstein
# Released under the GNU Public License
# Author


Advanced Bash-Scripting Guide
# We rely on Bash automatically recognising arrays.
local name=$1
local _keys _values _ptr
_inihash ${name}
eval ${_ptr}=0
}

function addhash () {
# Usage: addhash NAME KEY 'VALUE with spaces'
#
arguments with spaces need to be quoted with single quotes ''
local name=$


Advanced Bash-Scripting Guide
done;
}

# ----------------------------------------------------------------------# Now, let's test it.
# (Per comments at the beginning of the script.)
newhash Lovers
addhash Lovers Tristan Isolde
addhash Lovers 'Romeo Montague' 'Juliet Capulet'
# Output results.
echo
g


Advanced Bash-Scripting Guide
# -----------------# Konstantin Riabitsev, .
# Send any problem reports to my email address at the moment.
#
# ==> Comments added by ABS Guide author.

SYMLINKDEV=/dev/diskonkey
MOUNTPOINT=/mnt/diskonkey
DEVLABEL=/sbin/devlabel
DEVLABELCONFIG=/etc/sysconfig/devlabel
IAM


Advanced Bash-Scripting Guide
##
# This checks if we already have this UUID defined with devlabel.
# If not, it then adds the device to the list.
#
prodid=`$DEVLABEL printid -d $part`
if ! grep -q $prodid $DEVLABELCONFIG; then
# cross our fingers and hope it works
$DEVLABEL add -d $part -s $SYMLINKD


Advanced Bash-Scripting Guide
# Usage: sh tohtml.sh < textfile > htmlfile
# Script can easily be modified to accept source and target filenames.
#
# 1)
# 2)
#
#
# 3)
#+
#+

Assumptions:
Paragraphs in (target) text file are separated by a blank line.
Jpeg images (*.jpg) are located in "images" subdir


Advanced Bash-Scripting Guide
{
while read line
do
{
if [ ! "$line" ]
then
echo
echo "$LF"
echo "$LF"
echo
continue
else

# Read one line at a time.

# Blank line?
# Then new paragraph must follow.
# Insert two tags.

# Skip the underscore test.
# Otherwise . . .

if [[ "$line" =~ "\[*jpg\]" ]] # I


Advanced Bash-Scripting Guide
# =========
#
}
exit $?
#
#
#
#
#+

Exercises:
--------1) Fixup: Check for closing underscore before a comma or period.
2) Add a test for the presence of a closing underscore
in phrases to be italicized.

Here is something to warm the hearts of webmasters and mistresses


Advanced Bash-Scripting Guide
fi
# Move, rename and bzip2 the logs
for logday in $LOG_DAYS; do
for logfile in $LOG_FILES; do
MYFILE="$LOG_DIR/$logfile.$logday"
if [ -w $MYFILE ]; then
DTS=`$LS -lgo --time-style=+%Y%m%d $MYFILE | $COL -t | $CUT -d ' ' -f7`
$MV $MYFILE $BKP_DIR/$logfile.$DTS
$BZ2 $BKP


Advanced Bash-Scripting Guide
the user's chosen names when this is sourced as a
library.
_Protect_Literal_String_Doc
# The 'for illustration' function form
_protect_literal_str() {
# Pick an un-used, non-printing character as local IFS.
# Not required, but shows that we are ignoring it.
local IFS=$'


Advanced Bash-Scripting Guide
#
#
#
#
#

- - Test Two - Element 0: zero is: 4 long.
Element 1: 'Hello ${Me}' is: 13 long.
Element 2: Hello ${You} is: 12 long.
Element 3: \'Pass: \' is: 10 long.

#
#
#
#

Our marker element
Our "$(_pls '...' )"
Quotes are missing
${pw} expanded to nothing

# Now make


Advanced Bash-Scripting Guide
Copyright (c) Michael S. Zick, 2003; All Rights Reserved
License: Unrestricted reuse in any form, for any purpose.
Warranty: None
Revision: $ID$
Documentation redirected to the Bash no-operation. Bash will
'/dev/null' this block when the script is first read.
(Uncomment


Advanced Bash-Scripting Guide
# Element 4: ' is: 1 long

# The end quote is here now.

# set -vx
#
#
#+
#

Initialize 'Me' to something for the embedded ${Me} substitution.
This needs to be done ONLY just prior to evaluating the
protected string.
(This is why it was protected to begin with.)

Me="to


Advanced Bash-Scripting Guide
#!/bin/bash
# $Id: is_spammer.bash,v 1.12.2.11 2004/10/01 21:42:33 mszick Exp $
# Above line is RCS info.
#
#
#
#
#

The latest version of this script is available from http://www.morethan.org.
Spammer-identification
by Michael S. Zick
Used in the ABS Guide with permiss


Advanced Bash-Scripting Guide
3) A filename, with one name or address per line.
Script accepts an optional second argument, which may be:
1) A Blacklist server name;
2) A filename, with one Blacklist server name per line.
If the second argument is not provided, the script uses
a built-in set of (fre


Advanced Bash-Scripting Guide
__is_spammer_Doc_
#######################################################

#### Special IFS settings used for string parsing. ####
# Whitespace == :Space:Tab:Line Feed:Carriage Return:
WSP_IFS=$'\x20'$'\x09'$'\x0A'$'\x0D'
# No Whitespace == Line Feed:Carriage Return
NO_


Advanced Bash-Scripting Guide
# pend_func function_name [$(printf '%q\n' arguments)]
pend_func() {
local IFS=${NO_WSP}
set -f
_pending_[${#_pending_[@]}]=$@
set +f
}
# The function which stops the release:
pend_stop_mark() {
_p_ctrl_=0
}
pend_mark() {
pend_func pend_stop_mark
}
# Execute functions u


Advanced Bash-Scripting Guide
local -a _ee_Excludes
local -a _ee_Target
local _ee_x
local _ee_t
local IFS=${NO_WSP}
set -f
eval _ee_Excludes=\( \$\{$1\[@\]\} \)
eval _ee_Target=\( \$\{$2\[@\]\} \)
local _ee_len=${#_ee_Target[@]}
# Original length.
local _ee_cnt=${#_ee_Excludes[@]}
# Exclude list len


Advanced Bash-Scripting Guide
return 0
}
# This function described in unique_lines.bash.
# unique_lines
unique_lines() {
[ $# -eq 2 ] || return 1
local -a _ul_in
local -a _ul_out
local -i _ul_cnt
local -i _ul_pos
local _ul_tmp
local IFS=${NO_WSP}
set -f
eval _ul_in=\( \$\{$1\[@\]\} \)
_ul_cnt=${#_


Advanced Bash-Scripting Guide
_tl_out=${_tl_out//W/w}
_tl_out=${_tl_out//X/x}
_tl_out=${_tl_out//Y/y}
_tl_out=${_tl_out//Z/z}
echo ${_tl_out}
return 0
}
#### Application helper functions ####
# Not everybody uses dots as separators (APNIC, for example).
# This function described in to_dot.bash
# to_


Advanced Bash-Scripting Guide
eval $2=\(\ \$\{_si_input\[@\]\}\ \)
if [ $# -eq 3 ]
then
# Build query order array.
local -a _dns_ip
_dns_ip[0]=${_si_input[3]}
_dns_ip[1]=${_si_input[2]}
_dns_ip[2]=${_si_input[1]}
_dns_ip[3]=${_si_input[0]}
eval $3=\(\ \$\{_dns_ip\[@\]\}\ \)
fi
return 0
}
# This func


Advanced Bash-Scripting Guide
shift
shift
_cp_cnt=$#
for (( _cp = 0 ; _cp < _cp_cnt ; _cp++ ))
do
_cp_spc[${#_cp_spc[@]}]="${_cp_max:2:$1}" #"
shift
done
_cp_cnt=${#_cp_inp[@]}
for (( _cp = 0 ; _cp < _cp_cnt ; _cp++ ))
do
_cp_pos=1
IFS=${NO_WSP}$'\x20'
_cp_line=( ${_cp_inp[${_cp}]} )
IFS=${NO_WSP}
f


Advanced Bash-Scripting Guide
# Names which we have checked (or given up on)
declare -a known_name
# Addresses which we have checked (or given up on)
declare -a known_address
# List
# Each
#+ with
declare

of zero or more Blacklist servers to check.
'known_address' will be checked against each serve


Advanced Bash-Scripting Guide
local IFS=${NO_WSP}
eval _dda_tmp=\(\ \$\{$1\[@\]\}\ \)
_dda_cnt=${#_dda_tmp[@]}
if [ ${_dda_cnt} -gt 0 ]
then
for (( _dda = 0 ; _dda < _dda_cnt ; _dda++ ))
do
printf "${_dda_form}" \
"${_dda}" "${_dda_tmp[${_dda}]}" >>${_dot_file}
done
fi
}
# Which will also set _dot_d


Advanced Bash-Scripting Guide
dump_to_dot ref_chain RC
fi
if [ ${#name_address[@]} -gt 0 ]
then
echo >>${_dot_file}
echo '# Known name->address edges' >>${_dot_file}
dump_to_dot name_address NA
fi
if [ ${#name_srvc[@]} -gt 0 ]
then
echo >>${_dot_file}
echo '# Known name->service edges' >>${_dot_file


Advanced Bash-Scripting Guide
#+ environment variable SPAMMER_TRACE to the name of a writable file.
declare -a _trace_log
declare _log_file
# Function to fill the trace log
trace_logger() {
_trace_log[${#_trace_log[@]}]=${_pend_current_}
}
# Dump trace log to file function variable.
declare -f _log_


Advanced Bash-Scripting Guide
# [ ${_sf_rc} -ne 9 ] && pend_drop
return ${_sf_rc}
else
# Some versions of 'dig' return warnings on stdout.
_sf_cnt=${#_sf_reply[@]}
for (( _sf = 0 ; _sf < ${_sf_cnt} ; _sf++ ))
do
[ 'x'${_sf_reply[${_sf}]:0:2} == 'x;;' ] &&
unset _sf_reply[${_sf}]
done
eval $2=\( \$\{


Advanced Bash-Scripting Guide
for (( _st = 0 ; _st < ${#_st_cnt} ; _st++ ))
do
[ 'x'${_st_reply[${_st}]:0:2} == 'x;;' ] &&
unset _st_reply[${_st}]
done
eval $2=\( \$\{_st_reply\[@\]\} \)
fi
return 0
}
# The long forms, a.k.a., the parse it yourself versions
#
#
#
#
#

RFC 2782
Service lookups
dig +n


Advanced Bash-Scripting Guide
echo -n ':'
# echo 'lrev: '${1}
_lr_reply=( $(
dig +noall +nofail +answer +authority +additional \
${_lr_dns} -t soa ${_lr_dns} -t any 2>/dev/null) )
_lr_rc=$?
if [ ${_lr_rc} -ne 0 ]
then
_trace_log[${#_trace_log[@]}]='# Deleg lkp error '${_lr_rc}' on '${1}' #'
# [ ${_l


Advanced Bash-Scripting Guide
fi
;;
esac
echo ${_nf_str}
return 0
}
# Grope and mung original input(s).
split_input() {
[ ${#uc_name[@]} -gt 0 ] || return 0
local -i _si_cnt
local -i _si_len
local _si_str
unique_lines uc_name uc_name
_si_cnt=${#uc_name[@]}
for (( _si = 0 ; _si < _si_cnt ; _si++ ))
d


Advanced Bash-Scripting Guide
}
# For each name in uc_name:
#
Move name to chk_name.
#
Add addresses to uc_address.
#
Pend expand_input_address.
#
Repeat until nothing new found.
# expand_input_name
expand_input_name() {
[ ${#uc_name[@]} -gt 0 ] || return 0
local -a _ein_addr
local -a _ein_new
loca


Advanced Bash-Scripting Guide
expand_input_address() {
[ ${#uc_address[@]} -gt 0 ] || return 0
local -a _eia_addr
local -a _eia_name
local -a _eia_new
local -i _uca_cnt
local -i _eia_cnt
local _eia_tst
unique_lines uc_address _eia_addr
unset uc_address[@]
edit_exact been_there_addr _eia_addr
_uca_cn


Advanced Bash-Scripting Guide
local
local
local
local
local
local
local
local
local
local
local
local
local

-a _den_ref
-a _den_nr
-a _den_na
-a _den_ns
-a _den_achn
-i _den_cnt
-i _den_lmt
_den_who
_den_rec
_den_cont
_den_str
_den_str2
IFS=${WSP_IFS}

#
#
#
#
#
#
#
#
#
#
#
#

So does reference cha


Advanced Bash-Scripting Guide
_den_achn[${#_den_achn[@]}]=${_den_who}' '${_den_str2}' SOA.O'
fi
# Responsible party e-mail address (possibly bogus).
# Possibility of first.last@domain.name ignored.
set -f
if _den_str2=$(name_fixup ${_den_tmp[5]})
then
IFS=${ADR_IFS}
_den_auth=( ${_den_str2} )
IFS=${


Advanced Bash-Scripting Guide
if _den_str=$(name_fixup ${_den_tmp[0]})
then
_den_name[${#_den_name[@]}]=${_den_str}
_den_ref[${#_den_ref[@]}]=${_den_who}' '${_den_str}' MX'
fi
# Domain name of service provider
if _den_str=$(name_fixup ${_den_tmp[5]})
then
_den_name[${#_den_name[@]}]=${_den_str}
_den


Advanced Bash-Scripting Guide
#
#

TXT)
;;
esac
fi
done
else # Lookup error == 'A' record 'unknown address'
_den_pair[${#_den_pair[@]}]='0.0.0.0 '${_den_who}
fi
done
# Control dot array growth.
unique_lines _den_achn _den_achn
# Works best, all the same.
edit_exact auth_chain _den_achn
# Works best,


Advanced Bash-Scripting Guide
IFS=${WSP_IFS}
fi
# Update list known_pair (Address and Name).
unique_lines _den_pair _den_pair
edit_exact known_pair _den_pair
if [ ${#_den_pair[@]} -gt 0 ] # Anything new?
then
IFS=${NO_WSP}
known_pair=( ${known_pair[@]} ${_den_pair[@]} )
IFS=${WSP_IFS}
fi
# Update li


Advanced Bash-Scripting Guide
then
known_address=( ${known_address[@]} ${chk_address[@]} )
unset chk_address[@]
fi
return 0
}
# # # Application specific output functions # # #
# Pretty print the known pairs.
report_pairs() {
echo
echo 'Known network pairs.'
col_print known_pair 2 5 30
if [ ${#auth_c


Advanced Bash-Scripting Guide
fi
fi
done
return 0
}
# # # The usual application glue # # #
# Who did it?
credits() {
echo
echo 'Advanced Bash Scripting Guide: is_spammer.bash, v2, 2004-msz'
}
# How to use it?
# (See also, "Quickstart" at end of script.)
usage() {
cat Script failure,
2 -> Something


Advanced Bash-Scripting Guide
# The default list of Blacklist servers:
# Many choices, see: http://www.spews.org/lists.html
declare -a default_servers
# See: http://www.spamhaus.org (Conservative, well maintained)
default_servers[0]='sbl-xbl.spamhaus.org'
# See: http://ordb.org (Open mail relays)
de


Advanced Bash-Scripting Guide
exit 1
fi
_log_file=${SPAMMER_TRACE}
_pend_hook_=trace_logger
_log_dump=dump_log
else
if [ ! -w ${SPAMMER_TRACE} ]
then
pend_func echo $(printf '%q\n' \
'Unable to write log file >'${SPAMMER_TRACE}''${SPAMMER_DATA}''${SPAMMER_DATA}'


Advanced Bash-Scripting Guide
if ! setup_input $1
# Needs error checking.
then
pend_release
$_log_dump
exit 1
fi
list_server=( ${default_servers[@]} )
_list_cnt=${#list_server[@]}
echo 'Using default blacklist server list.'
echo 'Search depth limit: '${indirect}
;;
2)
if ! setup_input $1
# Needs err


Advanced Bash-Scripting Guide
live_log_die
data_capture
echo
do_user_args $@

# Setup debug trace log.
# Setup data capture file.

# # # Haven't exited yet - There is some hope # # #
# Discovery group - Execution engine is LIFO - pend
# in reverse order of execution.
_hs_RC=0
# Hunt the Spammer retu


Advanced Bash-Scripting Guide
Starting with domain name >web4.alojamentos7.com<
Using default blacklist server list.
Search depth limit: 0
.:....::::...:::...:::.......::..::...:::.......::
Known network pairs.
66.98.208.97
web4.alojamentos7.com.
66.98.208.97
ns1.alojamentos7.com.
69.56.202.147
ns2.


Advanced Bash-Scripting Guide
A version of dig which supports the +short options.
See: dig_wrappers.bash for details.

Optional Prerequisites
'named,' a local DNS caching program. Any flavor will do.
Do twice: dig $HOSTNAME
Check near bottom of output for: SERVER: 127.0.0.1#53
That means you have on


Advanced Bash-Scripting Guide
A limit of 0 means: no limit.
i. export SPAMMER_LIMIT=1
or whatever limit you want.
ii. OR provide the desired limit as the first
argument to the script.
3. Optional execution trace log.
(a) To use the default setting of no log output: Do nothing.
(b) To write an execut


Advanced Bash-Scripting Guide
wait (optionally, watch the dots and colons).
8. Optionally check the return code.
(a) Return code 0: All OK
(b) Return code 1: Script setup failure
(c) Return code 2: Something was blacklisted.
9. Where is my graph (diagram)?
The script does not directly produce a grap


Advanced Bash-Scripting Guide
# Known domain name nodes
N0000 [label="guardproof.info."] ;
N0002 [label="third.guardproof.info."] ;

# Known address nodes
A0000 [label="61.141.32.197"] ;

# PC0000 guardproof.info. third.guardproof.info.
N0000->N0002 ;

# NA0000 third.guardproof.info. 61.141.32.197
N


Advanced Bash-Scripting Guide
Michael Zick points out that there is a "makeviz.bash" interactive
Web site at rediris.es. Can't give the full URL, since this is not
a publically accessible site.

Another anti-spam script.

Example A-29. Spammer Hunt
#!/bin/bash
# whx.sh: "whois" spammer lookup
# Auth


Advanced Bash-Scripting Guide
# No response.
then
echo "Host not found!"
exit $E_NOHOST
# Bail out.
fi
if [[ "$IPADDR" =~ "^[;;]" ]]
# ;; connection timed out; no servers could be reached
then
echo "Host lookup timed out!"
exit $E_TIMEOUT
# Bail out.
fi
if [[ "$IPADDR" =~ "[(NXDOMAIN)]$" ]]
# Host x


Advanced Bash-Scripting Guide
#
#
#+
#
#+
#
#
#+
#
#+

Just about every country has its own internet registrar.
I don't normally bother consulting them, because the regional registry
usually supplies sufficient information.
There are a few exceptions, where the regional registry simply
refers to the


Advanced Bash-Scripting Guide
if grep -E "^country:[ ]+BR$" "$OUTFILE"
then
echo "Searching for $IPADDR in whois.registro.br"
whois -h whois.registro.br "$IPADDR" >> $OUTFILE
fi
}
RIPEquery() {
echo "Searching for $IPADDR in whois.ripe.net"
whois -h whois.ripe.net "$IPADDR" > $OUTFILE
}
#
#
#
#

Ini


Advanced Bash-Scripting Guide
elif [ $slash8 -ge 210 ] && [ $slash8 -le 211 ]; then APNICquery "$IPADDR"
elif [ $slash8 -ge 218 ] && [ $slash8 -le 223 ]; then APNICquery "$IPADDR"
# If we got this far without making a decision, query ARIN.
# If a reference is found in $OUTFILE to APNIC, AFRINIC, LAC


Advanced Bash-Scripting Guide
# ==> and cc: to the author of the ABS Guide, please.
# This script is licenced under the GPL.
# You are free to copy, alter and re-use it,
#+ but please don't try to claim you wrote it.
# Log your changes here instead.
# ================================================


Advanced Bash-Scripting Guide
Flag=""

# Default to not saving anything,
#+ or whatever else might be wanted in future.
lister=""
# Used for passing a list of urls directly to wget.
Woptions=""
# Used for passing wget some options for itself.
inFile=""
# Used for the run function.
newFile=""
# Used


Advanced Bash-Scripting Guide
done < $Cookie_List
# Feed the read statement.
echo "Enter the number of the cookie file you want to use."
echo "If you won't be using cookies, just press RETURN."
echo
echo "I won't be asking this again. Edit $Config"
echo "If you decide to change at a later date"
echo


Advanced Bash-Scripting Guide
Flag=S
# Tell the final bit of code what to do.
# Set a flag since stuff is done in main.
return
}

usage() # Tell them how it works.
{
echo "Welcome to wgetter. This is a front end to wget."
echo "It will always run wget with these options:"
echo "$CommandA"
echo "and


Advanced Bash-Scripting Guide
}

cookie_func() # Give the user the option to use a different cookie file.
{
while [ 1 ]; do
echo "Change the cookies file. Press return if you don't want to change
it."
read Cookies
# NB: this is not the same as Cookie, earlier.
# There is an 's' on the end.
# Bit lik


Advanced Bash-Scripting Guide
if [ ! -f "${savePath}/${inFile}" ]; then # If file doesn't exist.
echo "Sorry, that file does not exist. Please choose from:"
ls $savePath
# If a mistake is made.
read inFile
fi
done
filePath="${savePath}/${inFile}" # Make one variable . . .
fi
else filePath="${savePat


Advanced Bash-Scripting Guide
while [ 1 ]; do
# This is where we ask for the most used options.
# (Mostly unchanged from version 1 of wgetter)
if [ -z $curDepth ]; then
Current=""
else Current=" Current value is $curDepth"
fi
echo "How deep should I go? \
(integer: Default is $depthDefault.$Current)


Advanced Bash-Scripting Guide
# Tell the user.
echo "Referring page URL saved to the file$ \
savePath/site-list-${today}"
# Tell the user.
Saver=" with save option"
# Stick this somewhere, so it appears in the loop if set.
else
echo "*****************"
echo "*****Getting*****"
echo "****************


Advanced Bash-Scripting Guide
# ==> This script makes it possible.
# ==> See documentation at the script author's site, above.
# ==> ################################################################

# Make script crontab friendly:
cd $(dirname $0)
# ==> Change to directory where this script lives.
#


Advanced Bash-Scripting Guide
#
#
#
#
#
#
#
#+
#
#
#
#
#
#+
#
#+

nightly-backup.sh
http://www.richardneill.org/source.php#nightly-backup-rsync
Copyright (c) 2005 Richard Neill .
This is Free Software licensed under the GNU GPL.
==> Included in ABS Guide with script author's kind permission.
==> (Th


Advanced Bash-Scripting Guide
# Check
if [ -z
[ -z
[ -z
[ -z
then
echo
exit
fi

that all the important variables have been set:
"$LOCAL_USER" ] ||
"$SOURCE_DIR" ] ||
"$MOUNT_POINT" ] ||
"$BACKUP_DEST_DIR" ]
'One of the variables is not set! Edit the file: $0. BACKUP FAILED.'
$E_VARS_NOT_SET

if [ "$


Advanced Bash-Scripting Guide
#+ extra backups are preserved.
DAY_OF_MONTH=`date +%d`
# Day of month (01..31).
if [ $DAY_OF_MONTH = 01 ]; then
# First of month.
MONTHSTART=true
elif [ $DAY_OF_MONTH = 08 \
-o $DAY_OF_MONTH = 16 \
-o $DAY_OF_MONTH = 24 ]; then
# Day 8,16,24 (use 8, not 7 to better han


Advanced Bash-Scripting Guide
echo "ERROR: directory $BACKUP_DEST_DIR/backup.$i"
echo "is missing and could not be created."
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
# Before we exit, unmount the mount point if necessary.
cd
sudo umount $MOUNT_POINT &&
echo "Unmounted $MOUNT_POINT again. Giving up."


Advanced Bash-Scripting Guide
# Save a readme in the backup parent directory.
# Save another one in the recent subdirectory.
echo "Backup of $SOURCE_DIR on `hostname` was last run on \
`date`" > $BACKUP_DEST_DIR/README.txt
echo "This backup of $SOURCE_DIR on `hostname` was created on \
`date`" > $BA


Advanced Bash-Scripting Guide
/bin/mv
/bin/mv
/bin/mv
/bin/mv

$BACKUP_DEST_DIR/backup.11 $BACKUP_DEST_DIR/backup.12 &&
$BACKUP_DEST_DIR/backup.10 $BACKUP_DEST_DIR/backup.11 &&
$BACKUP_DEST_DIR/backup.9 $BACKUP_DEST_DIR/backup.10 &&
$BACKUP_DEST_DIR/backup.8 $BACKUP_DEST_DIR/backup.9

# At start of


Advanced Bash-Scripting Guide
#
############################################
#
Latest version of this script available from
#
http://freshmeat.net/projects/cd/
#
############################################
#
#
.cd_new
#
#
An enhancement of the Unix cd command
#
#
There are unlimited stack entries a


Advanced Bash-Scripting Guide
and then put it on special entry *
-R [] Go to directory
and put current dir on special entry *
-a
Alternative suggested directory. See note below.
-f [] File entries to .
-u [] Update entries from .
If no filename supplied then default file
(${CDPath}${2:-"$CDFile"})


Advanced Bash-Scripting Guide
Prompt string is set to the right characters of the
current directory.
If not set then prompt is left unchanged
CDL_PROMPT_PRE - Set to the string to prefix the prompt.
Default is:
non-root: \"\\[\\e[01;34m\\]\" (sets colour to blue).
root:
\"\\[\\e[01;31m\\]\" (sets co


Advanced Bash-Scripting Guide
#
cd_dohistory - packs history and specials side by side
#
cd_dohistoryH - Shows only hstory
#
cd_dohistoryS - Shows only specials
#
cd_dohistory ()
{
cd_getrc
${PRINTF} "History:\n"
local -i count=${cd_histcount}
while ${TEST} ${count} -ge 0
do
cd_right_trunc "${CD[cou


Advanced Bash-Scripting Guide
{
local -i nm=0
cd_doflag="TRUE"
if ${TEST} "${CD_MODE}" = "PREV"
then
if ${TEST} -z "$cd_npwd"
then
cd_npwd=0
fi
fi
tm=$(echo "${cd_npwd}" | cut -b 1)
if ${TEST} "${tm}" = "-"
then
pm=$(echo "${cd_npwd}" | cut -b 2)
nm=$(echo "${cd_npwd}" | cut -d $pm -f2)
case "${pm}"


Advanced Bash-Scripting Guide
if ${TEST} $1 -eq 1
then
${PRINTF} "${cd_mnset}"
fi
fi
}
cd_fsave ()
{
local sfile=${CDPath}${2:-"$CDFile"}
if ${TEST} "$1" = "SHOW"
then
${PRINTF} "Saved to %s\n" $sfile
fi
${RM} -f ${sfile}
local -i count=0
while ${TEST} ${count} -le ${cd_maxhistory}
do
echo "CD[$coun


Advanced Bash-Scripting Guide
then
${PRINTF} "Unknown dir: %s\n" "${cd_npwd}"
local -i ftflag=0
for i in "${cd_npwd}"*
do
if ${TEST} -d "${i}"
then
if ${TEST} ${ftflag} -eq 0
then
${PRINTF} "Suggest:\n"
ftflag=1
fi
${PRINTF} "\t-a${choose} %s\n" "$i"
cd_sugg[$choose]="${i}"
choose=${choose}+1
fi
don


Advanced Bash-Scripting Guide
CDL_PROMPT_POST=${CDL_PROMPT_POST:="\\[\\e[00m\\]$"}
fi
#########################################################################
#
# cd_maxhistory defines the max number of history entries allowed.
typeset -i cd_maxhistory=50
###########################################


Advanced Bash-Scripting Guide
cdll allows easy moving about between directories. When changing to a new
directory the current one is automatically put onto a stack. By default
50 entries are kept, but this is configurable. Special directories can be
kept for easy access - by default up to 10, but th


Advanced Bash-Scripting Guide
######################################################################
CDL_PROMPTLEN=21
# Allow a prompt length of up to 21 characters
. /usr/bin/cdll
# Initialise cdll
alias cd='cd_new'
# Replace the built in cd command
alias @='cd_new @'
# Allow @ at the prompt to dis


Advanced Bash-Scripting Guide
have been set up for cd and @ as described above and that cd's prompt
facility is active and the prompt length is 21 characters.
/home/phil$ @
# List the entries with the @
History:
# Output of the @ command
.....
# Skipped these entries for brevity
1 /home/phil/ummdev


Advanced Bash-Scripting Guide
$ cd -S
$ cd -r 1
$ cd -r

Go to special entry 0 and make special entry 0
current dir
Go to history entry 1 and put it on special entry 0
Go to history entry 0 and put it on special entry 0

Alternative suggested directories:
If a directory
possibilities.
and if any are


Advanced Bash-Scripting Guide
There are three variables defined in the file cdll which control the
number of entries stored or displayed. They are in the section labeled
'Initialisation here' towards the end of the file.
cd_maxhistory
cd_maxspecial
cd_histcount

- The number
Default is
- The number


Advanced Bash-Scripting Guide
echo activate > $WSSDEV/resources
echo activate > $CTLDEV/resources

# Parse resource settings.
{ read # Discard "state = active" (see below).
read bla port1
read bla port2
read bla port3
read bla irq
read bla dma1
read bla dma2
# The "bla's" are labels in the first fie


Advanced Bash-Scripting Guide
fm_port=$oplport irq=$irq dma1=$dma1 dma2=$dma2 isapnp=0 index=0
# See the modprobe manpage.
exit $?

Example A-35. Locating split paragraphs in a text file
#!/bin/bash
# find-splitpara.sh
# Finds split paragraphs in a text file,
#+ and tags the line numbers.

ARGCOUNT=


Advanced Bash-Scripting Guide
there is a blank line separating the two parts of the paragraph.
# ---------------------------------------------------------------Running this script on a file containing the above paragraph
yields:
4::

there is a blank line separating the two parts of the paragraph.


Advanced Bash-Scripting Guide
for(( j=i; j; j-- )) do
# Search for the 1st elem. less than current "pivot" . . .
[[ "${list[j-1]}" -le "${list[i]}" ]] && break
done
(( i==j )) && continue ## No insertion was needed for this element.
# . . . Move list[i] (pivot) to the left of list[j]:
list=(${list[@


Advanced Bash-Scripting Guide

arith_mean ()
{
local rt=0
local am=0
local ct=0

# Running total.
# Arithmetic mean.
# Number of data points.

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 $a


Advanced Bash-Scripting Guide
# ++++++++++++++++++++++++++++++++++++++++ #
# A sample data file (sample1.dat):
#
#
#
#
#

18.35
19.0
18.88
18.91
18.64

# $ sh sd.sh sample1.dat
#
#
#
#

Number of data points in "sample1.dat" = 5
Arithmetic mean (average) = 18.756000000
Standard Deviation = .23533805


Advanced Bash-Scripting Guide
else
echo -n "$1 $2? "
fi
read var

# Additional query?

# May paste to fill in field.
# This shows how flexible "read" can be.

if [ -z "$var" ]
then
echo -e "\t\t" >>$savefile
# Indent with 2 tabs.
return
else
echo -e "\t\t$var" >>$savefile
return ${#var}
# Return len


Advanced Bash-Scripting Guide
# Contact_Info
echo "CONTACT INFO"
CONTACT_HDR="Contact_Info"
echo "" >>$savefile
fill_in Author_First_Name
fill_in Author_Last_Name
fill_in Author_Email
fill_in Contact_First_Name
fill_in Contact_Last_Name
fill_in Contact_Email
echo -e "\t" >>$savefile
# END Contact_In


Advanced Bash-Scripting Guide
fill_in
fill_in
fill_in
echo -e
# END

File_Size_Bytes
File_Size_K
File_Size_MB
"\t" >>$savefile
File_Info

clear
# Expire_Info
echo "EXPIRE INFO"
EXPIRE_HDR="Expire_Info"
echo "" >>$savefile
fill_in Has_Expire_Info "Y/N"
fill_in Expire_Count
fill_in Expire_Based_On
fil


Advanced Bash-Scripting Guide
fill_in Char_Desc_250
check_field_length 250 "$?"
fill_in Char_Desc_450
fill_in Char_Desc_2000
echo "" >>$savefile
echo "" >>$savefile
# END Program Description
clear
echo "Done."; echo; echo
echo "Save file is: \""$savefile"\""
exit 0

Example A-39. A man page editor
#


Advanced Bash-Scripting Guide
{
echo -n "Program name? "
read name
echo -n "Manpage section? [Hit RETURN for default (\"1\") ]
read section
if [ -z "$section" ]
then
section=1
# Most man pages are in section 1.
fi
if [ -n "$name" ]
then
savefile=""$name"."$section""
echo -n "$1 " >>$savefile
name1=$


Advanced Bash-Scripting Guide
echo
read
if [
exec

-n "Would you like to view the saved man page (y/n)? "
ans
"$ans" = "n" -o "$ans" = "N" ]; then exit; fi
less "$savefile" # Exit script and hand off control to "less" ...
#+ ... which formats for viewing man page source.

}

# ----------------------


Advanced Bash-Scripting Guide
EXIT=exit

# Give up early?

RANDOM=$$

# Seeds the random number generator from PID of script.

# Bones (ASCII graphics for dice)
bone1[1]="|
|"
bone1[2]="|
o |"
bone1[3]="|
o |"
bone1[4]="| o
o |"
bone1[5]="| o
o |"
bone1[6]="| o
o |"
bone2[1]="|
o
|"
bone2[2]="|
|"
b


Advanced Bash-Scripting Guide
number=0
while [ "$number" -le $FLOOR ]
do
number=$RANDOM
let "number %= $RANGE"
# 1 - 6.
done
return $number
}

throw () {
fortune;
fortune;
fortune;
fortune;
fortune;

# Calculate each individual die.
B1=$?
B2=$?
B3=$?
B4=$?
B5=$?

calc () { # Function embedded within


Advanced Bash-Scripting Guide
while [ "$petal" != "$EXIT" ]
# Main loop.
do
game
read petal
echo "$petal" | grep [0-9] >/dev/null # Filter response for digit.
# Otherwise just roll dice again.
if [ "$?" -eq 0 ]
# If-loop #1.
then
if [ "$petal" == "$answer" ]; then
# If-loop #2.
echo -e "\nCorrect. T


Advanced Bash-Scripting Guide
# QUACKEY: a somewhat simplified version of Perquackey [TM]. #
#
#
# Author: Mendel Cooper
#
# version 0.1.02
03 May, 2008
#
# License: GPL3
#
##############################################################
WLIST=/usr/share/dict/word.lst
#
^^^^^^^^ Word list file found


Advanced Bash-Scripting Guide
and plurals of previously played words are allowed.
"Vulnerable" play is not yet implemented,
but it is otherwise feature-complete.
As the game begins, the player gets 10 letters.
The object is to construct valid dictionary words
of at least 3-letter-length from the let


Advanced Bash-Scripting Guide
For example, "qky 7633" specifies the letterset
c a d i f r h u s k ...
INSTRUCTION3
echo; echo -n "Hit ENTER to begin game. "; read az1
echo -e "\033[0m"
else clear

# Turn off red.

fi
clear
}

seed_random ()
{
if [ -n "$randseed" ]
then
#
RANDOM="$randseed"
echo "RAN


Advanced Bash-Scripting Guide
then
Status[3]="Duplicate-word-PENALTY"
let "Score[0]= 0 - $PENALTY"
let "Score[1]-=$PENALTY"
return $E_DUP
fi
((idx++))
done
Words[idx]="$wrd"
get_score
}
get_score()
{
local wlen=0
local score=0
local bonus=0
local first_word=0
local add_word=0
local numwords=0
wlen=$


Advanced Bash-Scripting Guide
Status[4]=""
fi

# Erase it.

let "score = $first_word +
$add_word * $numwords"
if [ "$numwords" -eq 0 ]
then
Score[0]=$score
else
Score[0]=$add_word
fi
# All this to distinguish last-word score
#+ from total running score.
let "Score[1] += ${Score[0]}"
let "Score[1] +=


Advanced Bash-Scripting Guide
then
return
fi
Status[1]=""
Status[2]=""
Status[3]=""
Status[4]=""
iscons=$(is_constructable "$1")
if [ "$iscons" ]
then
Status[1]="constructable"
v=$(is_valid "$1")
if [ "$v" -eq "$SUCCESS" ]
then
Status[2]="valid"
strlen=${#1}
if [ ${Score[strlen]} -eq "$MAXCAT" ]
# C


Advanced Bash-Scripting Guide
case "$wlen0" in
3) ;;
4) echo -n "
5) echo -n "
6) echo -n "
7) echo -n "
8) echo -n "
esac
echo "${Words[idx]}"
((idx++))
done

" ;;
" ;;
" ;;
" ;;
" ;;

### FIXME: The word display is pretty crude.
}

play ()
{
word="Start game"

# Dummy word, to start ...

while [ "


Advanced Bash-Scripting Guide
echo "Score for this round: $total"
echo "Words: ${Words[@]}"
}
# ---------#
instructions
seed_random
get_letset
play
end_of_game
# ---------#
exit $?
# TODO:
#
# 1) Clean up code!
# 2) Prettify the display_words () function (maybe with widgets?).
# 3) Improve the time-


Advanced Bash-Scripting Guide
1:
2:
3:
4:
5:

| | | | |
| | | |
| | |
| |
|

The number at the left identifies the row.
The human player moves first, and alternates turns with the bot.
A turn consists of removing at least one peg from a single row.
It is permissable to remove ALL the pegs from a row


Advanced Bash-Scripting Guide
done
# ----------------------------------------------echo
((index++))
done
tally_up
rp=${Rows[0]}
if [ "$rp" -eq 1 ]
then
peg_msg=peg
final_msg="Game over."
else
# Game not yet over . . .
peg_msg=pegs
final_msg=""
# . . . So "final message" is blank.
fi
echo "
echo "

$


Advanced Bash-Scripting Guide
then
echo "Premature exit."; echo
tput sgr0
# Restore display.
exit $QUIT
fi
if [ "$num" -gt ${Rows[idx]} -o "$num" -lt 1 ]
then
echo "Cannot remove $num!"
echo -n "Remove how many? "
else
break
fi
done
# TODO:
# Add check for non-numeric input.
# Also, script crashes o


Advanced Bash-Scripting Guide
else
let "num_b = $r0 - 1"
# Leave only a single peg in the row.
fi
# Not a very strong strategy,
#+ but probably a bit better than totally random.
let "Rows[row_b] -= $num_b"
echo -n "Bot: "
echo "Removing from row $row_b ... "
if [ "$num_b" -eq 1 ]
then
peg_msg=peg
el


Advanced Bash-Scripting Guide
#!/bin/sh
# sw.sh
# A command-line Stopwatch
# Author: Pádraig Brady
#
http://www.pixelbeat.org/scripts/sw
#
(Minor reformatting by ABS Guide author.)
#
Used in ABS Guide with script author's permission.
# Notes:
#
This script starts a few processes per lap, in additio


Advanced Bash-Scripting Guide
echo "import time; print time.time()" 2>/dev/null | python
else
printf "%.2f" `date +%s.%N`
fi
}
fmt_seconds() {
seconds=$1
mins=`echo $seconds/60 | bc`
if [ "$mins" != "0" ]; then
seconds=`echo "$seconds - ($mins*60)" | bc`
echo "$mins:$seconds"
else
echo "$seconds"
fi


Advanced Bash-Scripting Guide
total
lap "$laptime"

# Update laptotal.

fi
done
exit $?

Example A-44. An all-purpose shell scripting homework assignment solution
#!/bin/bash
# homework.sh: All-purpose homework assignment solution.
# Author: M. Leo Cooper
# If you substitute your own name as author,


Advanced Bash-Scripting Guide
sleep $DLA
}
restore ()
{
echo -e '\033[0m'
tput sgr0
}

# Bold off.
# Normal.

p_l ()
{
for ltr in $1
do
pt_lt "$ltr"
done
}
# ---------------------b_r
for i in $(seq 0 $MAXL)
do
p_l "${L[i]}"
if [[ "$i" -eq "$P1" || "$i" -eq "$P2" || "$i" -eq "$P3" ]]
then
cr
elif [[


Advanced Bash-Scripting Guide
# but cannot revisit any square he has already visited.
#
#
#
# And just why is Sir Knight unwelcome for a return visit?
#
# Could it be that he has a habit of partying into the wee hours #
#+ of the morning?
#
# Possibly he leaves pizza crusts in the bed, empty beer bo


Advanced Bash-Scripting Guide
for idx in {0..63}
do
board[$idx]=$UNVISITED
done
}

print_board ()
{
local idx
echo "
_____________________________________"
for row in {7..0}
# Reverse order of rows ...
do
#+ so it prints in chessboard order.
let "rownum = $row + 1"
# Start numbering rows at 1.
echo


Advanced Bash-Scripting Guide
fi
local xc=$1
local yc=$2
let "board_index = $xc * $ROWS + yc"
if [ $board_index -lt $MIN -o $board_index -gt $MAX ]
then
return $FAIL
# Strayed off the board!
else
return $board_index
fi
}

to_algebraic ()
{
if [ -z "$1" ]
then
return $FAIL
fi

# Translate board posit


Advanced Bash-Scripting Guide
fi
if [[ $acol -eq $MIN || $acol -gt $ROWS ]]
then
# Outside of range 1 - 8?
return $FAIL
fi
for ix in a b c d e f g h
do # Convert column letter to column number.
if [ "$arow" = "$ix" ]
then
break
fi
((ix_count++))
# Find index count.
done
((acol--))
# Decrementing con


Advanced Bash-Scripting Guide
move4=$BADMOVE
else
((valmov++))
fi
let "move5 = $kt_skip - $ROWS"
# 2 sideways
if [[ `expr $row_pos + $kt_skip` -ge $COLS ]]
then
move5=$BADMOVE
else
((valmov++))
fi
let "move6 = $kt_hop - $kt_skip * $ROWS" # 1 sideways
if [[ `expr $row_pos + $kt_hop` -ge $COLS ]]
then


Advanced Bash-Scripting Guide
mpm=
mov=
declare -a p_moves
########################## DECIDE-MOVE #############################
if [ $startpos -ne $CRITPOS ]
then
# CRITPOS = square #37
decide_move
else
# Needs a special patch for startpos=37 !!!
decide_move_patched
# Why this particular move and no


Advanced Bash-Scripting Guide
test $(( $Moves % $LINELEN )) -eq 0 && echo
# Print 21 moves/line.
return $valid_moves
# Found a square to move to!
fi
done
return $FAIL
# If no square found in all 8 loop iterations,
#+ then Knight's Tour attempt ends in failure.
# Dingbat algorithm will typically fail


Advanced Bash-Scripting Guide
lmin=$mpm
iex=$mov
fi
fi
done

# There has to be a better way to do this.

}

possible_moves ()
{

# Calculate number of possible moves,
#+ given the current position.

if [ -z "$1" ]
then
return $FAIL
fi
local curr_pos=$1
local valid_movl=0
local icx=0
local movl
local


Advanced Bash-Scripting Guide
do
cposl=$1
moves=( $(generate_moves $currpos) )
do_move "$currpos"
if [ $? -eq $FAIL ]
then
failure
fi
done
echo

# Could have condensed above two do-loops into a single one,
#+ but this would have slowed execution.

print_board
echo
echo "Knight's Tour ends on $(to_al


Advanced Bash-Scripting Guide
fi

initialize_board
movenum=0
board[startpos]=$movenum
# Mark each board square with move number.
currpos=$startpos
algpos=$(to_algebraic $startpos)
echo; echo "Starting from $algpos [square #$startpos] ..."; echo
echo -n "Moves:"
strategy "$currpos"
echo
exit 0

# ret


Advanced Bash-Scripting Guide
# All the rows, columns, and long diagonals add up to 15.

# Globals
EVEN=2
MAXSIZE=31
# 31 rows x 31 cols.
E_usage=90
# Invocation error.
dimension=
declare -i square
usage_message ()
{
echo "Usage: $0 square-size"
echo "
... where \"square-size\" is an ODD integer"
ec


Advanced Bash-Scripting Guide
#################################################
if [[ -z "$1" ]] || [[ "$1" -gt $MAXSIZE ]]
then
usage_message
fi
let "test_even = $1 % $EVEN"
if [ $test_even -eq 0 ]
then
# Can't handle even-order squares.
usage_message
fi
calculate $1
print_square

# echo "${square[


Advanced Bash-Scripting Guide

########
# Data #
########
Puzzle=( 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " " )

#############
# Functions #
#############
function swap
{
local tmp
tmp=${Puzzle[$1]}
Puzzle[$1]=${Puzzle[$2]}
Puzzle[$2]=$tmp
}

function Jumble
{ # Scramble the pieces at beginning of roun


Advanced Bash-Scripting Guide
while true
do
echo "Moves: $moves" # Also counts invalid moves.
read -p "Number to move: " puznum garbage
if [ "$puznum" = "quit" ]; then echo; exit $E_PREMATURE_EXIT; fi
test -z "$puznum" -o -n "${puznum//[0-9]/}" && continue
test $puznum -gt 0 -a $puznum -lt $SQUARES


Advanced Bash-Scripting Guide
do
GetNum
puznum=$?
GetPosFromNum $puznum
puzpos=$?
((moves++))
Move $puzpos && break
done
Solved && break
done
echo;echo
PrintPuzzle
echo; echo "BRAVO!"; echo
exit 0
####################################################
#
#
#
#+

Exercise:
-------Rewrite the script to d


Advanced Bash-Scripting Guide
let "lastmove_t = $DISKS - 1"

# Final move?

declare -a Rod1 Rod2 Rod3
###

#########################

function repeat
local n

{

###

# $1=char $2=number of repetitions
# Repeat-print a character.

for (( n=0; n


Advanced Bash-Scripting Guide
sequence=$(echo $(seq 0 $disks1 | tac))
for disk in $sequence; do
for rod in {1..3}; do
eval empty=$(( $DISKS - (Rod${rod}[$disk] / 2) ))
eval fill=\${Rod${rod}[$disk]}
repeat " " $empty
test $fill -gt 0 && repeat "*" $fill || echo -n "|"
repeat " " $empty
done
echo
don


Advanced Bash-Scripting Guide
setup_arrays ()
{
local dim n elem
let "dim1 = $1 - 1"
elem=$dim1
for n in $(seq 0 $dim1)
do
let "Rod1[$elem] = 2 * $n + 1"
Rod2[$n]=0
Rod3[$n]=0
((elem--))
done
}

###

Main

###

setup_arrays $DISKS
echo; echo "+ Start Position"
case $# in
1) case $(($1>0)) in
# Must


Advanced Bash-Scripting Guide
#
#+
#
#

Uses
with
This
Used

code contributed by Antonio Macchi,
heavy editing by ABS Guide author.
variant also falls under the original copyright, see above.
in ABS Guide with Amit Singh's permission (thanks!).

#
Variables
E_NOPARAM=86
E_BADPARAM=87
E_NOEXIT=88
DEL


Advanced Bash-Scripting Guide
while true; do
rod=$2
test ${rod/[^123]} || continue
sequence=$(echo $(seq 0 $disks1 | tac))
for firstfree in $sequence; do
eval weight=\${Rod${rod}[$firstfree]}
test $weight -gt 0 && { (( firstfree++ )); break; }
done
test $weight -gt $1 -o $firstfree = 0 &&
{ echo "$r


Advanced Bash-Scripting Guide
fi
sleep $DELAY
}
# From here down, almost the same as original (hanoi.bash) script.
dohanoi() {
# Recursive function.
case $1 in
0)
;;
*)
dohanoi "$(($1-1))" $2 $4 $3
if [ "$Moves" -ne 0 ]
then
tput cup 0 0
echo; echo "+ Position after move $Moves"
fi
((Moves++))
echo


Advanced Bash-Scripting Guide
exit 0;
;;
*)
echo "$0: Illegal value for number of disks";
exit $E_BADPARAM;
;;
esac
;;
*)
echo "usage: $0 N"
echo "
Where \"N\" is the number of disks."
exit $E_NOPARAM;
;;
esac
exit $E_NOEXIT
#
#
#
#+
#+

# Shouldn't exit here.

Exercise:
-------There is a minor bug


Advanced Bash-Scripting Guide
while true; do
case "${1}" in
--aoption | -a) # Argument found.
echo "Option [$1]"
;;
--debug | -d)
# Enable informational messages.
echo "Option [$1] Debugging enabled"
;;
--file | -f)
# Check for optional argument.
case "$2" in
#+ Double colon is optional argument.
""


Advanced Bash-Scripting Guide
done
#---------------------------------------------------------------------}
################################### M A I N ########################
# If you remove "function UseGetOpt () {" and corresponding "}",
#+ you can uncomment the "exit 0" line below, and invoke th


Advanced Bash-Scripting Guide

while true; do
case "${1}" in
--aoption | -a) # Argument found.
echo "Option [$1]"
;;
--debug | -d)
# Enable informational messages.
echo "Option [$1] Debugging enabled"
;;
--file | -f)
# Check for optional argument.
case "$2" in
#+ Double colon is optional argument.
"


Advanced Bash-Scripting Guide
Example A-52. Cycling through all the possible color backgrounds
#!/bin/bash
#
#
#
#

show-all-colors.sh
Displays all 256 possible background colors, using ANSI escape sequences.
Author: Chetankumar Phulpagare
Used in ABS Guide with permission.

T1=8
T2=6
T3=36
offset=0


Advanced Bash-Scripting Guide
# License: GPL3
# Reldate: 05/25/11
# Morse code training script.
# Converts arguments to audible dots and dashes.
# Note: lowercase input only at this time.

# Get the wav files from the source tarball:
# http://bash.webofcrafts.net/abs-guide-latest.tar.bz2
DOT='soundf


Advanced Bash-Scripting Guide
# The following must be escaped or quoted.
morse[?]="dot; dot; dash; dash; dot; dot"
morse[.]="dot; dash; dot; dash; dot; dash"
morse[,]="dash; dash; dot; dot; dash; dash"
morse[/]="dash; dot; dot; dash; dot"
morse[\@]="dot; dash; dash; dot; dash; dot"
# ===============


Advanced Bash-Scripting Guide
if [ -z "$1" ]
then
no_args
echo; echo; echo "$EXIT_MSG"; echo
exit $E_NOARGS
fi
echo; echo "$*"

# Print text that will be played.

until [ -z "$1" ]
do
extract_letters $1
shift
# On to next word.
usleep $WORDSPACE
echo -n " "
# Print space between words.
done
echo; ec


Advanced Bash-Scripting Guide
# Output text width when encoding
#+ (64 characters, just like openssl output).
text_width=64
function display_base64_char {
# Convert a 6-bit number (between 0 and 63) into its corresponding values
#+ in Base64, then display the result with the specified text width.
pr


Advanced Bash-Scripting Guide
# The next operations depends on the
case ${#c6[*]} in
3) (( c8[1] = ( (c6[1] & 15)
| c6[3] ))

2) ))
;;
2) ))
;;

for char in ${c8[*]}; do
printf "\x$(printf "%x" ${char})"
done
}

# main ()
if [ "$1" = "-d" ]; then

# decode

# Reformat STDIN in pseudo 4x6-bit groups


Advanced Bash-Scripting Guide
GROUPLEN=5
# Output in groups of 5 letters, per tradition.
alpha1=( abcdefghijklmnopqrstuvwxyz )
alpha2=( {A..Z} )
# Output in all caps, per tradition.
# Use
alpha2=( {a..z} )
for password generator.
wraplen=26
# Wrap around if past end of alphabet.
dflag=
# Decrypt fla


Advanced Bash-Scripting Guide
else
echo
fi
} # End encrypt/decrypt function.

# int main () {
# Check
if [ -z
then
echo
exit
fi

if command-line args.
"$1" ]
"Usage: $0 TEXT TO ENCODE/DECODE"
$E_NOARGS

if [ ${!#} == "$Enc_suffix" ]
#
^^^^^ Final command-line arg.
then
dflag=ON
echo -n "+"
# Flag de


Advanced Bash-Scripting Guide
Example A-56. Basics Reviewed
#!/bin/bash
# basics-reviewed.bash
# File extension == *.bash == specific to Bash
#
#
#
#
#
#
#

#
#
#
#+

Copyright (c) Michael S. Zick, 2003; All rights reserved.
License: Use in any form, for any purpose.
Revision: $ID$
Edited for layout


Advanced Bash-Scripting Guide
# A variable may also be an array.
# A string may contain embedded blanks and may be treated
#+ as if it where a function name with optional arguments.
# The names of variables and the names of functions
#+ are in different namespaces.

# A variable may be defined as a


Advanced Bash-Scripting Guide

# All-Elements-Of references
echo $@
echo ${@}

# Same as above
# Same as above

# Within double-quotes, the behavior of Glob-Pattern references
#+ depends on the setting of IFS (Input Field Separator).
# Within double-quotes, All-Elements-Of references behave the same


Advanced Bash-Scripting Guide
#
* A string formated so that it appears to be a function name
#
+ with optional arguments
###
#
Defined elements of a Bash-Array may be undefined (unset).
#
That is, a subscript packed Bash-Array may be changed
#
+
into a subscript sparse Bash-Array.
###
#
Elements may


Advanced Bash-Scripting Guide
echo
echo '---Case1: Within double-quotes - Default IFS of space-tabnewline ---'
IFS=$'\x20'$'\x09'$'\x0A'
# These three bytes,
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[


Advanced Bash-Scripting Guide
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case7: Within double-quotes


Advanced Bash-Scripting Guide
# test='Lit'$'\x00''eral'
# echo ${#test}

# $'\x00' is a null character.
# See that?

# The length of an array, measured in defined elements,
#+ including null content elements.
echo
echo 'Defined content count: '${#ArrayVar[@]}' elements.'
# That is NOT the maximum su


Advanced Bash-Scripting Guide
# Invoke the result of function _simple (Error msg intended)
###
$(_simple)
#
#

# Gives an error message:
line 436: SimpleFunc: command not found
---------------------------------------

echo
###
# The first word of the result of function _simple
#+ is neither a valid


Advanced Bash-Scripting Guide
funcVar="$(_print $VarSomething)"
$funcVar
echo
#
#+
#
#

# $VarSomething replaced HERE.
# The expansion is part of the
#+ variable contents.

The difference between the unquoted and the double-quoted versions
above can be seen in the "protect_literal.sh" example.
The f


Advanced Bash-Scripting Guide
#+ the name of the function.
# The name of the current function is accessed by the $FUNCNAME variable.
###
# A quick, review list follows (quick, not short).
echo
echo
echo
echo
echo
echo
echo

'- - Test (but not change) - -'
'- null reference -'
-n ${VarNull-'NotSet'}'


Advanced Bash-Scripting Guide
echo
echo '- - Test and Change - -'
echo '- Assignment if null reference -'
echo -n ${VarNull='NotSet'}' '
# NotSet NotSet
echo ${VarNull}
unset VarNull
echo '- Assignment if null reference -'
echo -n ${VarNull:='NotSet'}' '
# NotSet NotSet
echo ${VarNull}
unset VarNull


Advanced Bash-Scripting Guide
# local l=${#ArraySparse[@]}
# Count of defined elements
# local f=0
# Count of found subscripts
# local i=0
# Subscript to test
(
# Anonymous in-line function
for (( l=${#ArraySparse[@]}, f = 0, i = 0 ; f < l ; i++ ))
do
# 'if defined then...'
${ArraySparse[$i]+ eval e


Advanced Bash-Scripting Guide
echo ${ArrayVar[@]+'Empty'}
echo

# An array of 'Empty'(ies)

echo '- - Test 2 for undefined - -'
declare -i t
_incT() {
t=$t+1
}
# Note:
# This is the same test used in the sparse array
#+ listing code fragment.
# Null reference, set: t == -1
t=${#VarNull}-1
${VarNull+


Advanced Bash-Scripting Guide
echo
echo
echo
echo

'- All after -'
${VarSomething:1}
${ArrayVar[@]:1}
${@:2}

echo
echo '- Range after -'
echo ${VarSomething:4:3}

# all non-null after character[0]
# all after element[0] with content
# all after param[1] with content

# ral
# Three characters after


Advanced Bash-Scripting Guide
# The -it would be nice- First-Subscript-Of
# echo ${#sparseZ[@]#*}
# This is NOT valid Bash.
echo
echo
echo
echo
echo

'- Longest prefix -'
${stringZ##1*3}
${stringZ##a*C}
${arrayZ[@]##a*c}

# Unchanged (not a prefix)
# abc
# ABCABC 123123 ABCABC

# echo ${sparseZ[@]##


Advanced Bash-Scripting Guide
echo
echo
echo
echo
echo

'- Delete first occurrence -'
${stringZ/$(_123)/}
${stringZ/ABC/}
${arrayZ[@]/ABC/}
${sparseZ[@]/ABC/}

# The replacement need not be a literal,
#+ since the result of a function invocation is allowed.
# This is general to all forms of replacem


Advanced Bash-Scripting Guide
echo ${arrayZ[@]/%abc/}
echo ${sparseZ[@]/%abc/}
echo
echo '- - Special cases of null Glob-Pattern - -'
echo
echo '- Prefix all -'
# null substring pattern means 'prefix'
echo ${stringZ/#/NEW}
# NEWabcABC123ABCabc
echo ${arrayZ[@]/#/NEW}
# Applied to each element.
echo


Appendix B. Reference Cards
The following reference cards provide a useful summary of certain scripting concepts. The foregoing text
treats these matters in more depth, as well as giving usage examples.

Table B-1. Special Shell Variables
Variable
$0
$1
$2 - $9
${10}
$#
"$*"

Meaning
Filename of scr


Advanced Bash-Scripting Guide
String is not empty

-n
Arithmetic Comparison within double parentheses (( ... ))
>
Greater than
>=
Greater than or equal to
<
Less than


Advanced Bash-Scripting Guide
${var:=DEFAULT} If var not set, evaluate expression as $DEFAULT *
${var+OTHER}
${var:+OTHER}

If var set, evaluate expression as $OTHER, otherwise as null string
If var set, evaluate expression as $OTHER, otherwise as null string

${var?ERR_MSG} If var not set, print $E


Advanced Bash-Scripting Guide
expr "$string" : '$substring'
expr index "$string" $substring

expr substr $string $position
$length
expr match "$string"
'\($substring\)'
expr "$string" : '\($substring\)'
expr match "$string"
'.*\($substring\)'
expr "$string" :
'.*\($substring\)'
* Where $substring is


Advanced Bash-Scripting Guide


Appendix C. A Sed and Awk Micro-Primer
This is a very brief introduction to the sed and awk 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.
sed: a non-interactive text file editor


Advanced Bash-Scripting Guide
global

g

Operate on every pattern match within each
matched line of input

Unless the g (global) operator is appended to a substitute command, the substitution operates only on the
first instance of a pattern match within each line.
From the command-line and in a shel


Advanced Bash-Scripting Guide
The most important parts of any application are its GUI and sound effects

results in
The most important parts of any application are its

and sound effects

A backslash forces the sed replacement command to continue on to the next line. This has the effect of using
the


Advanced Bash-Scripting Guide

C.2. Awk
Awk [141] is a full-featured text processing language with a syntax reminiscent of C. 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.
Awk breaks each line of inp


Advanced Bash-Scripting Guide
#
#
#
#
#
#
#

letter-count2.sh: Counting letter occurrences in a text file.
Script by nyal [nyal@voila.fr].
Used in ABS Guide with permission.
Recommented and reformatted by ABS Guide author.
Version 1.1: Modified to work with gawk 3.1.3.
(Will still work with earlier


Advanced Bash-Scripting Guide
# Nothing all that complicated, just . . .
#+ for-loops, if-tests, and a couple of specialized functions.
exit $?
# Compare this script to letter-count.sh.

For simpler examples of awk within shell scripts, see:
1. Example 15-14
2. Example 20-8
3. Example 16-32
4. Examp


Appendix D. Exit Codes With Special Meanings
Table D-1. Reserved Exit Codes
Exit Code
Number
1

Meaning

Example

Catchall for general errors

Comments

let "var1 = 1/0"

Miscellaneous errors, such as
"divide by zero" and other
impermissible operations
2
Misuse of shell builtins (according empty_fun


Appendix E. A Detailed Introduction to I/O and I/O
Redirection
written by Stéphane Chazelas, and revised by the document author

A command expects the first three file descriptors to be available. The first, fd 0 (standard input, stdin), is
for reading. The other two (fd 1, stdout and fd 2, stderr)


Advanced Bash-Scripting Guide
lsof
lsof

426 root
426 root

1w
2w

FIFO
FIFO

0,0
0,0

7520 pipe
7520 pipe

This works for different types of redirection.
Exercise: Analyze the following script.
#! /usr/bin/env bash
mkfifo /tmp/fifo1 /tmp/fifo2
while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 &


Appendix F. Command-Line Options
Many executables, whether binaries or script files, accept options to modify their run-time behavior. For
example: from the command-line, typing command -o would invoke command, with option o.

F.1. Standard Command-Line Options
Over time, there has evolved a loose s


Advanced Bash-Scripting Guide
--recursive
Recursive: Operate recursively (down directory tree).
· -v
--verbose
Verbose: output additional information to stdout or stderr.
· -z
--compress
Compress: apply compression (usually gzip).
However:
· In tar and gawk:
-f
--file
File: filename follows.
· I


Advanced Bash-Scripting Guide
--restricted
Runs the shell, or a script, in restricted mode.
· --posix
Forces Bash to conform to POSIX mode.
· --version
Display Bash version information and exit.
· -End of options. Anything further on the command line is an argument, not an option.

Appendix F. Co


Appendix G. Important Files
startup files
These files contain the aliases and environmental variables made available to Bash running as a user
shell and to all Bash scripts invoked after system initialization.
/etc/profile
Systemwide defaults, mostly setting the environment (all Bourne-type shells,


Appendix H. Important System Directories
Sysadmins and anyone else writing administrative scripts should be intimately familiar with the following
system directories.
· /bin
Binaries (executables). Basic system programs and utilities (such as bash).
· /usr/bin [146]
More system binaries.
· /usr/l


Advanced Bash-Scripting Guide
devices.
· /media
In newer Linux distros, the preferred mount point for I/O devices, such as CD/DVD drives or USB
flash drives.
· /var
Variable (changeable) system files. This is a catchall "scratchpad" directory for data generated while
a Linux/UNIX machine is runnin


Appendix I. An Introduction to Programmable
Completion
The programmable completion feature in Bash permits typing a partial command, then pressing the [Tab] key
to auto-complete the command sequence. [147] If multiple completions are possible, then [Tab] lists them all.
Let's see how it works.
bash$


Advanced Bash-Scripting Guide
Let us take a modified version of the UseGetOpt.sh script as an example command. This script accepts a
number of command-line parameters, preceded by either a single or double dash. And here is the
corresponding completion script, by convention given a filename correspo


Advanced Bash-Scripting Guide
Bash programmable completion project
Mitch Frazier's Linux Journal article, More on Using the Bash Complete Command
Steve's excellent two-part article, "An Introduction to Bash Completion": Part 1 and Part 2

Appendix I. An Introduction to Programmable Completion

792


Appendix J. Localization
Localization is an undocumented Bash feature.
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


Advanced Bash-Scripting Guide
#
#
#
#
#+
#
#
#+
#
#+
#+
#
#
#
#+

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\t


Advanced Bash-Scripting Guide
1. understands the gettext and eval_gettext commands (whereas bash --dump-po-strings understands only
its deprecated $"..." syntax)
2. can extract comments placed by the programmer, intended to be read by the translator.
This shell code is then not specific to Bash any


Advanced Bash-Scripting Guide
--This appendix written by Stéphane Chazelas, with modifications suggested by Alfredo Pironti, and by Bruno
Haible, maintainer of GNU gettext.

Appendix J. Localization

796


Appendix K. History Commands
The Bash shell provides command-line tools for editing and manipulating a user's command history. This is
primarily a convenience, a means of saving keystrokes.
Bash history commands:
1. history
2. fc
bash$ history
1 mount /mnt/cdrom
2 cd /mnt/cdrom
3 ls
...

Internal va


Appendix L. Sample .bashrc and .bash_profile
Files
The ~/.bashrc file determines the behavior of interactive shells. A good look at this file can lead to a
better understanding of Bash.
Emmanuel Rouat contributed the following very elaborate .bashrc file, written for a Linux system. He
welcomes read


Advanced Bash-Scripting Guide
# troublesome).
# I have not found a 'universal' method yet.
#------------------------------------------------------------function get_xserver ()
{
case $TERM in
xterm )
XSERVER=$(who am i | awk '{print $NF}' | tr -d ')''(' )
# Ane-Pieter Wieringa suggests the following


Advanced Bash-Scripting Guide
export HISTTIMEFORMAT="%H:%M > "
export HISTIGNORE="&:bg:fg:ll:h"
export HOSTFILE=$HOME/.hosts
# Put list of remote hosts in ~/.hosts ...

#------------------------------------------------------------# Greeting, motd etc...
#---------------------------------------------


Advanced Bash-Scripting Guide

_powerprompt()
{
LOAD=$(uptime|sed -e "s/.*: \([^,]*\).*/\1/" -e "s/ //g")
}
function powerprompt()
{
PROMPT_COMMAND=_powerprompt
case $TERM in
*term | rxvt )
PS1="${HILIT}[\A - \$LOAD]$NC\n[\u@\h \#] \W > \
\[\033]0;\${TERM} [\u@\h] \w\007\]" ;;
linux )
PS1="${HILIT}[


Advanced Bash-Scripting Guide
#------------------------------------------------------------# The 'ls' family (this assumes you use a recent GNU ls)
#------------------------------------------------------------alias ll="ls -l --group-directories-first"
alias ls='ls -hF --color' # add colors for filet


Advanced Bash-Scripting Guide
alias ncftp="xtitle ncFTP ; ncftp"
# .. and functions
function man()
{
for i ; do
xtitle The $(basename $1|tr -d .[:digit:]) manual
command man -F -a "$i"
done
}

#------------------------------------------------------------# Make the following commands run in backgroun


Advanced Bash-Scripting Guide
find . -type f -name "${2:-*}" -print0 | \
xargs -0 egrep --color=always -sn ${case} "$1" 2>&- | more
}
function cuttail() # cut last n lines in file, 10 by default
{
nlines=${2:-10}
sed -n -e :a -e "1,${nlines}!{P;N;D;};N;ba" $1
}
function lowercase() # move filenames


Advanced Bash-Scripting Guide
echo "'$1' is not a valid file"
fi
}
#------------------------------------------------------------# Process/system related functions:
#-------------------------------------------------------------

function my_ps() { ps $@ -u $USER -o pid,%cpu,%mem,bsdtime,command ; }
f


Advanced Bash-Scripting Guide
done
}

function ask()
# See 'killps' for example of use.
{
echo -n "$@" '[y/n] ' ; read ans
case "$ans" in
y*|Y*) return 0 ;;
*) return 1 ;;
esac
}
function corename()
# Get name of app that created a corefile.
{
for file ; do
echo -n $file : ; gdb --core=$file --batch


Advanced Bash-Scripting Guide
complete -f -o default -X '*.+(bz2|BZ2)' bzip2
complete -f -o default -X '!*.+(bz2|BZ2)' bunzip2
complete -f -o default -X '!*.+(zip|ZIP|z|Z|gz|GZ|bz2|BZ2)' extract

# Documents - Postscript,pdf,dvi.....
complete -f -o default -X '!*.+(ps|PS)' gs ghostview ps2pdf ps2asc


Advanced Bash-Scripting Guide
case "$1" in
\~*)
eval cmd="$1" ;;
*)
cmd="$1" ;;
esac
COMPREPLY=( $(_get_longopts ${1} ${cur} ) )
}
complete
complete

-o default -F _longopts configure bash
-o default -F _longopts wget id info a2ps ls recode

_tar()
{
local cur ext regex tar untar
COMPREPLY=()
cur=${


Advanced Bash-Scripting Guide
return 0
else
# File completion on relevant files.
COMPREPLY=( $( compgen -G $cur\*.$ext ) )
fi
return 0
}
complete -F _tar -o default tar
_make()
{
local mdef makef makef_dir="." makef_inc gcmd cur prev i;
COMPREPLY=();
cur=${COMP_WORDS[COMP_CWORD]};
prev=${COMP_WORDS[


Advanced Bash-Scripting Guide

# If we have a partial word to complete, restrict completions to
# matches of that word.
if [ -n "$cur" ]; then gcmd='grep "^$cur"' ; else gcmd=cat ; fi
COMPREPLY=( $( awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ \
{split($1,A,/ /);for(i in A)print A[i]}' \
$makef 2>/


Advanced Bash-Scripting Guide
COMP_WORDS=( $cline )
# set current token number to 1 less than now.
COMP_CWORD=$(( $COMP_CWORD - 1 ))
# If current arg is empty, add it to COMP_WORDS array
# (otherwise that information will be lost).
if [ -z $cur ]; then COMP_WORDS[COMP_CWORD]="" ; fi
if [ "${cspec%%-


Advanced Bash-Scripting Guide
# Escape a variables whose value changes:
#
if [[ \$EUID -eq 0 ]],
# Otherwise the value of the EUID variable will be assigned only once,
#+ as above.
#
#+
#
#+
#

When a variable is assigned, it should be called escaped:
echo \$T,
Otherwise the value of the T variable


Appendix M. Converting DOS Batch Files to Shell
Scripts
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. Occasion


Advanced Bash-Scripting Guide
IF EXIST FILENAME
IF !%N==!

if [ -e filename ]
if [ -z "$N" ]

CALL
COMMAND /C

source or . (dot operator)
source or . (dot operator)

SET
SHIFT

export
shift

SGN
ERRORLEVEL
CON
PRN
LPT1
COM1

-lt or -gt
$?
stdin
/dev/lp0
/dev/lp0
/dev/ttyS0

test if file exists
if re


Advanced Bash-Scripting Guide
MKDIR
MORE

mkdir
more

MOVE
PATH
REN
RENAME
RD
RMDIR
SORT
TIME

mv
$PATH
mv
mv
rmdir
rmdir
sort
date

TYPE

cat

XCOPY

cp

make directory
text file paging
filter
move
path to executables
rename (move)
rename (move)
remove directory
remove directory
sort file
display s


Advanced Bash-Scripting Guide
Example M-2. viewdata.sh: Shell Script Conversion of VIEWDATA.BAT
#!/bin/bash
# viewdata.sh
# Conversion of VIEWDATA.BAT to shell script.
DATAFILE=/home/bozo/datafiles/book-collection.data
ARGNO=1
# @ECHO OFF

Command unnecessary here.

if [ $# -lt "$ARGNO" ]
then
less


Appendix N. Exercises
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.

On a dingy side street in a run-down section of Hoboken, New Jersey,
there sits a nondescr


Advanced Bash-Scripting Guide
continue
fi
break

# What happens when you comment out this line? Why?

done
echo "Number = $nr"

exit 0

--Explain what the following script does. It is really just a parameterized command-line pipe.
#!/bin/bash
DIRNAME=/usr/bin
FILETYPE="shell script"
LOGFILE=logfile


Advanced Bash-Scripting Guide
Analyze the following "one-liner" (here split into two lines for clarity) contributed by Rory Winston:
export SUM=0; for f in $(find src -name "*.java");
do export SUM=$(($SUM + $(wc -l $f | awk '{ print $1 }'))); done; echo $SUM

Hint: First, break the script up into b


Advanced Bash-Scripting Guide
Given a list of filenames as input, this script queries each target file (parsing the output of the file
command) for the type of compression used on it. Then the script automatically invokes the
appropriate decompression command (gunzip, bunzip2, unzip, uncompress, or


Advanced Bash-Scripting Guide
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 /home/username directory, then the script automatically sends her a
"pigout" warning e-mail.
The script will use the du and mail command


Advanced Bash-Scripting Guide
A lucky number is one whose individual digits add up to 7, in successive additions. For example,
62431 is a lucky number (6 + 2 + 4 + 3 + 1 = 16, 1 + 6 = 7). Find all the lucky numbers between 1000
and 10000.
Craps
Borrowing the ASCII graphics from Example A-40, write a


Advanced Bash-Scripting Guide
You may use last, lastlog, and lastcomm to aid your surveillance of the suspected fiend.
Checking for Broken Links
Using lynx with the -traversal option, write a script that checks a Web site for broken links.
DIFFICULT
Testing Passwords
Write a script to check and vali


Advanced Bash-Scripting Guide
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 previou


Advanced Bash-Scripting Guide
As necessary, use the appropriate network analysis commands.
For some ideas, see Example 16-41 and Example A-28.
Optional: Write a script that searches through a list of e-mail messages and deletes the spam
according to specified filters.
Creating man pages
Write a scri


Advanced Bash-Scripting Guide
The "fog index" 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.
The Gunning version of the fog


Advanced Bash-Scripting Guide
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 le


Advanced Bash-Scripting Guide
II. Encrypting a plaintext message.
III. Decrypting encrypted text.
The script will make extensive use of arrays and functions. You may use Example A-55 as an
inspiration.
-Please do not send the author your solutions to these exercises. There are more appropriate ways


Appendix O. Revision History
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 t


Advanced Bash-Scripting Guide
0.1
0.2
0.3
0.4
0.5
1.0
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
2.0
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
3.0
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
4.0
4.1
4.2
4.3
5.0
5.1
5.2
5.3

14 Jun 2000
30 Oct 2000
12 Feb 2001
08 Jul 2001
03 Sep 2001
14 Oct 2001
06 Jan 2002
31 Mar 2002
02 Ju


Advanced Bash-Scripting Guide
5.4
5.5
5.6
6.0
6.1
6.2
6.3
6.4

21 Jul 2008
23 Nov 2008
26 Jan 2009
23 Mar 2009
30 Sep 2009
17 Mar 2010
30 Apr 2011
30 Aug 2011

ANGLEBERRY release: Major update.
FARKLEBERRY release: Minor update.
WORCESTERBERRY release: Minor update.
THIMBLEBERRY release: Major updat


Appendix P. Download and Mirror Sites
The latest update of this document, as an archived, bzip2-ed "tarball" including both the SGML source and
rendered HTML, may be downloaded from the author's home site). A pdf version is also available. There is
likewise an epub version, courtesy of Craig Barnes.


Appendix Q. To Do List
· A comprehensive survey of incompatibilities between Bash and the classic Bourne shell.
· Same as above, but for the Korn shell (ksh).

Appendix Q. To Do List

833


Appendix R. Copyright
The Advanced Bash Scripting Guide is copyright © 2000, by Mendel Cooper. The author also asserts
copyright on all previous versions of this document. [152]
This blanket copyright recognizes and protects the rights of all contributors to this document.
This document may only be


Advanced Bash-Scripting Guide
If you display or distribute this document, any previous versions thereof, or any derivatives thereof under any
license except the one above, then you are required to obtain the author's written permission. Failure to do so
may terminate your distribution rights.
Additi


Advanced Bash-Scripting Guide
Hyun Jin Cha has done a Korean translation of version 1.0.11 of this book. Spanish, Portuguese, French,
German, Italian, Russian, Czech, Chinese, Indonesian, Dutch, and Romanian translations are also available or
in progress. If you wish to translate this document into


Appendix S. ASCII Table
By tradition, a book of this sort has an ASCII Table appendix. This book does not. Instead, here is a short
shell script that generates a complete ASCII table and writes it to the file ASCII.txt.

Example S-1. A script that generates an ASCII table
#!/bin/bash
# ascii.sh
# ve


Advanced Bash-Scripting Guide

Index
This index / glossary / quick-reference lists many of the important topics covered in the text. Terms are
arranged in approximate ASCII sorting order, modified as necessary for enhanced clarity.
Note that commands are indexed in Part 4.
***
^ (caret)
· Beginning


Advanced Bash-Scripting Guide
Opening a file for both reading and writing
> Right angle bracket
· Is-greater-than
String comparison
Integer comparison, within double parentheses
· Redirection
> Redirect stdout to a file
>> Redirect stdout to a file, but append
i>&j Redirect file descriptor i to f


Advanced Bash-Scripting Guide
terminating curly-bracketed code block
· ;;& ;& Terminators in a case option (version 4+ of Bash).
: Colon
· :> filename Truncate file to zero length
· null command, equivalent to the true Bash builtin
· Used in an anonymous here document
· Used as a function name


Advanced Bash-Scripting Guide


Advanced Bash-Scripting Guide
${!varprefix@}
Match names of all previously declared variables beginning with varprefix
· ${string:position}
${string:position:length} Substring extraction
· ${var#Pattern}
${var##Pattern} Substring removal
· ${var%Pattern}
${var%%Pattern} Substring removal
· ${str


Advanced Bash-Scripting Guide
# Hashmark, special symbol beginning a script comment
#! Sha-bang, special string starting a shell script
* Asterisk
· Wild card, in globbing
· Any number of characters in a Regular Expression
· ** Exponentiation, arithmetic operator
· ** Extended globbing file-matc


Advanced Bash-Scripting Guide
-a Logical AND compound comparison test
Address database, script example
Advanced Bash Scripting Guide, where to download
Alias
· Removing an alias, using unalias
Anagramming
And list
· To supply default command-line argument
And logical operator &&
Angle brackets, es


Advanced Bash-Scripting Guide
· Empty arrays, empty elements, example script
· Indirect references
· Initialization
array=( element1 element2 ... elementN)
Example script
Using command substitution
· Loading a file into an array
· Multidimensional, simulating
· Nesting and embedding
· Notatio


Advanced Bash-Scripting Guide
· Features that classic Bourne shell lacks
· Internal variables
· Version 2
· Version 3
· Version 4
Version 4.1
Version 4.2
.bashrc
$BASH_SUBSHELL
Basic commands, external
Batch files, DOS
Batch processing
bc, calculator utility
· In a here document
· Template fo


Advanced Bash-Scripting Guide
· Enclose character set to match in a Regular Expression
· Test construct
Brackets, curly, {}, used in
· Code block
· find
· Extended Regular Expressions
· Positional parameters
· xargs
break loop control command
· Parameter (optional)
Builtins in Bash
· Do not


Advanced Bash-Scripting Guide
Command-line options
command_not_found_handle () builtin error-handling function (version 4+ of Bash)
Command substitution
· $( ... ), preferred notation
· Backquotes
· Extending the Bash toolset
· Invokes a subshell
· Nesting
· Removes trailing newlines
· Settin


Advanced Bash-Scripting Guide
Cryptography
Curly brackets {}
· in find command
· in an Extended Regular Expression
· in xargs
***
Daemons, in UNIX-type OS
date
dc, calculator utility
dd, data duplicator command
· Conversions
· Copying raw data to/from devices
· File deletion, secure
· Keystro


Advanced Bash-Scripting Guide
dialog, utility for generating dialog boxes in a script
$DIRSTACK directory stack
Disabled commands, in restricted shells
do keyword, begins execution of commands within a loop
done keyword, terminates a loop
DOS batch files, converting to shell scripts
DOS commands, UN


Advanced Bash-Scripting Guide
Escaped characters, special meanings of
· Within $' ... ' string expansion
· Used with Unicode characters
/etc/fstab (filesystem mount) file
/etc/passwd (user account) file
$EUID, Effective user ID
eval, Combine and evaluate expression(s), with variable expansion
· E


Advanced Bash-Scripting Guide
· Substring extraction
· Substring index (numerical position in string)
· Substring matching
Extended Regular Expressions
· ? (question mark) Match zero / one characters
· ( ... ) Group of expressions
· \{ N \} "Curly" brackets, escaped, number of character sets t


Advanced Bash-Scripting Guide
Forking a child process
for loops
Functions
· Arguments passed referred to by position
· Capturing the return value of a function using echo
· Colon as function name
· Definition must precede first call to function
· Exit status
· Local variables
and recursion
·


Advanced Bash-Scripting Guide
Graphic version
Alternate graphic version
getopt, external command for parsing script command-line arguments
· Emulated in a script
getopts, Bash builtin for parsing script command-line arguments
· $OPTIND / $OPTARG
Global variable
Globbing, filename expansion
· Wild


Advanced Bash-Scripting Guide
· cat scripts
· Command substitution
· ex scripts
· Function, supplying input to
· Here strings
Calculating the Golden Ratio
Prepending text
As the stdin of a loop
Using read
· Limit string
! as a limit string
Closing limit string may not be indented
Dash option t


Advanced Bash-Scripting Guide
in, keyword preceding [list] in a for loop
Initialization table, /etc/inittab
Inline group, i.e., code block
Interactive script, test for
I/O redirection
Indirect referencing of variables
· New notation, introduced in version 2 of Bash ( example script)
iptables, packe


Advanced Bash-Scripting Guide
· C-style increment and decrement operators
Limit string, in a here document
$LINENO, variable indicating the line number where it appears in a script
Link, file (using ln command)
· Invoking script with multiple names, using ln
· symbolic links, ln -s
List construct


Advanced Bash-Scripting Guide
Parameterizing [list]
Redirection
· in, (keyword) preceding [list] in a for loop
· Nested loops
· Running a loop in the background, script example
· Semicolon required, when do is on first line of loop
for loop
while loop
· until loop
until [ condition-is-true ]; d


Advanced Bash-Scripting Guide
Math commands
Meta-meaning
Morse code training script
Modulo, arithmetic remainder operator
· Application: Generating prime numbers
Mortgage calculations, example script
***
-n String not null test
Named pipe, a temporary FIFO buffer
· Example script
nc, netcat, a net


Advanced Bash-Scripting Guide
$OLDPWD Previous working directory
openssl encryption utility
Operator
· Definition of
· Precedence
Options, passed to shell or script on command line or by set command
Or list
Or logical operator, ||
***
Parameter substitution
· ${parameter+alt_value}
${parameter:+a


Advanced Bash-Scripting Guide
· Command group
· Enclose group of Extended Regular Expressions
· Double parentheses, in arithmetic expansion
$PATH, the path (location of system binaries)
· Appending directories to $PATH using the += operator.
Perl, programming language
· Combined in the same fil


Advanced Bash-Scripting Guide
= and -eq not interchangeable
· Omitting terminal semicolon, in a curly-bracketed code block
· Piping
echo to a loop
echo to read (however, this problem can be circumvented)
tail -f to grep
· Preserving whitespace within a variable, unintended consequences
· suid co


Advanced Bash-Scripting Guide
· Generating primes using the factor command
· Generating primes using the modulo operator
· Sieve of Eratosthenes, example script
printf, formatted print command
/proc directory
· Running processes, files describing
· Writing to files in /proc, warning
Process
·


Advanced Bash-Scripting Guide
· Whitespace, using quoting to preserve
***
Random numbers
· /dev/urandom
· rand(), random function in awk
· $RANDOM, Bash function that returns a pseudorandom integer
· Random sequence generation, using date command
· Random sequence generation, using jot
· Rand


Advanced Bash-Scripting Guide
2>&1
· stdin / stdout, using · stdinof a function
· stdout to a file
> ... >>
· stdout to file descriptor j
>&j
· file descriptori to file descriptor j
i>&j
· stdout of a command to stderr
>&2
· stdout and stderr of a command to a file
&>
· tee, redirect to a fi


Advanced Bash-Scripting Guide
Restricted shell, shell (or script) with certain commands disabled
return, command that terminates a function
run-parts
· Running scripts in sequence, without user intervention
***
Scope of a variable, definition
Script options, set at command line
Scripting routines,


Advanced Bash-Scripting Guide
shift, reassigning positional parameters
$SHLVL, shell level, depth to which the shell (or script) is nested
shopt, change shell options
Signal, a message sent to a process
Simulations
· Brownian motion
· Galton board
· Horserace
· Life, game of
· PI, approximating


Advanced Bash-Scripting Guide
Strings
· =~ String match operator
· Comparison
· Length
${#string}
· Manipulation
· Manipulation, using awk
· Null string, testing for
· Protecting strings from expansion and/or reinterpretation, script example
Unprotecting strings, script example
· strchr(), e


Advanced Bash-Scripting Guide
Subshell
· Command list within parentheses
· Variables, $BASH_SUBSHELL and $SHLVL
· Variables in a subshell
scope limited, but ...
... can be accessed outside the subshell?
su Substitute user, log on as a different user or as root
suid (set user id) file flag
· suid


Advanced Bash-Scripting Guide
· -eq is-equal-to (integer comparison)
· -f File is a regular file
· -ge greater-than or equal (integer comparison)
· -gt greater-than (integer comparison)
· -le less-than or equal (integer comparison)
· -lt less-than (integer comparison)
· -n not-zero-length (st


Advanced Bash-Scripting Guide
Using if-then constructs
· Comment headers, special purpose
· C-style syntax , for manipulating variables
· Double-spacing a text file
· Filenames prefixed with a dash, removing
· Filter, feeding output back to same filter
· Function return value workarounds
· if


Advanced Bash-Scripting Guide
true, returns successful (0) exit status
typeset builtin
· options
***
$UID, User ID number
unalias, to remove an alias
uname, output system information
Unicode, encoding standard for representing letters and symbols
Uninitialized variables
uniq, filter to remove dupli


Advanced Bash-Scripting Guide
${!variable}
· Integer
· Integer / string (variables are untyped)
· Length
${#var}
· Lvalue
· Manipulating and expanding
· Name and value of a variable, distinguishing between
· Null string, testing for
· Null variable assignment, avoiding
· Quoting
within test


Advanced Bash-Scripting Guide
· Preceding closing limit string in a here document, error
· Preceding script comments
· Quoting, to preserve whitespace within strings or variables
· [:space:], POSIX character class
who, information about logged on users
·w
· whoami
· logname
Widgets
Wild card


Advanced Bash-Scripting Guide
Notes
[1]
[2]
[3]
[4]
[5]

[6]
[7]
[8]

These are referred to as builtins, features internal to the shell.
Although recursion is possible in a shell script, it tends to be slow and its implementation is often an
ugly kludge.
An acronym is a word formed by pasting togeth


Advanced Bash-Scripting Guide
[12] If Bash is your default shell, then the #! isn't necessary at the beginning of a script. However, if
launching a script from a different shell, such as tcsh, then you will need the #!.
[13] Caution: invoking a Bash script by sh scriptname turns off Bash-specific ex


Advanced Bash-Scripting Guide
From the command-line, however, $0 is the name of the shell.
bash$ echo $0
bash
tcsh% echo $0
tcsh

[27] If the the script is sourced or symlinked, then this will not work. It is safer to check $BASH_Source.
[28] Unless there is a file named first in the current working


Advanced Bash-Scripting Guide
Deprecate
...
To
to
to
to
to
to

pray against, as an evil;
seek to avert by prayer;
desire the removal of;
seek deliverance from;
express deep regret for;
disapprove of strongly.

[34] Be aware that suid binaries may open security holes. The suid flag has no effect on s


Advanced Bash-Scripting Guide

[50]
[51]
[52]
[53]

Note that $substring and $replacement may refer to either literal strings or variables,
depending on context. See the first usage example.
If $parameter is null in a non-interactive script, it will terminate with a 127 exit status (the Bash error
c


Advanced Bash-Scripting Guide
Note that the -f option to enable is not portable to all systems.
[68] The same effect as autoload can be achieved with typeset -fu.
[69]
Dotfiles are files whose names begin with a dot, such as ~/.Xdefaults. Such filenames do not
appear in a normal ls listing (although


Advanced Bash-Scripting Guide

[88]
[89]
[90]
[91]

[92]
[93]

For more detail on burning CDRs, see Alex Withers' article, Creating CDs, in the October, 1999 issue
of Linux Journal.
The -c option to mke2fs also invokes a check for bad blocks.
Since only root has write permission in the /var/lock dir


Advanced Bash-Scripting Guide
~/[.]bashrc
~/?bashrc

#
#
#
#+

Will not expand to ~/.bashrc
Neither will this.
Wild cards and metacharacters will NOT
expand to a dot in globbing.

~/.[b]ashrc
~/.ba?hrc
~/.bashr*

#
#
#

Will expand to ~/.bashrc
Likewise.
Likewise.

# Setting the "dotglob" option tur


Advanced Bash-Scripting Guide

[108]
[109]
[110]
[111]
[112]

"Local can only be used within a function; it makes the variable name have a visible scope restricted to
that function and its children." [emphasis added] The ABS Guide author considers this behavior to be a
bug.
Otherwise known as redund


Advanced Bash-Scripting Guide
[120] Setting the suid permission on the script itself has no effect in Linux and most other UNIX flavors.
[121] In this context, "magic numbers" have an entirely different meaning than the magic numbers used to
designate file types.
[122] Quite a number of Linux utilit


Advanced Bash-Scripting Guide
[146] Some early UNIX systems had a fast, small-capacity fixed disk (containing /, the root partition), and a
second drive which was larger, but slower (containing /usr and other partitions). The most frequently
used programs and utilities therefore resided on the small

.
.
.
.