Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced Bash Shell Scripting Gude.pdf
Скачиваний:
62
Добавлен:
17.08.2013
Размер:
4.82 Mб
Скачать

Quoting

Advanced Bash-Scripting Guide:

Prev

Next

Chapter 6. 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, such as the wild card character, *.)

bash$ ls -l

[Vv]*

 

 

 

 

 

 

-rw-rw-r--

1 bozo

bozo

324 Apr

2 15:05 VIEWDATA.BAT

-rw-rw-r--

1

bozo

bozo

507

May

4

14:25 vartrace.sh

-rw-rw-r--

1

bozo

bozo

539

Apr

14

17:11 viewdata.sh

bash$ ls -l

'[Vv]*'

 

 

 

 

 

ls: [Vv]*: No such file or directory

Certain programs and utilities can still reinterpret or expand special characters in a quoted string. This is an important use of quoting, protecting a command-line parameter from the shell, but still letting the calling program expand it.

bash$ grep '[Ff]irst' *.txt

file1.txt:This is the first line of file1.txt. file2.txt:This is the First line of file2.txt.

Of course, grep [Ff]irst *.txt would not work.

When referencing a variable, it is generally advisable in enclose it in double quotes (" "). This preserves all special characters within the variable name, except $, ` (backquote), and \ (escape). Keeping $ as a special character permits referencing a quoted variable ("$variable"), that is, replacing the variable with its value (see Example 5-1, above).

Use double quotes to prevent word splitting. [1] An argument enclosed in double quotes presents itself as a single word, even if it contains whitespace separators.

variable1="a variable containing five words"

COMMAND This is $variable1

# Executes COMMAND with 7 arguments:

 

# "This" "is" "a" "variable" "containing" "five" "words"

 

COMMAND "This is $variable1" # Executes COMMAND with 1 argument:

 

# "This is a variable containing five words"

 

 

variable2=""

# Empty.

 

 

 

COMMAND $variable2 $variable2 $variable2

# Executes COMMAND

with no arguments.

COMMAND "$variable2" "$variable2" "$variable2"

# Executes COMMAND

with 3 empty

arguments.

 

 

 

 

COMMAND "$variable2 $variable2 $variable2"

# Executes COMMAND

with 1 argument (2

spaces).

 

 

 

 

http://tldp.org/LDP/abs/html/quoting.html (1 of 7) [7/15/2002 6:34:02 PM]

Quoting

# Thanks, S.C.

Enclosing the arguments to an echo statement in double quotes is necessary only when word splitting is an issue.

Example 6-1. Echoing Weird Variables

#!/bin/bash

# weirdvars.sh: Echoing weird variables.

var="'(]\\{}\$\""

 

 

echo $var

# '(]\{}$"

 

echo "$var"

# '(]\{}$"

Doesn't make a difference.

echo

 

 

IFS='\'

 

 

echo $var

# '(] {}$"

\ converted to space.

echo "$var"

# '(]\{}$"

 

# Examples above supplied by S.C.

exit 0

Single quotes (' ') operate similarly to double quotes, but do not permit referencing variables, since the special meaning of $ is turned off. Within single quotes, every special character except ' gets interpreted literally. Consider single quotes ("full quoting") to be a stricter method of quoting than double quotes ("partial quoting").

Since even the escape character (\) gets a literal interpretation within single quotes, trying to enclose a single quote within single quotes will not yield the expected result.

echo "Why can't I write 's between single quotes"

echo

# The roundabout method.

echo 'Why can'\''t I write '"'"'s between single quotes'

#|-------| |----------| |-----------------------|

#Three single-quoted strings, with escaped and quoted single quotes between.

#This example courtesy of Stephane Chazelas.

Escaping is a method of quoting single characters. The escape (\) preceding a character tells the shell to interpret that character literally.

With certain commands and utilities, such as echo and sed, escaping a character may have the opposite effect - it can toggle on a special meaning for that character.

Special meanings of certain escaped characters

http://tldp.org/LDP/abs/html/quoting.html (2 of 7) [7/15/2002 6:34:02 PM]

Quoting

used with echo and sed

\n

means newline

\r

means return

\t

means tab

\v

means vertical tab

\b

means backspace

\a

means "alert" (beep or flash)

\0xx

translates to the octal ASCII equivalent of 0xx

Example 6-2. Escaped Characters

#!/bin/bash

# escaped.sh: escaped characters

echo; echo

echo "\v\v\v\v"

# Prints \v\v\v\v

#Use the -e option with 'echo' to print escaped characters. echo -e "\v\v\v\v" # Prints 4 vertical tabs.

echo -e "\042" # Prints " (quote, octal ASCII character 42).

#The $'\X' construct makes the -e option unnecessary.

echo $'\n'

# Newline.

echo $'\a'

# Alert (beep).

# Version

2 and later of Bash permits using the $'\xxx' construct.

echo $'\t

\042 \t'

# Quote (") framed by tabs.

#Assigning ASCII characters to a variable.

#----------------------------------------

quote=$'\042' # " assigned to a variable.

echo "$quote This is a quoted string, $quote and this lies outside the quotes."

echo

# Concatenating ASCII chars in a variable. triple_underline=$'\137\137\137' # 137 is octal ASCII code for '_'. echo "$triple_underline UNDERLINE $triple_underline"

http://tldp.org/LDP/abs/html/quoting.html (3 of 7) [7/15/2002 6:34:02 PM]

no visible output.

Quoting

ABC=$'\101\102\103\010'

# 101, 102, 103 are octal A, B, C.

echo $ABC

 

echo; echo

 

escape=$'\033'

# 033 is octal for escape.

echo "\"escape\" echoes as $escape"

#

echo; echo

exit 0

See Example 35-1 for another example of the $' ' string expansion construct.

\"

gives the quote its literal meaning

echo

"Hello"

#

Hello

echo

"\"Hello\", he said."

#

"Hello", he said.

 

 

 

 

\$

gives the dollar sign its literal meaning (variable name following \$ will not be referenced)

echo "\$variable01" # results in $variable01

\\

gives the backslash its literal meaning

echo "\\" # results in \

The behavior of \ depends on whether it is itself escaped, quoted, or appearing within command substitution or a here document.

http://tldp.org/LDP/abs/html/quoting.html (4 of 7) [7/15/2002 6:34:02 PM]

Quoting

 

#

Simple escaping and quoting

echo \z

#

z

echo \\z

# \z

echo '\z'

# \z

echo '\\z'

# \\z

echo "\z"

# \z

echo "\\z"

# \z

 

#

Command substitution

echo `echo \z`

#

z

echo `echo \\z`

#

z

echo `echo \\\z`

# \z

echo `echo \\\\z`

# \z

echo `echo \\\\\\z`

# \z

echo `echo \\\\\\\z`

# \\z

echo `echo "\z"`

# \z

echo `echo "\\z"`

# \z

 

# Here document

cat <<EOF

 

 

\z

 

 

EOF

# \z

cat <<EOF

 

 

\\z

 

 

EOF

# \z

# These examples supplied by Stephane Chazelas.

Elements of a string assigned to a variable may be escaped, but the escape character alone may not be assigned to a variable.

variable=\

echo "$variable"

#Will not work - gives an error message:

#test.sh: : command not found

#A "naked" escape cannot safely be assigned to a variable.

#What actually happens here is that the "\" escapes the newline and

#+ the effect is

variable=echo "$variable"

#+

 

 

invalid variable assignment

variable=\

 

 

 

23skidoo

 

 

 

 

echo "$variable"

#

23skidoo

 

 

 

#

This works, since the second line

 

 

 

#+ is a valid variable assignment.

variable=\

 

 

 

#

\^

escape followed by space

echo "$variable"

# space

variable=\\

 

 

 

echo "$variable"

# \

 

http://tldp.org/LDP/abs/html/quoting.html (5 of 7) [7/15/2002 6:34:02 PM]

Quoting

variable=\\\ echo "$variable"

# Will not work - gives an error message:

#test.sh: \: command not found

#First escape escapes second one, but the third one is left "naked", #+ with same result as first instance, above.

variable=\\\\

 

echo "$variable"

# \\

 

# Second and fourth escapes escaped.

 

# This is o.k.

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 /sbin/dump $file_list

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

"

# What happens if we escape a couple of spaces?

 

ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list

 

#Error: the first three files concatenated into a single argument to 'ls -l'

#because the two escaped spaces prevent argument (word) splitting.

The escape also provides a means of writing a multi-line command. Normally, each separate line constitutes a different command, but an escape at the end of a line escapes the newline character, and the command sequence continues on to the next line.

(cd /source/directory && tar cf - . ) | \ (cd /dest/directory && tar xpvf -)

#Repeating Alan Cox's directory tree copy command,

#but split into two lines for increased legibility.

#As an alternative:

tar cf - -C /source/directory . | tar xpvf - -C /dest/directory

#See note below.

#(Thanks, Stephane Chazelas.)

If a script line ends with a |, a pipe character, then a \, an escape, is not strictly necessary. It is, however, good programming practice to always escape the end of a line of code that continues to the following line.

http://tldp.org/LDP/abs/html/quoting.html (6 of 7) [7/15/2002 6:34:02 PM]

Quoting

echo "foo bar"

#foo

#bar

echo

echo 'foo

bar' # No difference yet. #foo

#bar

echo

echo foo\

bar # Newline escaped. #foobar

echo

echo "foo\

bar" # Same here, as \ still interpreted as escape within weak quotes. #foobar

echo

echo 'foo\

bar' # Escape character \ taken literally because of strong quoting. #foor\

#bar

# Examples suggested by Stephane Chazelas.

Notes

[1]"Word splitting", in this context, means dividing a character string into a number of separate and discrete arguments.

Prev

Home

Next

Special Variable Types

Up

Tests

http://tldp.org/LDP/abs/html/quoting.html (7 of 7) [7/15/2002 6:34:02 PM]

Command Substitution

Advanced Bash-Scripting Guide:

Prev

Next

Chapter 14. Command Substitution

Command substitution reassigns the output of a command [1] or even multiple commands; it literally plugs the command output into another context.

The classic form of command substitution uses backquotes (`...`). Commands within backquotes (backticks) generate command line text.

script_name=`basename $0`

echo "The name of this script is $script_name."

The output of commands can be used as arguments to another command, to set a variable, and even for generating the argument list in a for loop.

rm `cat filename`

# "filename" contains a list of files to delete.

#

 

# S. C. points out that "arg list too long" error might result.

# Better is

xargs rm -- < filename

# ( -- covers those

cases where "filename" begins with a "-" )

textfile_listing=`ls *.txt`

# Variable contains names of all *.txt files in current working directory. echo $textfile_listing

textfile_listing2=$(ls *.txt) # The alternative form of command substitution. echo $textfile_listing

#Same result.

#A possible problem with putting a list of files into a single string

#is that a newline may creep in.

#

# A safer way to assign a list of files to a parameter is with an array.

# shopt -s nullglob # If no match, filename expands to nothing.

#textfile_listing=( *.txt )

#Thanks, S.C.

http://tldp.org/LDP/abs/html/commandsub.html (1 of 6) [7/15/2002 6:34:03 PM]

Command Substitution

Command substitution may result in word splitting.

COMMAND `echo a b`

# 2

args: a

and b

COMMAND "`echo a b`"

# 1

arg: "a b"

COMMAND `echo`

# no arg

 

COMMAND "`echo`"

# one empty arg

# Thanks, S.C.

 

 

 

 

 

 

 

Even when there is no word splitting, command substitution can remove trailing newlines.

#cd "`pwd`" # This should always work.

#However...

mkdir 'dir with trailing newline

'

cd 'dir with trailing newline

'

cd "`pwd`"

# Error message:

# bash: cd:

/tmp/file with trailing newline: No such file or directory

cd "$PWD"

# Works fine.

old_tty_setting=$(stty -g)

# Save old terminal setting.

echo "Hit a key "

 

stty -icanon -echo

# Disable "canonical" mode for terminal.

 

# Also, disable *local* echo.

key=$(dd bs=1 count=1 2> /dev/null) # Using 'dd' to get a keypress.

stty "$old_tty_setting"

# Restore old setting.

echo "You hit ${#key} key." # ${#variable} = number of characters in $variable

#

#Hit any key except RETURN, and the output is "You hit 1 key."

#Hit RETURN, and it's "You hit 0 key."

#The newline gets eaten in the command substitution.

Thanks, S.C.

http://tldp.org/LDP/abs/html/commandsub.html (2 of 6) [7/15/2002 6:34:03 PM]

Command Substitution

Using echo to output an unquoted variable set with command substitution removes trailing newlines characters from the output of the reassigned command(s). This can cause unpleasant surprises.

dir_listing=`ls -l`

 

echo $dir_listing

# unquoted

#Expecting a nicely ordered directory listing.

#However, what you get is:

#total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo

#bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

#The newlines disappeared.

echo "$dir_listing" # quoted

 

 

 

# -rw-rw-r--

1

bozo

30

May 13

17:15 1.txt

# -rw-rw-r--

1

bozo

51

May

15

20:57 t2.sh

# -rwxr-xr-x

1

bozo

217

Mar

5

21:13 wi.sh

Command substitution even permits setting a variable to the contents of a file, using either redirection or the cat command.

variable1=`<file1`

#

Set

"variable1"

to

contents

of

"file1".

variable2=`cat file2`

#

Set

"variable2"

to

contents

of

"file2".

#Be aware that the variables may contain embedded whitespace, #+ or even (horrors), control characters.

#Excerpts from system file, /etc/rc.d/rc.sysinit

#+ (on a Red Hat Linux installation)

if [ -f /fsckoptions ]; then fsckoptions=`cat /fsckoptions`

...

fi

#

#

if [ -e "/proc/ide/${disk[$device]}/media" ] ; then hdmedia=`cat /proc/ide/${disk[$device]}/media`

...

fi

#

#

if [ ! -n "`uname -r | grep -- "-"`" ]; then ktag="`cat /proc/version`"

...

fi

#

#

if [ $usb = "1" ]; then

http://tldp.org/LDP/abs/html/commandsub.html (3 of 6) [7/15/2002 6:34:03 PM]

Command Substitution

sleep 5

mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"` kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"`

