
- •Advanced Bash-Scripting Guide
- •Dedication
- •Table of Contents
- •Part 1. Introduction
- •Advanced Bash-Scripting Guide
- •Chapter 2. Starting Off With a Sha-Bang
- •2.1. Invoking the script
- •2.2. Preliminary Exercises
- •Part 2. Basics
- •Chapter 3. Exit and Exit Status
- •Chapter 4. Special Characters
- •Chapter 5. Introduction to Variables and Parameters
- •5.1. Variable Substitution
- •5.2. Variable Assignment
- •5.3. Bash Variables Are Untyped
- •5.4. Special Variable Types
- •Chapter 6. Quoting
- •Chapter 7. Tests
- •7.1. Test Constructs
- •7.2. File test operators
- •7.3. Comparison operators (binary)
- •7.4. Nested if/then Condition Tests
- •7.5. Testing Your Knowledge of Tests
- •8.1. Operators
- •8.2. Numerical Constants
- •Part 3. Beyond the Basics
- •Chapter 9. Variables Revisited
- •9.1. Internal Variables
- •9.2. Manipulating Strings
- •9.3. Parameter Substitution
- •9.4. Typing variables: declare or typeset
- •9.5. Indirect References to Variables
- •9.6. $RANDOM: generate random integer
- •9.7. The Double Parentheses Construct
- •Chapter 10. Loops and Branches
- •10.1. Loops
- •10.2. Nested Loops
- •10.3. Loop Control
- •10.4. Testing and Branching
- •Chapter 11. Internal Commands and Builtins
- •12.1. Basic Commands
- •12.2. Complex Commands
- •12.3. Time / Date Commands
- •12.4. Text Processing Commands
- •12.5. File and Archiving Commands
- •12.6. Communications Commands
- •12.7. Terminal Control Commands
- •12.8. Math Commands
- •12.9. Miscellaneous Commands
- •Chapter 13. System and Administrative Commands
- •Chapter 14. Command Substitution
- •Chapter 15. Arithmetic Expansion
- •Chapter 16. I/O Redirection
- •16.1. Using exec
- •16.2. Redirecting Code Blocks
- •16.3. Applications
- •Chapter 17. Here Documents
- •Chapter 18. Recess Time
- •Part 4. Advanced Topics
- •Chapter 19. Regular Expressions
- •19.1. A Brief Introduction to Regular Expressions
- •19.2. Globbing
- •Chapter 20. Subshells
- •Chapter 21. Restricted Shells
- •Chapter 22. Process Substitution
- •Chapter 23. Functions
- •23.1. Complex Functions and Function Complexities
- •23.2. Local Variables
- •Chapter 24. Aliases
- •Chapter 25. List Constructs
- •Chapter 26. Arrays
- •Chapter 27. Files
- •Chapter 28. /dev and /proc
- •28.2. /proc
- •Chapter 29. Of Zeros and Nulls
- •Chapter 30. Debugging
- •Chapter 31. Options
- •Chapter 32. Gotchas
- •Chapter 33. Scripting With Style
- •Chapter 34. Miscellany
- •34.2. Shell Wrappers
- •34.3. Tests and Comparisons: Alternatives
- •34.4. Optimizations
- •34.5. Assorted Tips
- •34.6. Oddities
- •34.7. Security Issues
- •34.8. Portability Issues
- •34.9. Shell Scripting Under Windows
- •Chapter 35. Bash, version 2
- •36. Endnotes
- •36.1. Author's Note
- •36.2. About the Author
- •36.3. Tools Used to Produce This Book
- •36.4. Credits
- •List of Tables
- •List of Examples
- •Bibliography

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]

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