...

fi

Do not set a variable to the contents of a long text file unless you have a very good reason for doing so. Do not set a variable to the contents of a binary file, even as a joke.

Example 14-1. Stupid script tricks

#!/bin/bash

# stupid-script-tricks.sh: Don't try this at home, folks.

dangerous_variable=`cat /boot/vmlinuz` # The compressed Linux kernel itself.

echo "string-length of \$dangerous_variable = ${#dangerous_variable}"

#string-length of $dangerous_variable = 794151

#(Does not give same count as 'wc -c /boot/vmlinuz'.)

#echo "$dangerous_variable"

#Don't try this! It would hang the script.

#The document author is aware of no useful applications for #+ setting a variable to the contents of a binary file.

exit 0

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 this is grabbing the output of an echo command within the loop.

Example 14-2. Generating a variable from a loop

#!/bin/bash

# csubloop.sh: Setting a variable to the output of a loop.

variable1=`for i in 1 2 3 4 5

 

do

 

echo -n "$i"

# The 'echo' command is critical

done`

#+ to command substitution.

echo "variable1 = $variable1"

# variable1 = 12345

i=0

 

variable2=`while [ "$i" -lt 10

]

do

 

http://tldp.org/LDP/abs/html/commandsub.html (4 of 6) [7/15/2002 6:34:03 PM]

Command Substitution

echo -n "$i"

# Again, the necessary 'echo'.

let "i += 1"

# Increment.

done`

 

echo "variable2 = $variable2"

# variable2 = 0123456789

exit 0

 

 

 

Command substitution makes it possible to extend the toolset available to Bash. It is simply a matter of writing a program or script that outputs to stdout (like a well-behaved UNIX tool should) and assigning that output to a variable.

#include <stdio.h>

/* "Hello, world." C program */

int main()

{

printf( "Hello, world." ); return (0);

}

bash$ gcc -o hello hello.c

#!/bin/bash

# hello.sh

greeting=`./hello` echo $greeting

bash$ sh hello.sh

Hello, world.

The $(COMMAND) form has superseded backticks for command substitution.

output=$(sed -n /"$1"/p $file)

# From "grp.sh" example.

Examples of command substitution in shell scripts:

1.Example 10-7

2.Example 10-25

3.Example 9-23

4.Example 12-2

http://tldp.org/LDP/abs/html/commandsub.html (5 of 6) [7/15/2002 6:34:03 PM]

Command Substitution

5.Example 12-15

6.Example 12-12

7.Example 12-38

8.Example 10-13

9.Example 10-10

10.Example 12-24

11.Example 16-7

12.Example A-17

13.Example 28-1

14.Example 12-32

15.Example 12-33

16.Example 12-34

Notes

[1]For purposes of command substitution, a command may be an external system command, an internal scripting builtin, or even a script function.

Prev

Home

Next

System and Administrative Commands

Up

Arithmetic Expansion

http://tldp.org/LDP/abs/html/commandsub.html (6 of 6) [7/15/2002 6:34:03 PM]

Math Commands

 

Advanced Bash-Scripting Guide:

 

Prev

Chapter 12. External Filters, Programs and Commands

Next

12.8. Math Commands

"Doing the numbers"

factor

Decompose an integer into prime factors.

bash$ factor 27417

27417: 3 13 19 37

bc, dc

These are flexible, arbitrary precision calculation utilities.

bc has a syntax vaguely resembling C.

dc is stack-oriented and uses RPN ("Reverse Polish Notation").

Of the two, bc seems more useful in scripting. It is a fairly well-behaved UNIX utility, and may therefore be used in a pipe.

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

Here is a simple template for using bc to calculate a script variable. This uses command substitution.

variable=$(echo "OPTIONS; OPERATIONS" | bc)

Example 12-32. Monthly Payment on a Mortgage

http://tldp.org/LDP/abs/html/mathc.html (1 of 9) [7/15/2002 6:34:04 PM]

Math Commands

#!/bin/bash

#monthlypmt.sh: Calculates monthly payment on a mortgage.

#This is a modification of code in the "mcalc" (mortgage calculator) package,

#by Jeff Schmidt and Mendel Cooper (yours truly, the author of this document).

#http://www.ibiblio.org/pub/Linux/apps/financial/mcalc-1.6.tar.gz [15k]

echo

echo "Given the principal, interest rate, and term of a mortgage," echo "calculate the monthly payment."

bottom=1.0

echo

echo -n "Enter principal (no commas) " read principal

echo -n "Enter interest rate (percent) " # If 12%, enter "12", not ".12". read interest_r

echo -n "Enter term (months) " read term

interest_r=$(echo "scale=9; $interest_r/100.0" | bc) # Convert to decimal.

# "scale" determines how many decimal places.

interest_rate=$(echo "scale=9; $interest_r/12 + 1.0" | bc)

top=$(echo "scale=9; $principal*$interest_rate^$term" | bc)

echo; echo "Please be patient. This may take a while."

let "months = $term - 1"

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

for ((x=$months; x > 0; x--)) do

bot=$(echo "scale=9; $interest_rate^$x" | bc) bottom=$(echo "scale=9; $bottom+$bot" | bc)

#bottom = $(($bottom + $bot"))

done

#--------------------------------------------------------------------

#Rick Boivie pointed out a more efficient implementation

#+ of the above loop, which decreases computation time by 2/3.

#for ((x=1; x <= $months; x++))

#do

#bottom=$(echo "scale=9; $bottom * $interest_rate + 1" | bc)

#done

#And then he came up with an even more efficient alternative, #+ one that cuts down the run time by about 95%!

#bottom=`{

#echo "scale=9; bottom=$bottom; interest_rate=$interest_rate"

http://tldp.org/LDP/abs/html/mathc.html (2 of 9) [7/15/2002 6:34:04 PM]

Math Commands

#for ((x=1; x <= $months; x++))

#do

#echo 'bottom = bottom * interest_rate + 1'

#done

#echo 'bottom'

# } | bc` # Embeds a 'for loop' within command substitution.

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

# let "payment = $top/$bottom" payment=$(echo "scale=2; $top/$bottom" | bc)

# Use two decimal places for dollars and cents.

echo

echo "monthly payment = \$$payment" # Echo a dollar sign in front of amount. echo

exit 0

#Exercises:

#1) Filter input to permit commas in principal amount.

#2) Filter input to permit interest to be entered as percent or decimal.

#3) If you are really ambitious,

#expand this script to print complete amortization tables.

Example 12-33. Base Conversion

:

##########################################################################

# Shellscript:

base.sh - print number to different bases (Bourne Shell)

# Author

:

Heiner Steven (heiner.steven@odn.de)

# Date

:

07-03-95

# Category

:

Desktop

#$Id: base.sh,v 1.2 2000/02/06 19:55:35 heiner Exp $

##########################################################################

#Description

#

#Changes

#21-03-95 stv fixed error occuring with 0xb as input (0.2)

##########################################################################

#==> Used in this document with the script author's permission.

#==> Comments added by document author.

NOARGS=65

 

 

 

PN=`basename "$0"`

 

# Program name

VER=`echo

'$Revision: 1.2

$' | cut -d' ' -f2`

# ==> VER=1.2

Usage () {

echo "$PN - print number to different bases, $VER (stv '95) usage: $PN [number ...]

If no number is given, the numbers are read from standard input.

http://tldp.org/LDP/abs/html/mathc.html (3 of 9) [7/15/2002 6:34:04 PM]

Math Commands

A number may be

 

 

 

binary (base 2)

starting with 0b

(i.e. 0b1100)

octal (base

8)

starting with 0

(i.e. 014)

hexadecimal

(base 16)

starting with 0x

(i.e. 0xc)

decimal

 

otherwise (i.e. 12)" >&2

exit $NOARGS

 

 

 

}# ==> Function to print usage message.

Msg () {

 

 

 

for i

# ==> in [list] missing.

 

do echo "$PN: $i" >&2

 

 

done

 

 

 

}

 

 

 

Fatal () { Msg "$@"; exit 66; }

 

 

PrintBases () {

 

 

# Determine base of the number

 

for i

# ==> in [list] missing...

 

do

# ==> so operates on command line arg(s).

case "$i" in

 

 

 

0b*)

ibase=2;;

# binary

 

0x*|[a-f]*|[A-F]*) ibase=16;;

# hexadecimal

 

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, rather than "/".

#Convert number to decimal

dec=`echo "ibase=$ibase; $number" | bc`

# ==> 'bc' is calculator utility.

case

"$dec" in

 

 

 

 

[0-9]*)

;;

#

number ok

 

*)

continue;;

#

error: ignore

esac

 

 

 

 

#Print all conversions in one line.

#==> 'here document' feeds command list to 'bc'. echo `bc <<!

obase=16; "hex="; $dec obase=10; "dec="; $dec obase=8; "oct="; $dec obase=2; "bin="; $dec

!

` | sed -e 's: : :g'

done

}

while [ $# -gt 0 ] do

http://tldp.org/LDP/abs/html/mathc.html (4 of 9) [7/15/2002 6:34:04 PM]

Math Commands

case "$1" in

 

--)

shift; break;;

 

-h)

Usage;;

# ==> Help message.

-*)

Usage;;

 

*)

break;;

# first number

esac

# ==> More error checking for illegal input would be useful.

shift

 

 

done

 

 

if [ $# -gt 0 ]

 

then

 

 

PrintBases "$@"

 

else

 

# read from stdin

while read line

 

do

 

 

PrintBases $line

 

done

 

 

fi

 

 

 

 

 

An alternate method of invoking bc involves using a here document embedded within a command substitution block. This is especially appropriate when a script needs to pass a list of options and commands to bc.

variable=`bc << LIMIT_STRING options

statements operations LIMIT_STRING

`

...or...

variable=$(bc << LIMIT_STRING options

statements operations LIMIT_STRING

)

Example 12-34. Another way to invoke bc

http://tldp.org/LDP/abs/html/mathc.html (5 of 9) [7/15/2002 6:34:04 PM]

Math Commands

#!/bin/bash

# Invoking 'bc' using command substitution

# in combination with a 'here document'.

var1=`bc << EOF 18.33 * 19.78

EOF

 

`

 

echo $var1

# 362.56

# $( ... ) notation also works. v1=23.53

v2=17.881

v3=83.501

v4=171.63

var2=$(bc

<< EOF

 

scale = 4

 

 

a = ( $v1

+ $v2

)

b = ( $v3

* $v4

)

a * b + 15.35

 

EOF

 

 

)

 

 

echo $var2

 

# 593487.8452

var3=$(bc -l << EOF scale = 9

s ( 1.7 ) EOF

)

#Returns the sine of 1.7 radians.

#The "-l" option calls the 'bc' math library.

echo $var3

#

.991664810

# Now, try it in a

function...

hyp=

#

Declare global variable.

hypotenuse ()

# Calculate hypotenuse of a right triangle.

{

 

 

hyp=$(bc -l << EOF scale = 9

sqrt ( $1 * $1 + $2 * $2 ) EOF

)

# Unfortunately, can't return floating point values from a Bash function.

}

hypotenuse 3.68 7.31

echo "hypotenuse = $hyp" # 8.184039344

exit 0

http://tldp.org/LDP/abs/html/mathc.html (6 of 9) [7/15/2002 6:34:04 PM]

Math Commands

Most persons avoid dc, since it requires non-intuitive RPN input. Yet it has its uses.

Example 12-35. Converting a decimal number to hexadecimal

#!/bin/bash

# hexconvert.sh: Convert a decimal number to hexadecimal.

BASE=16 # Hexadecimal.

if [ -z "$1" ] then

echo "Usage: $0 number" exit $E_NOARGS

# Need a command line argument.

fi

# Exercise: add argument validity checking.

hexcvt ()

{

if [ -z "$1" ]

then

 

echo 0

 

return

# "Return" 0 if no arg passed to function.

fi

 

echo ""$1" "$BASE" o p" | dc

#

"o" sets radix (numerical base) of output.

#

"p" prints the top of stack.

# See 'man dc' for other options. return

}

hexcvt "$1"

exit 0

Studying the info page for dc gives some insight into its intricacies. However, there seems to be a small, select group of dc wizards who delight in showing off their mastery of this powerful, but arcane utility.

Example 12-36. Factoring

http://tldp.org/LDP/abs/html/mathc.html (7 of 9) [7/15/2002 6:34:04 PM]

Math Commands

#!/bin/bash

# factr.sh: Factor a number

MIN=2 # Will not work for number smaller than this.

E_NOARGS=65

E_TOOSMALL=66

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:"

# ---------------------------------------------------------------------------------

echo "$1[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2" | dc

# ---------------------------------------------------------------------------------

#Above line of code written by Michel Charpentier <charpov@cs.unh.edu>.

#Used with permission (thanks).

exit 0

awk

Yet another way of doing floating point math in a script is using awk's built-in math functions in a shell wrapper.

Example 12-37. Calculating the hypotenuse of a triangle

#!/bin/bash

# hypotenuse.sh: Returns the "hypotenuse" of a right triangle.

#

( square root of sum

of squares of

the "legs")

ARGS=2

# Script needs

sides of triangle passed.

E_BADARGS=65

# Wrong number

of arguments.

 

if [ $# -ne "$ARGS" ] # Test number of arguments to script. then

echo "Usage: `basename $0` side_1 side_2" exit $E_BADARGS

fi

 

 

AWKSCRIPT=' { printf( "%3.7f\n", sqrt($1*$1

+ $2*$2) ) } '

#

command(s) / parameters passed

to awk

echo -n "Hypotenuse of $1 and $2 = "

http://tldp.org/LDP/abs/html/mathc.html (8 of 9) [7/15/2002 6:34:04 PM]

Math Commands

echo $1 $2 | awk "$AWKSCRIPT"

exit 0

Prev

Home

Next

Terminal Control Commands

Up

Miscellaneous Commands

http://tldp.org/LDP/abs/html/mathc.html (9 of 9) [7/15/2002 6:34:04 PM]

Internal Variables

 

Advanced Bash-Scripting Guide:

 

Prev

Chapter 9. Variables Revisited

Next

9.1. Internal Variables

Builtin variables

variables affecting bash script behavior $BASH

the path to the Bash binary itself

bash$ echo $BASH

/bin/bash

$BASH_ENV

an environmental variable pointing to a Bash startup file to be read when a script is invoked

$BASH_VERSINFO[n]

a 6-element array containing version information about the installed release of Bash. This is similar to $BASH_VERSION, below, but a bit more detailed.

# Bash version info:

for n in 0 1 2 3 4 5 do

echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}" done

 

# BASH_VERSINFO[0] = 2

# Major version no.

 

# BASH_VERSINFO[1] = 05

# Minor version no.

 

# BASH_VERSINFO[2] = 8

# Patch level.

 

# BASH_VERSINFO[3] = 1

# Build version.

 

# BASH_VERSINFO[4] = release

# Release status.

 

# BASH_VERSINFO[5] = i386-redhat-linux-gnu

# Architecture

 

 

# (same as $MACHTYPE).

$BASH_VERSION

 

the version of Bash installed on the system

 

 

 

 

 

bash$ echo $BASH_VERSION

 

 

2.04.12(1)-release

 

 

 

 

http://tldp.org/LDP/abs/html/internalvariables.html (1 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

tcsh% echo $BASH_VERSION

BASH_VERSION: Undefined variable.

Checking $BASH_VERSION is a good method of determining which shell is running. $SHELL does not necessarily give the correct answer.

$DIRSTACK

the top value in the directory stack (affected by pushd and popd)

This builtin variable corresponds to the dirs command, however dirs shows the entire contents of the directory stack.

$EDITOR

the default editor invoked by a script, usually vi or emacs. $EUID

"effective" user id number

Identification number of whatever identity the current user has assumed, perhaps by means of su.

The $EUID is not necessarily the same as the $UID.

$FUNCNAME

name of the current function

xyz23 ()

{

echo "$FUNCNAME now executing." # xyz23 now executing.

}

 

 

 

xyz23

 

 

 

echo "FUNCNAME = $FUNCNAME"

#

FUNCNAME =

 

 

#

Null value

outside a function.

$GLOBIGNORE

A list of filename patterns to be excluded from matching in globbing.

$GROUPS

groups current user belongs to

This is a listing (array) of the group id numbers for current user, as recorded in /etc/passwd.

http://tldp.org/LDP/abs/html/internalvariables.html (2 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

root# echo $GROUPS

0

root# echo ${GROUPS[1]}

1

root# echo ${GROUPS[5]}

6

$HOME

home directory of the user, usually /home/username (see Example 9-12)

$HOSTNAME

The hostname command assigns the system name at bootup in an init script. However, the gethostname() function sets the Bash internal variable $HOSTNAME. See also Example 9-12.

$HOSTTYPE

host type

Like $MACHTYPE, identifies the system hardware.

bash$ echo $HOSTTYPE i686

$IFS

input field separator

This defaults to whitespace (space, tab, and newline), but may be changed, for example, to parse a comma-separated data file. Note that $* uses the first character held in $IFS. See Example 6-1.

bash$ echo $IFS | cat -vte

$

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

http://tldp.org/LDP/abs/html/internalvariables.html (3 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

$IFS does not handle whitespace the same as it does other characters.

Example 9-1. $IFS and whitespace

#!/bin/bash

# $IFS treats whitespace differently than other characters.

output_args_one_per_line()

{

for arg

do echo "[$arg]" done

}

echo; echo "IFS=\" \"" echo "-------"

IFS=" "

var=" a b c "

output_args_one_per_line $var # output_args_one_per_line `echo " a b c "`

#

#[a]

#[b]

#[c]

echo; echo "IFS=:" echo "-----"

IFS=:

var=":a::b:c:::" # Same as above, but substitute ":" for " ". output_args_one_per_line $var

#

#[]

#[a]

#[]

#[b]

#[c]

#[]

#[]

#[]

#The same thing happens with the "FS" field separator in awk.

#Thank you, Stephane Chazelas.

echo

exit 0

(Thanks, S. C., for clarification and examples.) $IGNOREEOF

ignore EOF: how many end-of-files (control-D) the shell will ignore before logging out. $LC_COLLATE

http://tldp.org/LDP/abs/html/internalvariables.html (4 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

Often set in the .bashrc or /etc/profile files, this variable controls collation order in filename expansion and pattern matching. If mishandled, LC_COLLATE can cause unexpected results in filename globbing.

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, set LC_COLLATE to C by an export LC_COLLATE=C in /etc/profile and/or ~/.bashrc.

$LC_CTYPE

This internal variable controls character interpretation in globbing and pattern matching.

$LINENO

This variable is the line number of the shell script in which this variable appears. It has significance only within the script in which it appears, and is chiefly useful for debugging purposes.

# *** BEGIN DEBUG BLOCK ***

last_cmd_arg=$_ # Save it.

echo "At line number $LINENO, variable \"v1\" = $v1" echo "Last command argument processed = $last_cmd_arg"

# *** END DEBUG BLOCK ***

$MACHTYPE

machine type

Identifies the system hardware.

bash$ echo $MACHTYPE i686-debian-linux-gnu

$OLDPWD

old working directory ("OLD-print-working-directory", previous directory you were in) $OSTYPE

operating system type

bash$ echo $OSTYPE linux-gnu

$PATH

path to binaries, usually /usr/bin/, /usr/X11R6/bin/, /usr/local/bin, etc.

When given a command, the shell automatically does a hash table search on the directories listed in the path for the executable. The path is stored in the environmental variable, $PATH, a list of directories, separated by colons. Normally, the system stores the $PATH definition in /etc/profile and/or ~/.bashrc (see Chapter 27).

http://tldp.org/LDP/abs/html/internalvariables.html (5 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

bash$ echo $PATH

/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin

PATH=${PATH}:/opt/bin appends the /opt/bin directory to the current path. In a script, it may be expedient to temporarily add a directory to the path in this way. When the script exits, this restores the original $PATH (a child process, such as a script, may not change the environment of the parent process, the shell).

The current "working directory", ./, is usually omitted from the $PATH as a security measure.

$PIPESTATUS

Exit status of last executed pipe. Interestingly enough, this does not give the same result as the exit status of the last executed command.

bash$ echo $PIPESTATUS

0

bash$ ls -al | bogus_command

bash: bogus_command: command not found bash$ echo $PIPESTATUS

141

bash$ ls -al | bogus_command

bash: bogus_command: command not found bash$ echo $?

127

$PPID

The $PPID of a process is the process id (pid) of its parent process. [1]

Compare this with the pidof command.

$PS1

This is the main prompt, seen at the command line.

$PS2

The secondary prompt, seen when additional input is expected. It displays as ">".

$PS3

The tertiary prompt, displayed in a select loop (see Example 10-28).

$PS4

The quartenary prompt, shown at the beginning of each line of output when invoking a script with the -x option. It displays as "+".

$PWD

working directory (directory you are in at the time)

This is the analog to the pwd builtin command.

http://tldp.org/LDP/abs/html/internalvariables.html (6 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

#!/bin/bash

E_WRONG_DIRECTORY=73

clear # Clear screen.

TargetDirectory=/home/bozo/projects/GreatAmericanNovel

cd $TargetDirectory

echo "Deleting stale files in $TargetDirectory."

if [ "$PWD" != "$TargetDirectory" ]

then

# Keep from wiping out wrong directory by accident.

echo

"Wrong directory!"

echo

"In $PWD, rather than $TargetDirectory!"

echo

"Bailing out!"

exit

$E_WRONG_DIRECTORY

fi

 

 

rm -rf

*

 

rm .[A-Za-z0-9]*

# Delete dotfiles.

# rm -f .[^.]* ..?*

to remove filenames beginning with multiple dots.

# (shopt -s dotglob; rm -f *) will also work.

#Thanks, S.C. for pointing this out.

#Filenames may contain all characters in the 0 - 255 range, except "/".

#Deleting files beginning with weird characters is left as an exercise.

#Various other operations here, as necessary.

echo

echo "Done."

echo "Old files deleted in $TargetDirectory." echo

exit 0

$REPLY

The default value when a variable is not supplied to read. Also applicable to select menus, but only supplies the item number of the variable chosen, not the value of the variable itself.

#!/bin/bash

echo

echo -n "What is your favorite vegetable? " read

echo "Your favorite vegetable is $REPLY."

#REPLY holds the value of last "read" if and only if

#no variable supplied.

echo

echo -n "What is your favorite fruit? " read fruit

http://tldp.org/LDP/abs/html/internalvariables.html (7 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

echo "Your favorite fruit is $fruit." echo "but..."

echo "Value of \$REPLY is still $REPLY."

#$REPLY is still set to its previous value because

#the variable $fruit absorbed the new "read" value.

echo

exit 0

$SECONDS

The number of seconds the script has been running.

#!/bin/bash

ENDLESS_LOOP=1

INTERVAL=1

echo

echo "Hit Control-C to exit this script." echo

while [ $ENDLESS_LOOP ] do

if [ "$SECONDS" -eq 1 ] then

units=second else

units=seconds

fi

echo "This script has been running $SECONDS $units." sleep $INTERVAL

done

exit 0

$SHELLOPTS

the list of enabled shell options, a readonly variable

bash$ echo $SHELLOPTS braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs

$SHLVL

Shell level, how deeply Bash is nested. If, at the command line, $SHLVL is 1, then in a script it will increment to 2. $TMOUT

http://tldp.org/LDP/abs/html/internalvariables.html (8 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

If the $TMOUT environmental variable is set to a non-zero value time, then the shell prompt will time out after time seconds. This will cause a logout.

Unfortunately, this works only while waiting for input at the shell prompt console or in an xterm. While it would be nice to speculate on the uses of this internal variable for timed input, for example in combination with read, $TMOUT does not work in that context and is virtually useless for shell scripting. (Reportedly the ksh version of a timed read does work.)

Implementing timed input in a script is certainly possible, but may require complex machinations. One method is to set up a timing loop to signal the script when it times out. This also requires a signal handling routine to trap (see Example 30-5) the interrupt generated by the timing loop (whew!).

Example 9-2. Timed Input

#!/bin/bash

# timed-input.sh

# TMOUT=3

useless in a script

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 needed TimerOn function running in background.

 

# $! is

PID of last job running in background.

fi

 

 

 

 

}

TimerOn()

{

sleep $TIMELIMIT && kill -s 14 $$ &

# Waits 3 seconds, then sends sigalarm to script.

}

Int14Vector()

{

answer="TIMEOUT" PrintAnswer exit 14

}

trap Int14Vector 14 # Timer interrupt (14) subverted for our purposes.

echo "What is your favorite vegetable " TimerOn

read answer PrintAnswer

http://tldp.org/LDP/abs/html/internalvariables.html (9 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

#Admittedly, this is a kludgy implementation of timed input, #+ however the "-t" option to "read" simplifies this task.

#See "t-out.sh", below.

#If you need something really elegant...

#+ consider writing the application in C or C++,

#+ using appropriate library functions, such as 'alarm' and 'setitimer'.

exit 0

An alternative is using stty.

Example 9-3. Once more, timed input

#!/bin/bash

# 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 "$old_tty_settings"

 

 

# See man page for "stty".

}

echo; echo -n "What's your name? Quick! " timedout_read $INTERVAL your_name

#This may not work on every terminal type.

#The maximum timeout depends on the terminal.

#(it is often 25.5 seconds).

echo

if [ ! -z "$your_name" ] # If name input before timeout...

then

echo "Your name is $your_name." else

echo "Timed out."

fi

echo

#The behavior of this script differs somewhat from "timed-input.sh".

#At each keystroke, the counter resets.

exit 0

http://tldp.org/LDP/abs/html/internalvariables.html (10 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

Perhaps the simplest method is using the -t option to read.

Example 9-4. Timed read

#!/bin/bash

# t-out.sh (per a suggestion by "syngin seven)

TIMELIMIT=4

# 4 seconds

read -t $TIMELIMIT variable <&1

echo

if [ -z "$variable" ] then

echo "Timed out, variable still unset." 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 subject to change from the command line or within a script, and is the counterpart to the id builtin.

Example 9-5. Am I root?

#!/bin/bash

# am-i-root.sh: Am I root or not?

ROOT_UID=0 # Root has $UID 0.

if [ "$UID" -eq "$ROOT_UID" ] # Will the real "root" please stand up? then

echo "You are root." else

echo "You are just an ordinary user (but mom loves you just the same)."

fi

exit 0

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

#Code below will not execute, because the script already exited.

#An alternate method of getting to the root of matters:

ROOTUSER_NAME=root

username=`id -nu` # Or... username=`whoami` if [ "$username" = "$ROOTUSER_NAME" ]

http://tldp.org/LDP/abs/html/internalvariables.html (11 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

then

echo "Rooty, toot, toot. You are root." else

echo "You are just a regular fella."

fi

See also Example 2-2.

The variables $ENV, $LOGNAME, $MAIL, $TERM, $USER, and $USERNAME are not Bash builtins. These are, however, often set as environmental variables in one of the Bash startup files. $SHELL, the name of the user's login shell, may be set from /etc/passwd or in an "init" script, and it is likewise not a Bash builtin.

tcsh% echo $LOGNAME bozo

tcsh% echo $SHELL

/bin/tcsh

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 5-5 and Example 11-11)

$#

number of command line arguments [2] or positional parameters (see Example 34-2)

$*

All of the positional parameters, seen as a single word

$@

Same as $*, but each parameter is a quoted string, that is, the parameters are passed on intact, without interpretation or expansion. This means, among other things, that each parameter in the argument list is seen as a separate word.

Example 9-6. arglist: Listing arguments with $* and $@

http://tldp.org/LDP/abs/html/internalvariables.html (12 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

#!/bin/bash

# Invoke this script with several arguments, such as "one two three".

E_BADARGS=65

if [ ! -n "$1" ] then

echo "Usage: `basename $0` argument1 argument2 etc." exit $E_BADARGS

fi

echo

index=1

echo "Listing args with \"\$*\":"

for arg in "$*"

# Doesn't work properly if "$*" isn't quoted.

do

 

echo "Arg #$index = $arg"

let "index+=1"

 

done

# $* sees all arguments as single word.

echo "Entire arg

list seen as single word."

echo

index=1

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

exit 0

Following a shift, the $@ holds the remaining command-line parameters, lacking the previous $1, which was lost.

#!/bin/bash

# Invoke with ./scriptname 1 2 3 4 5

echo "$@" # 1 2 3 4 5 shift

echo "$@" # 2 3 4 5 shift

echo "$@" # 3 4 5

#Each "shift" loses parameter $1.

#"$@" then contains the remaining parameters.

The $@ special parameter finds use as a tool for filtering input into shell scripts. The cat "$@" construction accepts input to a script either from stdin or from files given as parameters to the script. See Example 12-17 and Example 12-18.

http://tldp.org/LDP/abs/html/internalvariables.html (13 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

The $* and $@ parameters sometimes display inconsistent and puzzling behavior, depending on the setting of $IFS.

Example 9-7. Inconsistent $* and $@ behavior

#!/bin/bash

#Erratic behavior of the "$*" and "$@" internal Bash variables,

#depending on whether these are quoted or not.

#Word splitting and linefeeds handled inconsistently.

#This example script by Stephane Chazelas,

#and slightly modified by the document author.

set -- "First one" "second" "third:one" "" "Fifth: :one"

# Setting the script arguments, $1, $2, etc.

echo

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 unchanged, using "$@"' c=0

for i in "$@"

do echo "$((c+=1)): [$i]" done

echo ---

echo 'IFS unchanged, using $@' c=0

for i in $@

do echo "$((c+=1)): [$i]" done

echo ---

IFS=:

echo 'IFS=":", using "$*"' c=0

for i in "$*"

do echo "$((c+=1)): [$i]" done

echo ---

http://tldp.org/LDP/abs/html/internalvariables.html (14 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

echo 'IFS=":", using $*' c=0

for i in $*

do echo "$((c+=1)): [$i]" done

echo ---

var=$*

echo 'IFS=":", using "$var" (var=$*)' 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 ---

var="$*"

echo 'IFS=":", using $var (var="$*")' 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 i in $@

do echo "$((c+=1)): [$i]" done

echo ---

var=$@

echo 'IFS=":", using $var (var=$@)' c=0

for i in $var

do echo "$((c+=1)): [$i]" done

http://tldp.org/LDP/abs/html/internalvariables.html (15 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

echo ---

echo 'IFS=":", using "$var" (var=$@)' c=0

for i in "$var"

do echo "$((c+=1)): [$i]" done

echo ---

var="$@"

echo 'IFS=":", using "$var" (var="$@")' 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

# Try this script with ksh or zsh -y.

exit 0

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 echo positional params as expected.

mecho ()

#

Echo positional parameters.

{

 

 

echo "$1,$2,$3";

 

}

 

 

IFS=""

#

Set, but empty.

set a b c

#

Positional parameters.

mecho "$*"

#

abc,,

mecho $*

#

a,b,c

mecho $@

#

a,b,c

mecho "$@"

#

a,b,c

#The behavior of $* and $@ when $IFS is empty depends

#on whatever Bash or sh version being run.

#It is therefore inadvisable to depend on this "feature" in a script.

http://tldp.org/LDP/abs/html/internalvariables.html (16 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

# Thanks, S.C.

exit 0

Other Special Parameters

$-

Flags passed to script

This was originally a ksh construct adopted into Bash, and unfortunately it does not seem to work reliably in Bash scripts. One possible use for it is to have a script self-test whether it is interactive.

$!

PID (process id) of last job run in background

$_

Special variable set to last argument of previous command executed.

Example 9-9. underscore variable

#!/bin/bash

 

echo $_

# /bin/bash

 

# Just called /bin/bash to run the script.

du >/dev/null

# So no output from command.

echo $_

# du

ls -al >/dev/null

# So no output from command.

echo $_

# -al (last argument)

:

 

echo $_

# :

 

 

$?

 

exit status of a command, function, or the script itself (see Example 23-3)

$$

process id of the script itself, often used in scripts to construct "unique" temp file names (see Example A-13, Example 30-6, Example 12-23, and Example 11-20)

Notes

[1]The pid of the currently running script is $$, of course.

[2]The words "argument" and "parameter" are often used interchangeably. In the context of this document, they have the same precise meaning, that of a variable passed to a script or function.

Prev

Home

Next

http://tldp.org/LDP/abs/html/internalvariables.html (17 of 18) [7/15/2002 6:34:06 PM]

Internal Variables

Variables Revisited

Up

Manipulating Strings

http://tldp.org/LDP/abs/html/internalvariables.html (18 of 18) [7/15/2002 6:34:06 PM]

Special Characters

Advanced Bash-Scripting Guide:

Prev

Next

Chapter 4. Special Characters

Special Characters Found In Scripts and Elsewhere

#

Comments. Lines beginning with a # (with the exception of #!) are comments.

# This line is a comment.

Comments may also occur at the end of a command.

echo "A comment will follow." # Comment here.

Comments may also follow whitespace at the beginning of a line.

# A tab precedes this comment.

A command may not follow a comment on the same line. There is no method of terminating the comment, in order for "live code" to begin on the same line. Use a new line for the next command.

Of course, an escaped # in an echo statement does not begin a comment. Likewise, a # appears in certain parameter substitution constructs and in numerical constant expressions.

echo "The # here does not begin a comment." echo 'The # here does not begin a comment.' echo The \# here does not begin a comment. echo The # here begins a comment.

echo ${PATH#*:}

# Parameter substitution, not a comment.

echo $(( 2#101011 ))

# Base conversion, not a comment.

# Thanks, S.C.

 

The standard quoting and escape characters (" ' \) escape the #.

Certain pattern matching operations also use the #.

;

Command separator. [Semicolon] Permits putting two or more commands on the same line.

http://tldp.org/LDP/abs/html/special-chars.html (1 of 19) [7/15/2002 6:34:08 PM]

Special Characters

echo hello; echo there

Note that the ";" sometimes needs to be escaped.

;;

Terminator in a case option. [Double semicolon]

case "$variable" in

abc) echo "$variable = abc" ;; xyz) echo "$variable = xyz" ;; esac

.

"dot" command. [period] Equivalent to source (see Example 11-16). This is a bash builtin.

.

"dot", as a component of a filename. When working with filenames, a dot is the prefix of a "hidden" file, a file that an ls will not normally show.

bash$ touch .hidden-file

 

 

 

 

bash$ ls -l

 

 

 

 

 

 

 

total 10

 

 

 

 

 

 

 

-rw-r--r--

1

bozo

 

4034

Jul 18 22:04 data1.addressbook

-rw-r--r--

1

bozo

 

4602

May 25 13:58 data1.addressbook.bak

-rw-r--r--

1

bozo

 

877

Dec 17

2000

employment.addressbook

bash$ ls -al

 

 

 

 

 

 

 

total 14

 

 

 

 

 

 

 

drwxrwxr-x

2

bozo

bozo

 

1024

Aug 29

20:54 ./

drwx------

52

bozo

bozo

 

3072

Aug 29

20:51 ../

-rw-r--r--

1

bozo

bozo

 

4034

Jul 18

22:04 data1.addressbook

-rw-r--r--

1

bozo

bozo

 

4602

May 25

13:58 data1.addressbook.bak

-rw-r--r--

1

bozo

bozo

 

877

Dec 17

2000 employment.addressbook

-rw-rw-r--

1

bozo

bozo

 

0

Aug 29

20:54 .hidden-file

 

 

 

 

 

 

 

 

When considering directory names, a single dot represents the current working directory, and two dots denote the parent directory.

http://tldp.org/LDP/abs/html/special-chars.html (2 of 19) [7/15/2002 6:34:08 PM]

Special Characters

bash$ pwd /home/bozo/projects

bash$ cd . bash$ pwd

/home/bozo/projects

bash$ cd .. bash$ pwd /home/bozo/

The dot often appears as the destination (directory) of a file movement command.

bash$ cp /home/bozo/current_work/junk/* .

.

"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 interpretation) most of the special characters within STRING. See also Chapter 6.

'

full quoting. [single quote] 'STRING' preserves all special characters within STRING. This is a stronger form of quoting than using ". See also Chapter 6.

,

comma operator. The comma operator links together a series of arithmetic operations. All are evaluated, but only the last one is returned.

let "t2 = ((a = 9, 15 / 3))" # Set "a" and calculate "t2".

\

escape. [backslash] \X "escapes" the character X. This has the effect of "quoting" X, equivalent to 'X'. The \ may be used to quote " and ', so they are expressed literally.

See Chapter 6 for an in-depth explanation of escaped characters.

/

Filename path separator. [forward slash] Separates the components of a filename (as in

http://tldp.org/LDP/abs/html/special-chars.html (3 of 19) [7/15/2002 6:34:08 PM]

Special Characters

/home/bozo/projects/Makefile).

This is also the division arithmetic operator.

`

command substitution. [backticks] `command` makes available the output of command for setting a variable. This is also known as backticks or backquotes.

:

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 a itself a Bash builtin, and its exit status is "true" (0).

:

echo $? # 0

Endless loop:

while : do

operation-1 operation-2

...

operation-n done

#Same as:

#while true

#do

#...

#done

Placeholder in if/then test:

if condition

then : # Do nothing and branch ahead else

take-some-action

fi

Provide a placeholder where a binary operation is expected, see Example 8-2 and default parameters.

: ${username=`whoami`}

 

# ${username=`whoami`}

without the leading : gives an error

#

unless "username" is a command or builtin...

 

 

http://tldp.org/LDP/abs/html/special-chars.html (4 of 19) [7/15/2002 6:34:08 PM]

Special Characters

Provide a placeholder where a command is expected in a here document. See Example 17-9.

Evaluate string of variables using parameter substitution (as in Example 9-12).

: ${HOSTNAME?} ${USER?} ${MAIL?}

#Prints error message if one or more of essential environmental variables not set.

Variable expansion / substring replacement.

In combination with the > redirection operator, truncates a file to zero length, without changing its permissions. If the file did not previously exist, creates it.

: > data.xxx # File "data.xxx" now empty.

#

Same effect as

cat /dev/null >data.xxx

#

However, this does not fork a new process, since ":" is a builtin.

See also Example 12-11.

In combination with the >> redirection operator, updates a file access/modification time (: >> new_file). If the file did not previously exist, creates it. This is equivalent to touch.

This applies to regular files, not pipes, symlinks, and certain special files.

May be used to begin a comment line, although this is not recommended. Using # for a comment turns off error checking for the remainder of that line, so almost anything may be appear in a comment. However, this is not the case with :.

: This is a comment that generates an error, ( if [ $x -eq 3] ).

The ":" also serves as a field separator, in /etc/passwd, and in the $PATH variable.

bash$ echo $PATH

/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games

!

reverse (or negate) the sense of a test or exit status. The ! operator inverts the exit status of the command to which it is applied (see Example 3-2). It also inverts the meaning of a test operator. This can, for example, change the sense of "equal" ( = ) to "not-equal" ( != ). The ! operator is a Bash keyword.

In a different context, the ! also appears in indirect variable references.

In yet another context, from the command line, the ! invokes the Bash history mechanism (see Appendix F). Note that within a script, the history mechanism is disabled.

*

http://tldp.org/LDP/abs/html/special-chars.html (5 of 19) [7/15/2002 6:34:08 PM]

Special Characters

wild card. [asterisk] The * character serves as a "wild card" for filename expansion in globbing, as well as representing any number (or zero) characters in a regular expression.

*

arithmetic operator. In the context of arithmetic operations, the * denotes multiplication.

A double asterisk, **, is the exponentiation operator.

?

test operator. Within certain expressions, the ? indicates a test for a condition.

In a double parentheses construct, the ? serves as a C-style trinary operator. See Example 9-25.

In a parameter substitution expression, the ? tests whether a variable has been set.

?

wild card. The ? character serves as a single-character "wild card" for filename expansion in globbing, as well as representing one character in an extended regular expression.

$

Variable substitution.

var1=5

var2=23skidoo

echo

$var1

#

5

echo

$var2

#

23skidoo

A $ prefixing a variable name indicates the value the variable holds.

$

end-of-line. In a regular expression, a "$" addresses the end of a line of text.

${}

Parameter substitution.

$*, $@

positional parameters.

$?

exit status variable. The $? variable holds the exit status of a command, a function, or of the script itself.

http://tldp.org/LDP/abs/html/special-chars.html (6 of 19) [7/15/2002 6:34:08 PM]

Special Characters

$$

process id variable. The $$ variable holds the process id of the script in which it appears.

()

command group.

(a=hello; echo $a)

A listing of commands within parentheses starts a subshell.

Variables inside parentheses, within the subshell, are not visible to the rest of the script. The parent process, the script, cannot read variables created in the child process, the subshell.

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.

grep Linux file*.{txt,htm*}

#Finds all instances of the word "Linux"

#in the files "fileA.txt", "file2.txt", "fileR.html", "file-87.htm", etc.

A command may act upon a comma-separated list of file specs within braces. [1] Filename expansion (globbing) applies to the file specs between the braces.

No spaces allowed within the braces unless the spaces are quoted or escaped.

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{}

Block of code. [curly brackets] Also referred to as an "inline group", this construct, in effect, creates an anonymous function.

http://tldp.org/LDP/abs/html/special-chars.html (7 of 19) [7/15/2002 6:34:08 PM]

Special Characters

However, unlike a function, the variables in a code block remain visible to the remainder of the script.

bash$ { local a; a=123; }

bash: local: can only be used in a function

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 4-1. Code blocks and I/O redirection

#!/bin/bash

# Reading lines in /etc/fstab.

File=/etc/fstab

{

read line1 read line2 } < $File

echo "First line in $File is:" echo "$line1"

echo

echo "Second line in $File is:" echo "$line2"

exit 0

Example 4-2. Saving the results of a code block to a file

#!/bin/bash

#rpm-check.sh

#Queries an rpm file for description, listing, and whether it can be installed.

#Saves output to a file.

#

# This script illustrates using a code block.

SUCCESS=0

E_NOARGS=65

if [ -z "$1" ] then

echo "Usage: `basename $0` rpm-file" exit $E_NOARGS

http://tldp.org/LDP/abs/html/special-chars.html (8 of 19) [7/15/2002 6:34:08 PM]

Special Characters

fi

{

echo

echo "Archive Description:"

rpm -qpi $1 # Query description. echo

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

 

} > "$1.test"

# Redirects output of everything in block to file.

echo "Results of rpm test in file $1.test"

# See rpm man page for explanation of options.

exit 0

Unlike a command group within (parentheses), as above, a code block enclosed by {braces} will not normally launch a subshell. [2]

{} \;

pathname. Mostly used in find constructs. This is not a shell builtin.

The ";" ends the -exec option of a find command sequence. It needs to be escaped to protect it from interpretation by the shell.

[ ]

test.

Test expression between [ ]. Note that [ is part of the shell builtin test (and a synonym for it), not a link to the external command /usr/bin/test.

[[ ]]

test.

Test expression between [[ ]] (shell keyword).

See the discussion on the [[ ... ]] construct.

[ ]

array element.

http://tldp.org/LDP/abs/html/special-chars.html (9 of 19) [7/15/2002 6:34:08 PM]

Special Characters

In the context of an array, brackets set off the numbering of each element of that array.

Array[1]=slot_1 echo ${Array[1]}

[ ]

range of characters.

As part of a regular expression, brackets delineate a range of characters to match.

(( ))

integer expansion.

Expand and evaluate integer expression between (( )).

See the discussion on the (( ... )) construct.

> &> >& >> <

redirection.

scriptname >filename redirects the output of scriptname to file filename. Overwrite filename if it already exists.

command &>filename redirects both the stdout and the stderr of command to filename.

command >&2 redirects stdout of command to stderr.

scriptname >>filename appends the output of scriptname to file filename. If filename does not already exist, it will be created.

process substitution.

(command)>

<(command)

In a different context, the "<" and ">" characters act as string comparison operators.

In yet another context, the "<" and ">" characters act as integer comparison operators. See also Example 12-6.

<<

redirection used in a here document.

<, >

http://tldp.org/LDP/abs/html/special-chars.html (10 of 19) [7/15/2002 6:34:08 PM]

Special Characters

ASCII comparison.

veg1=carrots

veg2=tomatoes

if [[ "$veg1" < "$veg2" ]] then

echo "Although $veg1 precede $veg2 in the dictionary," echo "this implies nothing about my culinary preferences."

else

echo "What kind of dictionary are you using, anyhow?"

fi

\<, \>

word boundary in a regular expression.

bash$ grep '\<the\>' textfile

|

pipe. Passes the output of previous command to the input of the next one, or to the shell. This is a method of chaining commands together.

echo ls -l | sh

# Passes the output of "echo ls -l" to the shell, #+ with the same result as a simple "ls -l".

cat *.lst | sort | uniq

# Merges and sorts all ".lst" files, then deletes duplicate lines.

A pipe, as a classic method of interprocess communication, sends the stdout of one process to the stdin of another. In a typical case, a command, such as cat or echo, pipes a stream of data to a "filter" (a command that transforms its input) for processing.

cat $filename | grep $search_word

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 filename generation from single-letter filenames.

exit 0

http://tldp.org/LDP/abs/html/special-chars.html (11 of 19) [7/15/2002 6:34:08 PM]

Special Characters

Now, let us pipe the output of ls -lto this script.

bash$ ls -l | ./uppercase.sh

 

 

 

 

-RW-RW-R--

1 BOZO

BOZO

109 APR

7 19:49 1.TXT

-RW-RW-R--

1

BOZO

BOZO

109

APR

14

16:48 2.TXT

-RW-R--R--

1

BOZO

BOZO

725

APR

20

20:56 DATA-FILE

 

 

 

 

 

 

 

 

The stdout of each process in a pipe must be read as the stdin of the next. If this is not the case, the data stream will block, and the pipe will not behave as expected.

cat file1 file2 | ls -l | sort

# The output from "cat file1 file2" disappears.

A pipe runs as a child process, and therefore cannot alter script variables.

variable="initial_value"

echo "new_value" | read variable

echo "variable = $variable"

# variable = initial_value

If one of the commands in the pipe aborts, this prematurely terminates execution of the pipe. Called a broken pipe, this condition sends a SIGPIPE signal.

>|

force redirection (even if the noclobber option is set). This will forcibly overwrite an existing file.

||

OR logical operator. In a test construct, the || operator causes a return of 0 (success) if either of the linked test conditions is true.

&

Run job in background. A command followed by an & will run in the background.

bash$ sleep 10 &

 

[1] 850

 

[1]+ Done

sleep 10

Within a script, commands and even loops may run in the background.

Example 4-3. Running a loop in the background

http://tldp.org/LDP/abs/html/special-chars.html (12 of 19) [7/15/2002 6:34:08 PM]

Special Characters

#!/bin/bash

# background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10

# First loop.

do

 

echo -n "$i "

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 # Second loop. do

echo -n "$i " done

echo # This 'echo' sometimes will not display.

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

#The expected output from the script:

#1 2 3 4 5 6 7 8 9 10

#11 12 13 14 15 16 17 18 19 20

#Sometimes, though, you get:

#11 12 13 14 15 16 17 18 19 20

#1 2 3 4 5 6 7 8 9 10 bozo $

#(The second 'echo' doesn't execute. Why?)

#Occasionally also:

#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

#(The first 'echo' doesn't execute. Why?)

#Very rarely something like:

#11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20

#The foreground loop preempts the background one.

exit 0

A command run in the background within a script may cause the script to hang, waiting for a keystroke. Fortunately, there is a remedy for this.

&&

AND logical operator. In a test construct, the && operator causes a return of 0 (success) only if both the linked test conditions are true.

-

option, prefix. Option flag for a command or filter. Prefix for an operator.

COMMAND -[Option1][Option2][...]

ls -al

http://tldp.org/LDP/abs/html/special-chars.html (13 of 19) [7/15/2002 6:34:08 PM]

Special Characters

sort -dfu $filename

set -- $variable

if [ $file1 -ot $file2 ] then

echo "File $file1 is older than $file2."

fi

if [ "$a" -eq "$b" ] then

echo "$a is equal to $b."

fi

if [ "$c" -eq 24 -a "$d" -eq 47 ] then

echo "$c equals 24 and $d equals 47."

fi

-

redirection from/to stdin or stdout. [dash]

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

#Move entire file tree from one directory to another

#[courtesy Alan Cox <a.cox@swansea.ac.uk>, with a minor change]

# 1) cd /source/directory

Source directory, where the files to be moved are.

# 2) &&

"And-list": if the 'cd' operation successful, then

execute the next command.

 

# 3) tar cf - .

The 'c' option 'tar' archiving command creates a new

archive,

 

#

the 'f' (file) option, followed by '-' designates the

target file as stdout,

 

#

and do it in current directory tree ('.').

# 4) |

Piped to...

# 5) ( ... )

a subshell

# 6) cd /dest/directory

Change to the destination directory.

# 7) &&

"And-list", as above

# 8) tar xpvf -

Unarchive ('x'), preserve ownership and file permissions

('p'),

 

#

and send verbose messages to stdout ('v'),

#

reading data from stdin ('f' followed by '-').

#

 

#

Note that 'x' is a command, and 'p', 'v', 'f' are

options.

 

#Whew!

#More elegant than, but equivalent to:

#cd source-directory

#tar cf - . | (cd ../target-directory; tar xzf -)

http://tldp.org/LDP/abs/html/special-chars.html (14 of 19) [7/15/2002 6:34:08 PM]

Special Characters

# cp -a

/source/directory /dest

also has same effect.

 

 

 

bunzip2

linux-2.4.3.tar.bz2 | tar xvf

-

# --uncompress tar file--

| --then

pass it to "tar"--

#If "tar" has not been patched to handle "bunzip2",

#this needs to be done in two discrete steps, using a pipe.

#The purpose of the exercise is to unarchive "bzipped" kernel source.

Note that in this context the "-" is not itself a Bash operator, but rather an option recognized by certain UNIX utilities that write to stdout, such as tar, cat, etc.

bash$ echo "whatever" | cat - whatever

Where a filename is expected, - redirects output to stdout (sometimes seen with tar cf), or accepts input from stdin, rather than from a file. This is a method of using a file-oriented utility as a filter in a pipe.

bash$ file

Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...

By itself on the command line, filefails with an error message.

Add a "-" for a more useful result. This causes the shell to await user input.

bash$ file -

 

abc

 

standard input:

ASCII text

bash$ file -

 

#!/bin/bash

 

standard input:

Bourne-Again shell script text executable

 

 

Now the command accepts input from stdinand 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 compare a file with a section of another:

grep Linux file1 | diff file2 -

Finally, a real-world example using - with tar.

Example 4-4. Backup of all files changed in last day

http://tldp.org/LDP/abs/html/special-chars.html (15 of 19) [7/15/2002 6:34:08 PM]

Special Characters

#!/bin/bash

# Backs up all files in current directory modified within last 24 hours #+ in a "tarball" (tarred and gzipped file).

NOARGS=0

E_BADARGS=65

if [ $# = $NOARGS ] then

echo "Usage: `basename $0` filename" exit $E_BADARGS

fi

tar cvf - `find . -mtime -1 -type f -print` > $1.tar gzip $1.tar

#Stephane Chazelas points out that the above code will fail #+ if there are too many files found

#+ or if any filenames contain blank characters.

#He suggests the following alternatives:

# -------------------------------------------------------------

#find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$1.tar"

#using the GNU version of "find".

#find . -mtime -1 -type f -exec tar rvf "$1.tar" '{}' \;

#portable to other UNIX flavors, but much slower.

# -------------------------------------------------------------

exit 0

Filenames beginning with "-" may cause problems when coupled with the "-" redirection operator. A script should check for this and add an appropriate prefix to such filenames, for example ./-FILENAME, $PWD/-FILENAME, or $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. [dash] cd - changes to the previous working directory. This uses the $OLDPWD environmental variable.

Do not confuse the "-" used in this sense with the "-" redirection operator just discussed. The interpretation of the "-" depends on the context in which it appears.

http://tldp.org/LDP/abs/html/special-chars.html (16 of 19) [7/15/2002 6:34:08 PM]

Special Characters

-

Minus. Minus sign in an arithmetic operation.

=

Equals. Assignment operator

a=28

 

 

echo

$a

# 28

In a different context, the "=" is a string comparison operator.

+

Plus. Addition arithmetic operator.

In a different context, the + is a Regular Expression operator.

+

Option. Option flag for a command or filter.

Certain commands and builtins use the + to enable certain options and the - to disable them.

%

modulo. Modulo (remainder of a division) arithmetic operation.

In a different context, the % is a pattern matching operator.

~

home directory. [tilde] This corresponds to the $HOME internal variable. ~bozo is bozo's home directory, and ls ~bozo lists the contents of it. ~/ is the current user's home directory, and ls ~/ lists the contents of it.

bash$ echo ~bozo

/home/bozo

bash$ echo ~ /home/bozo

bash$ echo ~/

/home/bozo/

bash$ echo ~:

/home/bozo:

bash$ echo ~nonexistent-user

~nonexistent-user

http://tldp.org/LDP/abs/html/special-chars.html (17 of 19) [7/15/2002 6:34:08 PM]

Special Characters

~+

current working directory. This corresponds to the $PWD internal variable.

~-

previous working directory. This corresponds to the $OLDPWD internal variable.

^

beginning-of-line. In a regular expression, a "^" addresses the beginning of a line of text.

Control Characters

change the behavior of the terminal or text display. A control character is a CONTROL + key combination.

Ctl-C

Terminate a foreground job.

Ctl-D

Log out from a shell (similar to exit).

"EOF" (end of file). This also terminates input from stdin.

Ctl-G

"BEL" (beep).

Ctl-H

Backspace.

#!/bin/bash

# Embedding Ctl-H in a string.

a="^H^H"

# Two Ctl-H's (backspaces).

echo "abcdef"

# abcdef

echo -n "abcdef$a "

# abcd f

#

Space at end ^

^ Backspaces twice.

echo -n "abcdef$a"

# abcdef

#

No space at end

Doesn't backspace (why?).

# Results may not be quite as expected.

echo; echo

Ctl-J

Carriage return.

Ctl-L

http://tldp.org/LDP/abs/html/special-chars.html (18 of 19) [7/15/2002 6:34:08 PM]

Special Characters

Formfeed (clear the terminal screen). This has the same effect as the clear command.

Ctl-M

Newline.

Ctl-U

Erase a line of input.

Ctl-Z

Pause a foreground job.

Whitespace

functions as a separator, separating commands or variables. Whitespace consists of either spaces, tabs, blank lines, or any combination thereof. In some contexts, such as variable assignment, whitespace is not permitted, and results in a syntax error.

Blank lines have no effect on the action of a script, and are therefore useful for visually separating functional sections.

$IFS, the special variable separating fields of input to certain commands, defaults to whitespace.

Notes

[1]The shell does the brace expansion. The command itself acts upon the result of the expansion.

[2]Exception: a code block in braces as part of a pipe may be run as a subshell.

ls | { read firstline; read secondline; }

#Error. The code block in braces runs as a subshell,

#so the output of "ls" cannot be passed to variables within the block.

echo "First line is $firstline; second line is $secondline" # Will not work.

# Thanks, S.C.

Prev

Home

Next

Exit and Exit Status

Up

Introduction to Variables and Parameters

http://tldp.org/LDP/abs/html/special-chars.html (19 of 19) [7/15/2002 6:34:08 PM]

Arrays

Advanced Bash-Scripting Guide:

Prev

Next

Chapter 26. 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 (find the contents of) an array element, use curly bracket notation, that is, ${variable[xx]}.

Example 26-1. Simple array usage

#!/bin/bash

area[11]=23

area[13]=37

area[51]=UFOs

#Array members need not be consecutive or contiguous.

#Some members of the array can be left uninitialized.

#Gaps in the array are o.k.

echo -n "area[11]

=

"

echo ${area[11]}

 

# {curly brackets} needed

echo -n "area[13]

=

"

echo

${area[13]}

 

 

echo

"Contents of

area[51] are ${area[51]}."

# Contents of uninitialized array variable print blank. echo -n "area[43] = "

echo ${area[43]}

echo "(area[43] unassigned)"

echo

# Sum of two array variables assigned to third area[5]=`expr ${area[11]} + ${area[13]}`

echo "area[5] = area[11] + area[13]" echo -n "area[5] = "

echo ${area[5]}

area[6]=`expr ${area[11]} + ${area[51]}` echo "area[6] = area[11] + area[51]" echo -n "area[6] = "

echo ${area[6]}

# This fails because adding an integer to a string is not permitted.

echo; echo; echo

http://tldp.org/LDP/abs/html/arrays.html (1 of 15) [7/15/2002 6:34:10 PM]

Arrays

# -----------------------------------------------------------------

# Another array, "area2".

# 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 second element of array.

# -----------------------------------------------------------------

 

echo; echo; echo

 

# -----------------------------------------------

# Yet another array, "area3".

# Yet another way of assigning array variables...

# array_name=([xx]=XXX [yy]=YYY ...)

area3=([17]=seventeen [24]=twenty-four)

echo -n "area3[17] = " echo ${area3[17]}

echo -n "area3[24] = " echo ${area3[24]}

# -----------------------------------------------

exit 0

Array variables have a syntax all their own, and even standard Bash commands and operators have special options adapted for array use.

array=( zero one two three four five )

echo ${array[0]}

#

zero

 

echo ${array:0}

#

zero

 

 

#

Parameter expansion of first

element.

echo ${array:1}

#

ero

 

 

#

Parameter expansion of first

element,

 

#+

starting at position #1 (2nd

character).

echo ${#array}

#

4

 

 

#

Length of first element of array.

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

Example 26-2. Some special properties of arrays

http://tldp.org/LDP/abs/html/arrays.html (2 of 15) [7/15/2002 6:34:10 PM]

Arrays

#!/bin/bash

declare -a colors

# Permits declaring an array without specifying its size.

echo "Enter your favorite colors (separated from each other by a space)."

read -a colors

# Enter at least 3 colors to demonstrate features below.

#Special option to 'read' command,

#+ allowing assignment of elements in an array.

echo

element_count=${#colors[@]}

#Special syntax to extract number of elements in array.

#element_count=${#colors[*]} works also.

#

# The "@" variable allows word splitting within quotes #+ (extracts variables separated by whitespace).

index=0

while [ "$index" -lt "$element_count" ]

do

# List

all the elements in the array.

 

echo ${colors[$index]}

 

 

let "index

= $index + 1"

 

done

 

 

# Each array

element listed on

a separate line.

# If this is

not desired, use

echo -n "${colors[$index]} "

#

 

 

 

#Doing it with a "for" loop instead:

#for i in "${colors[@]}"

#do

#echo "$i"

#done

#(Thanks, S.C.)

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 colors[1]=

echo ${colors[@]}

# List array again, missing 2nd element.

unset colors

# Delete entire array.

 

# unset colors[*] and

 

#+ unset colors[@] also work.

echo; echo -n "Colors gone."

 

echo ${colors[@]}

# List array again, now empty.

http://tldp.org/LDP/abs/html/arrays.html (3 of 15) [7/15/2002 6:34:10 PM]

Arrays

exit 0

As seen in the previous example, either ${array_name[@]} or ${array_name[*]} refers to all the elements of the array. Similarly, to get a count of the number of elements in an array, use either ${#array_name[@]} or ${#array_name[*]}. ${#array_name} is the length (number of characters) of ${array_name[0]}, the first element of the array.

Example 26-3. Of empty arrays and empty elements

#!/bin/bash

#empty-array.sh

#An empty array is not the same as an array with empty elements.

array0=( first

second third )

array1=( '' )

#

"array1" has one empty element.

array2=( )

#

No elements... "array2" is empty.

echo

 

 

 

echo "Elements

in

array0:

${array0[@]}"

echo "Elements

in

array1:

${array1[@]}"

echo "Elements

in

array2:

${array2[@]}"

echo

 

 

 

echo "Length of first element in array0 = ${#array0}"

echo "Length of first element in array1 = ${#array1}"

echo "Length of first element in array2 = ${#array2}"

echo

 

 

 

echo "Number of elements in array0 = ${#array0[*]}"

# 3

 

echo "Number of elements in array1 = ${#array1[*]}"

#

1

(surprise!)

echo "Number of elements in array2 = ${#array2[*]}"

#

0

 

echo

exit 0 # Thanks, S.C.

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[@]}"

#Adding an element to an array. array=( "${array[@]}" "new element" )

#or

array[${#array[*]}]="new element"

# Thanks, S.C.

http://tldp.org/LDP/abs/html/arrays.html (4 of 15) [7/15/2002 6:34:10 PM]

Arrays

The array=( element1 element2 ... elementN ) initialization operation, with the help of command substitution, makes it possible to load the contents of a text file into an array.

#!/bin/bash

filename=sample_file

#

cat sample_file

#

 

 

#

1

a b c

#

2

d e fg

declare -a array1

array1=( `cat "$filename" | tr '\n' ' '`) # Loads contents

# of $filename into array1.

#list file to stdout.

#

change linefeeds in file to spaces.

echo ${array1[@]}

# List the array.

#

1 a b c 2 d e fg

#

 

# Each whitespace-separated "word" in the file #+ has been assigned to an element of the array.

element_count=${#array1[*]}

 

echo $element_count

# 8

Arrays permit deploying old familiar algorithms as shell scripts. Whether this is necessarily a good idea is left to the reader to decide.

Example 26-4. An old friend: The Bubble Sort

#!/bin/bash

#bubble.sh: Bubble sort, of sorts.

#Recall the algorithm for a bubble sort. In this particular version...

#With each successive pass through the array to be sorted,

#+ compare two adjacent elements, and swap them if out of order.

#At the end of the first pass, the "heaviest" element has sunk to bottom.

#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()

{

# Swaps two members of the array.

http://tldp.org/LDP/abs/html/arrays.html (5 of 15) [7/15/2002 6:34:10 PM]

Arrays

local temp=${Countries[$1]} # Temporary storage

#+ for element getting swapped out. Countries[$1]=${Countries[$2]}

Countries[$2]=$temp

return

}

declare -a Countries # Declare array,

#+ optional here since it's initialized below.

#Is it permissable to split an array variable over multiple lines #+ using an escape (\)?

#Yes.

Countries=(Netherlands Ukraine Zaire Turkey Russia Yemen Syria \ Brazil Argentina Nicaragua Japan Mexico Venezuela Greece England \ Israel Peru Canada Oman Denmark Wales France Kenya \

Xanadu Qatar Liechtenstein Hungary)

# "Xanadu" is the mythical place where, according to Coleridge, #+ Kubla Khan did a pleasure dome decree.

clear

# Clear the screen to start with.

echo "0: ${Countries[*]}" # List entire array at pass 0.

number_of_elements=${#Countries[@]}

let "comparisons = $number_of_elements - 1"

count=1 # Pass number.

 

while [ "$comparisons" -gt 0 ]

# Beginning of outer loop

do

 

index=0 # Reset index to start of array after each pass.

while [ "$index" -lt "$comparisons" ] # Beginning of inner loop do

if [ ${Countries[$index]} \> ${Countries[`expr $index + 1`]} ]

#If out of order...

#Recalling that \> is ASCII comparison operator #+ within single brackets.

#if [[ ${Countries[$index]} > ${Countries[`expr $index + 1`]} ]] #+ also works.

then

exchange $index `expr $index + 1` # Swap.

fi

let "index += 1"

done # End of inner loop

let "comparisons -= 1" # Since "heaviest" element bubbles to bottom, #+ we need do one less comparison each pass.

http://tldp.org/LDP/abs/html/arrays.html (6 of 15) [7/15/2002 6:34:10 PM]

Arrays

echo

echo "$count: ${Countries[@]}" # Print resultant array at end of each pass.

echo

 

 

 

let "count += 1"

# Increment

pass count.

done

#

End of outer loop

 

#

All done.

 

exit 0

--

Arrays enable implementing a shell script version of the Sieve of Eratosthenes. Of course, a resource-intensive application of this nature should really be written in a compiled language, such as C. It runs excruciatingly slowly as a script.

Example 26-5. Complex array application: Sieve of Eratosthenes

#!/bin/bash

#sieve.sh

#Sieve of Eratosthenes

#Ancient algorithm for finding prime numbers.

#This runs a couple of orders of magnitude

#slower than the equivalent C program.

LOWER_LIMIT=1

# Starting

with 1.

UPPER_LIMIT=1000

# Up to 1000.

 

# (You may set this

higher...

if

you have time on your hands.)

PRIME=1

NON_PRIME=0

let SPLIT=UPPER_LIMIT/2

#Optimization:

#Need to test numbers only halfway to upper limit.

declare -a Primes

# Primes[] is an array.

initialize ()

{

# Initialize the array.

i=$LOWER_LIMIT

until [ "$i" -gt "$UPPER_LIMIT" ] do

Primes[i]=$PRIME let "i += 1"

done

# Assume all array members guilty (prime)

http://tldp.org/LDP/abs/html/arrays.html (7 of 15) [7/15/2002 6:34:10 PM]

Arrays

# until proven innocent.

}

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 () # Sift out the non-primes.

{

let i=$LOWER_LIMIT+1

# We know 1 is prime, so let's start with 2.

until [ "$i" -gt "$UPPER_LIMIT" ] do

if [ "${Primes[i]}" -eq "$PRIME" ]

# Don't bother sieving numbers already sieved (tagged as non-prime). then

t=$i

while [ "$t" -le "$UPPER_LIMIT" ] do

let "t += $i " Primes[t]=$NON_PRIME

# Tag as non-prime all multiples. done

fi

let "i += 1" done

}

# Invoke the functions sequentially.

http://tldp.org/LDP/abs/html/arrays.html (8 of 15) [7/15/2002 6:34:10 PM]

Arrays

initialize sift print_primes

#This is what they call structured programming.

echo

exit 0

#----------------------------------------------- #

#Code below line will not execute.

#This improved version of the Sieve, by Stephane Chazelas,

#executes somewhat faster.

#Must invoke with command-line argument (limit of primes).

UPPER_LIMIT=$1

#

From command line.

let SPLIT=UPPER_LIMIT/2

#

Halfway to max number.

Primes=( '' $(seq $UPPER_LIMIT) )

i=1

until (( ( i += 1 ) > SPLIT )) # Need check only halfway. do

if [[ -n $Primes[i] ]] then

t=$i

until (( ( t += i ) > UPPER_LIMIT )) do

Primes[t]= done

fi done

echo ${Primes[*]}

exit 0

Compare this array-based prime number generator with an alternative that does not use arrays, Example A-16.

--

Arrays lend themselves, to some extent, to emulating data structures for which Bash has no native support.

Example 26-6. Emulating a push-down stack

http://tldp.org/LDP/abs/html/arrays.html (9 of 15) [7/15/2002 6:34:10 PM]

Arrays

#!/bin/bash

#stack.sh: push-down stack simulation

#Similar to the CPU stack, a push-down stack stores data items

#+ sequentially, but releases them in reverse order, last-in first-out.

BP=100

# Base Pointer of stack array.

 

# Begin at element 100.

SP=$BP

# Stack Pointer.

 

# Initialize it to "base" (bottom) of stack.

Data=

# Contents of stack location.

 

# Must use local variable,

 

#+ because of limitation on function return range.

declare -a stack

 

push()

# Push item on stack.

{

 

if [ -z "$1" ]

# Nothing to push?

then

 

return

 

fi

 

let "SP -= 1"

# Bump stack pointer.

stack[$SP]=$1

 

return

 

}

 

pop()

# Pop item off stack.

{

 

Data=

# Empty out data item.

if [ "$SP" -eq "$BP" ] # Stack empty?

then

 

return

 

fi

# This also keeps SP from getting past 100,

 

#+ i.e., prevents a runaway stack.

Data=${stack[$SP]}

 

let "SP += 1"

# Bump stack pointer.

return

 

}

 

status_report()

# Find out what's happening.

{

 

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

"

echo "REPORT"

 

echo "Stack Pointer = $SP"

echo "Just popped \""$Data"\" off the stack."

echo

"-------------------------------------

"

echo

 

 

http://tldp.org/LDP/abs/html/arrays.html (10 of 15) [7/15/2002 6:34:10 PM]

Arrays

}

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

#Now, for some fun.

echo

# See if you can pop anything off empty stack. pop

status_report

echo

 

push garbage

 

pop

 

status_report

# Garbage in, garbage out.

value1=23; push $value1 value2=skidoo; push $value2 value3=FINAL; push $value3

pop

# FINAL

status_report

 

pop

# skidoo

status_report

 

pop

# 23

status_report

# Last-in, first-out!

# Notice how the stack pointer decrements with each push, #+ and increments with each pop.

echo

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

#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 element from the stack with a single function call.

# 3) Using this script as a jumping-off point,

# + write a stack-based 4-function calculator.

exit 0

--

Fancy manipulation of array "subscripts" may require intermediate variables. For projects involving this, again consider using a more powerful programming language, such as Perl or C.

http://tldp.org/LDP/abs/html/arrays.html (11 of 15) [7/15/2002 6:34:10 PM]

Arrays

Example 26-7. Complex array application: Exploring a weird mathematical series

#!/bin/bash

#Douglas Hofstadter's notorious "Q-series":

#Q(1) = Q(2) = 1

#Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), for n>2

#This is a "chaotic" integer series with strange and unpredictable behavior.

#The first 20 terms of the series are:

#1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12

#See Hofstadter's book, "Goedel, Escher, Bach: An Eternal Golden Braid",

#p. 137, ff.

LIMIT=100

# Number of terms to calculate

LINEWIDTH=20

# Number of terms printed per line

Q[1]=1

# First two terms of series are 1.

Q[2]=1

 

 

echo

 

 

 

echo

"Q-series [$LIMIT terms]:"

 

echo

-n "${Q[1]} "

# Output first two terms.

echo

-n "${Q[2]} "

 

for ((n=3; n <= $LIMIT; n++))

# C-like loop conditions.

do

# Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]] for n>2

#Need to break the expression into intermediate terms,

#since Bash doesn't handle complex array arithmetic very well.

let "n1 = $n

- 1"

# n-1

let "n2 = $n

- 2"

# n-2

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

# n - Q[n-1]

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

# n - Q[n-2]

T0=${Q[t0]}

 

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

T1=${Q[t1]}

 

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

Q[n]=`expr $T0

+ $T1`

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

echo -n "${Q[n]} "

 

if [

`expr $n % $LINEWIDTH` -eq 0 ]

# Format output.

then

#

mod

 

echo # Break lines into neat chunks.

fi

done

echo

exit 0

http://tldp.org/LDP/abs/html/arrays.html (12 of 15) [7/15/2002 6:34:10 PM]

Arrays

#This is an iterative implementation of the Q-series.

#The more intuitive recursive implementation is left as an exercise.

#Warning: calculating this series recursively takes a *very* long time.

--

Bash supports only one-dimensional arrays, however a little trickery permits simulating multi-dimensional ones.

Example 26-8. Simulating a two-dimensional array, then tilting it

#!/bin/bash

#Simulating a two-dimensional array.

#A two-dimensional array stores rows sequentially.

Rows=5

Columns=5

declare -a alpha

# char alpha [Rows] [Columns];

 

# Unnecessary declaration.

load_alpha ()

 

{

 

local rc=0

 

local index

 

for i in 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 do

local row=`expr $rc / $Columns` local column=`expr $rc % $Rows`

let "index = $row * $Rows + $column" 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

 

 

while [ "$row" -lt "$Rows" ]

# Print

out in "row major" order -

do

# columns vary

 

# while

row (outer loop) remains the same.

local column=0

 

 

http://tldp.org/LDP/abs/html/arrays.html (13 of 15) [7/15/2002 6:34:10 PM]

Arrays

while [ "$column" -lt "$Columns" ] do

let "index = $row * $Rows + $column"

echo -n "${alpha[index]} " # alpha[$row][$column] let "column += 1"

done

let "row += 1" echo

done

#The simpler equivalent is

#echo ${alpha[*]} | xargs -n $Columns

echo

}

filter ()

# Filter out negative array indices.

{

 

echo -n "

" # Provides the tilt.

if [[ "$1" -ge 0 && "$1" -lt "$Rows" && "$2" -ge 0 && "$2" -lt "$Columns" ]] then

let "index = $1 * $Rows + $2"

# Now, print it rotated.

echo -n " ${alpha[index]}" # alpha[$row][$column]

fi

}

rotate ()

#

Rotate the array 45 degrees

{

#

("balance" it on its lower lefthand corner).

local row

 

 

local column

for (( row = Rows; row > -Rows; row-- )) # Step through the array backwards. do

for (( column = 0; column < Columns; column++ )) do

if [ "$row" -ge 0 ] then

let "t1 = $column - $row" let "t2 = $column"

else

let "t1 = $column"

let "t2 = $column + $row"

fi

filter $t1 $t2 # Filter out negative array indices.

http://tldp.org/LDP/abs/html/arrays.html (14 of 15) [7/15/2002 6:34:10 PM]

Arrays

done

echo; echo

done

#Array rotation inspired by examples (pp. 143-146) in

#"Advanced C Programming on the IBM PC", by Herbert Mayer

#(see bibliography).

}

 

#-----------------------------------------------------

#

load_alpha

# Load the array.

print_alpha

# Print it out.

rotate

# Rotate it 45 degrees counterclockwise.

#-----------------------------------------------------

#

#This is a rather contrived, not to mention kludgy simulation.

#Exercises:

# ---------

 

# 1)

Rewrite the array loading and printing functions

# + in a more intuitive and elegant fashion.

#

 

# 2)

Figure out how the array rotation functions work.

#

Hint: think about the implications of backwards-indexing an array.

exit 0

A two-dimensional array is essentially equivalent to a one-dimensional one, but with additional addressing modes for referencing and manipulating the individual elements by "row" and "column" position.

For an even more elaborate example of simulating a two-dimensional array, see Example A-10.

Prev

Home

Next

List Constructs

Up

Files

http://tldp.org/LDP/abs/html/arrays.html (15 of 15) [7/15/2002 6:34:10 PM]