I In nt tr ro od du uc ct ti io on n t to o S Sc cr ri ip pt ti in ng g

vroomhuhΛογισμικό & κατασκευή λογ/κού

4 Νοε 2013 (πριν από 3 χρόνια και 11 μήνες)

74 εμφανίσεις

OCB 4/16/2000





PART I: REVIEW AND INTRODUCTORY CONCEPTS

Introduction: Unix Shells
By now, you should know that there are many different shells available for the Unix operating system.
The most common are the bourne shell (sh), and C shell (csh). You have seen already that these shells provide
a command line interface between the user and the system. In this workshop, we will see that UNIX shells also
have a sort of programming language associated with them, which allow us to quickly write very powerful
programs called shell scripts. Unfortunately, each shell (sh, csh, etc…) has a slightly different language syntax,
Figure 1: Structure of Unix operating system.
Update: 3/13/2002
Author: BBR

I
I
n
n
t
t
r
r
o
o
d
d
u
u
c
c
t
t
i
i
o
o
n
n


t
t
o
o


S
S
c
c
r
r
i
i
p
p
t
t
i
i
n
n
g
g

( Unix Part 3of 3)
ECST Computer Link
College of Engineering, Computer
Science, and Technology.
California State University, Los Angeles
CPU
Kernel
Interpreter

(eg. shell)

Interpreted Language
Script
(bourne shell script)
Compiler

(eg. gcc)

Compiled
Language Code
eg( C, C++, etc..)

Binary
Program


OCB 4/16/2000
2
so scripts written in one shell will not work in another. We will be doing all of our scripting in the sh (bourne)
shell syntax, since almost all UNIX systems have this shell installed.
1

Unix Concepts
Programs vs. Scripts
Before we look at scripting for the bourne shell, it is important that you understand a number of
concepts about how the UNIX kernel deals with programs. As you can see in figure 1, there are two types of
programming languages: Compiled and Interpreted. In order to be used, code from a compiled language (like
C, C++, Fortran, Pascal, etc…) must be run through a compiler, which produces a binary program as output.
This binary program can run directly on the machine, as the figure shows. By contrast, code from an interpreted
or scripting language is executed directly by a program known as the interpreter, instead of being made into its
own program.
There are advantages and disadvantages to both compiled and interpreted languages. The chief
difference between the two is that interpreters generally parse the code line by line as they execute it (i.e., at run
time). This makes debugging the code much easier, because the interpreter can give a detailed error message if
it doesn’t understand a particular line of code. Interpreted code also does not need to be as rigorously defined
as compiled code. For example, variable types may not need to be declared in a scripting language. However,
translating code on the fly takes time, and thus interpreted languages pay for their convenience in execution
time. Generally though, scripting languages are still fast enough to be used for almost any program, unless
there is a great deal of computation or data processing involved.
Properties of UNIX programs
User ID and Environment Variables

Recall that on a UNIX machine, access to files is controlled by the permissions set on the file. In order
to use files, a program must know the user id of the person who started it. This user id is stored in an
environment variable. The kernel keeps track of environment variables for each UNIX process(program).
Processes are able to read their environment variables, and to change most of them.
Process inheritance

UNIX processes are able to create child processes, by a process known as forking. Children of a given
process inherit most of the environment variables of that process: thus, they are owned by the same user and
have the same permissions as the parent process. (Programs which have the set user id permission bit set are an
exception to this, but that is a topic beyond the scope of this handout). Processes can spawn child processes to
perform certain tasks for them, and can pass information to these subprocesses by setting environment
variables.


1
The POSIX standard, which almost all UNIX systems comply with, specifies the syntax for a shell that must be installed as /bin/sh.
This shell is very close to the original bourne shell in syntax. It is referred to as the POSIX shell, the bourne shell, or simply sh.

OCB 4/16/2000
3
Return values

All programs in a UNIX environment return a value to their parent program when they finish execution.
This value is used to indicate whether the program was successful in performing the task it was given.
Generally, UNIX programs return 0 if everything went well, and something besides 0 otherwise. Since all
commands typed by the user at the command prompt are started as child processes of the shell, the shell
receives the return value of the program when it finishes. This is also true of programs invoked in shell scripts.
Standard File streams

To make things easier, UNIX provides all programs with three standard files, which are predefined for
them by the kernel. These are standard input, standard output, and standard error. Standard input is generally
connected to the keyboard of the machine, and standard output and standard error to the screen. These default
settings can be changed when the shell when it invokes the program, a process known as file redirection. The
important thing to remember is that the program itself doesn’t know (or care) where its standard
input/output/error file streams are connected; it simply reads or writes to/from them.
The Shell
As we mentioned before, UNIX shells lead a dual life: they can be both command line interfaces to the
user and interpreters for shell scripts. Generally, we speak of a shell being executed as an interactive shell or in
interactive mode if it reads commands in from the keyboard. A shell is in non-interactive mode if it is reading
commands from a file.
How the shell processes commands
When interpreting a script, the shell processes commands in the file line by line. Each line of the file, in
turn, is processed as a command. Some commands are internal to the shell: the shell knows how to execute
these commands itself (for example, the control flow statements like if and while that we will see later are
built in).
If the shell does not recognize a command as built in, it searches the directories listed in the $PATH
environment variable, looking for files by the same name. If it cannot find such a file, then the shell prints an
error message (something to the effect of “command not found”). If it can, it checks that that file is executable
by this user. If it is not, the shell will print an error message, stating that execute permission to the file is
denied. If the file is a binary program, the shell executes it as a child process. If not, the shell checks the first
line of the file. If it contains the characters ‘#!’ followed by the name of a program then the shell assumes
that this file is a script, and that the program given will be able to execute it. Thus, it runs the given program as
a child process of the shell with the script as input. In our case, we are using the sh shell as the interpreter for
our scripts. Thus, all our script files should be executable (have the ‘x’ permission bit set), and should start with
the line ‘#!/bin/sh’, since /bin/sh is always the name of the POSIX shell.

OCB 4/16/2000
4
PART II: HOW TO PROGRAM WITH THE SHELL

Making a file into a shell script
Before we can execute a shell script, we must make sure that we have execute permissions on the script,
and that the first line of the script contains the text ‘#!/bin/sh’.
Once our file is an executable shell script, all we have to do is write the script to do what we want it to
do. To do this, let us examine the programming abilities that the shell gives us.
Control flow
Like most compiled languages, shell scripts can contain a number of statements that allow the program
to make decisions. These statements include the following:
Statement Syntax Notes
if
if condition1; then
command1
command2
elif condition2; then
command3
command4
else
command5
fi
If condition1 is true, execute
command1 and command2.
If condition1 is false and condition2
is true, execute command3 and
command4.
If both condition1 and condition2 are
false, execute command5.
elif and else parts are optional,
and can be included or left out in any
combinations.
Multiple elif statements are
allowed, but only one else may be
used, and it must be last.
Must put fi (if backwards) at end,
to tell script where if statement ends.

while loop
while condition1; do
command1
command2
done

If condition1 is true, execute
command1 and command2. Keep
executing these commands, over and
over, until condition1 becomes false.
Obviously, command1 or
command2 must somehow be able
to make condition1 false:
otherwise, the loop will continue
forever.
until loop
until condition1; do
command1
command2
done
Same as while loop, except
execute commands until
condition1 becomes true.

OCB 4/16/2000
5
for loop for varname in arg1 arg2 arg3 …; do
command1
command2
done

or

for varname do
command1
command2
done
Set the variable varname to each
element of list (arg1, arg2, arg3,
etc…) in turn, and execute
commands.

or

Set varname to each positional
parameter ($1, $2, $3, etc…) in turn,
and execute commands.
case statement case word in
value1)
command1
command2
;;
value2|value3)
command3
command4
;;
...
default)
command5
command6
;;
esac
Compare word to value1. If they
are equal, execute commands after
value one, until two semicolons in a
row (‘;;’) are encountered, then
continue executing after the esac
(case backward) statement.
If they are not equal, compare
word to value2 and value3. If either
of these are equal to word, execute
following commands until two
semicolons in a row are encountered
(‘;;’), as before.
Continue this way until all values
have been tested. If none of the
values matches word, then execute
the commands after the default)
statement.
Word can be a literal or a
variable, as can all values
Expressions
So, what is a condition. Remember that control flow syntax requires expressions that evaluate to true or
false. Expressions can involve files, strings, or numbers, and there are different tests that can be applied to each
type of data.
Expressions are actually evaluated by the test command. Thus, an if statement would look like this
if test condition; then
statement1
statement2
fi
However, since this is not terribly clear to read, there is an alternate form of the test command, which
looks like this:
if [ condition ]; then

OCB 4/16/2000
6
statement1
statement2
fi
This is easier to read and understand than the former syntax. Note that there must be a space between
the open bracket ‘[‘ and the condition. Remember that this is an alternate form of the test command. The
‘[‘ character is actually interpreted as a program name; hence, there must be a space between it and its
arguments.
With this said, here is how to actually form a condition:
Files:
Operator Example Comments
-f -f myfile
True if myfile exists and is a normal file
-d -d mydir
True if myfile exists and is a directory
-s -s myfile

True if the length of myfile is greater than zero.
-r -r myfile
True if myfile exists and is readable by the user who is running this script
-w -w myfile
True if myfile exists and is writeable by the user who is running this script
-x -x myfile
True if myfile exists and is executable by the user who is runni ng this
script
Numbers:
Operator Example Comments
+ 3 + 4
7
Regular addition
- 4 – 3 Regular subtraction
* 4 * 3
12
Multiplication.
/ 9 / 4
2

9.0/4.0
2.25
Division.
Note that if both numbers are integers (no decimal point), then integer
division is performed.
% 9 % 4
1
Remainder.
Remainder when integer division is performed on two numbers
-eq 3 –eq 3
1 (true)
True if both numbers are equal
-ge 3 –ge 2
1 (true)
True if the first number is greater than or equal to the second
-le 3 –le 3
1 (true)
True if the first number is less than or equal to the second
-ne 3 –ne 4
1 (true)
True if the first and second numbers are unequal
-gt 3 –gt 2 True if the first number is greater than the second

OCB 4/16/2000
7
0 (false)
-lt 3 –lt 4
0 (false)
True if the first number is less than the second
Strings:
Operator Example Comments
= s1 = s2 True if string s1 and string s2 are the same
!= s1 != s2 True if string s1 and s2 are not the same
-z -z s1 True if string s1 does not exist (has a length of zero)
-n -n s1 True if string s1 exists (ha a length greater than zero)
Logic and grouping:
Operator Example Comments
-a cond1 –a cond2 AND condition. True if cond1 and cond2 are both
true.
-o cond1 –o cond2 OR condition. True if either cond1 or cond2 are true.
! !cond1
NOT condition. !cond1 is true if cond1 is false.
() (cond1 –o cond2) –a cond3 Parenthesis. Used for grouping expressions

See the Examples section for an example of control flow and conditional syntax.
Variables
In addition to the standard environment variables, Unix shells have their own set of local variables. The
following expressions and commands allow us to use and manipulate both environment and local variables.
export varname
Put local variable varname into environment
var1=value1
Define a local variable varname with value value1
$var1
“$var1”
“${var1}”
(variable substitution)
Replace $var1 (or ${var1}) with the value of the variable var1.
var1 can be either an environment variable or a local variable.

Note:‘$var2’ (or ’${varname}’) is replaced directly with its value,
before the shell tries to execute the line it is in. If the value is blank (i.e.
“”) or that variable name has not been defined, then a blank string will be
substituted, leaving nothing where the variable used to be. This can cause
problems when used in certain contexts. For example:
if [ $undefined_var = 3 ]
Becomes
if [ = 3 ]
After variable substitution. When the shell tries to execute this line, it
encounters a syntax error. Thus, it is usually best to put double quotes (“”)
around variables. Then, even if the substituted string is blank, there will
still be the quotes where the variable once was.

OCB 4/16/2000
8
$0 The name of this script as called on the command line. i.e. if I run the
command:
myscript arg1 arg2 arg3
Then $0 would equal “myscript”
$1 $2 … $9
These 9 variables (referred to as positional
parameters) contain the first 9 arguments to the
program.
Thus, if I run the command:
myscript arg1 arg2 arg3 arg4
then the positional parameters would be set like so:
$1 arg1
$2 arg2
$3 arg3
$4 arg4
$5 - $9 undefined.
shift n Shift the positional parameters by n positions.
In other words, if n is 2:
new $1 = old $3
new $2 = old $4
etc…

note that old $1 and $2 in this example are permanently lost. Make sure to
save them in another variable if you care about their values.

if n is not given, it is assumed to be 1.

$* All of the command line arguments of the script, separated by spaces. (Just
the arguments, not the script name (i.e. not $0)).
$# The number of command line arguments
$? The return value of the last command executed. Useful for checking if a
command was successful or not.
Literals:
Numbers:
To specify a number in the shell, you simply type it out as you would any other place. These are all valid
numbers:
1
4.5
392
1874.025

OCB 4/16/2000
9
String:
To specify text or string values to the shell, simply type them out, like this:
hello
how are you?
Note that spaces normally separate strings. Thus, the second line will be interpreted as three separate strings
(‘how’, ‘are’, and ‘you?’). If you want to include spaces in a string, you must put quotes around it, like this:
"how are you?"
'how are you?'
You can use either single or double quotes to quote a string. The difference is that variables in a double quoted
string will be replaced with their values, while single quoted strings are taken at face value. Thus, in this
example:
spoo=”how”
var1=”$spoo are you”
var2=’$spoo are you’
The value of var1 is ‘how are you’, while the value of var2 is ‘$spoo are you’.

Now, we come to an obvious question. What if I want to put an apostrophe, or double quotes inside a quoted
string. If we just put it in, the shell will think that it marks the end of the quoted string. The way to do this is
with the backslash character (\). In general, putting the backslash character before another character toggles
any ‘special’ meanings of that character. The following character sequences, when placed into a quoted string,
have the following meanings:
\\ \ (literal backslash)
\' ‘ (literal apostrophe (single quote))
\" “ (literal double quote)
\n newline (like return key)
\t tab (like tab key)
Thus, the string “I can\’t do that” becomes ‘I can’t do that’. Putting a backslash before a
character is often termed This type of syntax is often termed ‘escaping’ a character.
Other useful functions
echo string
write string to standard output
cat filename
copy the contents of the file filename to standard output
read varname
Read a line of input from the user (until the user presses ENTER). Store the
text typed in the variable varname. This function is extremely useful,
because it allows the program to get feedback from the user.
exit num
Exit the shell script, returning num to the calling program. Generally, num
should be zero if the program completed correctly, and nonzero if an error
occurred. If num is omitted, it is assumed to be zero.

OCB 4/16/2000
10
cat <<- end
Text to be output
more text
still more text
end
This construct is known as a here document. It is actually a special form of
redirection. Everything between the line with ‘<<- end’ and the word
end on a line by itself (except for leading tabs) will be passed as standard
input to the cat command, which will print it to standard output. Actually,
any word can be used in place of end. This is useful for printing out long,
multiline messages in shell scripts. This is functionally equivalent to:
echo “Text to be output”
echo “more text”
echo “still more text”
but is easier to use for long messages.
PART III: EXAMPLE

To illustrate shell scripting, we will do an example. Suppose I want to make a database of names and phone
numbers. It would be nice to have a program that could provide a menu interface to this database. Here, we
will write a program that will allow us to do this.
The first step in writing any program is to decide what you want the program to do. In our case, there
are four basic actions. Of course, all programs must have some form of quit command, to allow the user to exit.
Also, we would like to be able to view the contents of our database file, and to change them. Finally, since our
database could potentially become very large, it would be nice to be able to search the database for a certain
name or number.
In addition to these commands, there are some more commands that, while not necessary, would be
convenient. It would be nice if we could specify on the command line which database file should be used by
the program. It would also be nice if we could give a starting command to the program on the command line, so
that we would not need to be prompted for the first command.
Lets say that these are the commands we will use:
At prompt:
e edit database
v view database
s search database
h print a helpful message
? print a helpful message (different key, same command)
q quit the program
On command line:
-c command run command before prompting for input
dbfilename use the file ‘dbfilename’ as the database file

Thus, if I type:

phone –c e db2


OCB 4/16/2000
11
Then the program should start and immediately let me e
dit the database file ‘db2’, before it prompts me
for input.
Now that we know what we want our program to do, we will outline the structure of the program in
English, so that we understand it. This “pre-write” of the program is referred to as pseucoding. Pseudocoding
is a good idea, because it allows you to see the structure of the program without worrying about all of the details
of correct shell syntax. Some people prefer to draw a flowchart instead of writing pseudocode. In either case,
these pre-coding techniques help us to have a clear picture in our minds of how the program will run, and we
can then more easily write the final shell script.

Here is the pseudocode:
START

print out welcome message

do this for each command line argument:
if the argument is the name of a valid file,
use the argument as the database file name.
if the argument is ‘-c’
set up the next argument after ‘-c’ as a command to be
done before any prompts for input

do this until we receive the quit command:
if there is no command already
prompt for command
read in command from user
if command is ‘e’ (edit)
let user edit database file
if command is ‘v’ (view)
print out database file contents
if command is ‘s’ (search)
ask user what to search for
search file and display results
if command is ‘h’ or ‘?’ (help)
print out a (hopefully!) helpful message
if command is ‘q’ (quit)
print closing message
exit program
END

OCB 4/16/2000
12
Now that we have the structure of the program down, we can “flesh it out” into a full shell script, putting in the
appropriate syntax:
############### Start of Scripts #################
#!/bin/sh
#don't forget to put this line at the top!

#this is the default database filename to use if there is none on
#the command line
dbfile=database

#give starting values to some of the variables we will use
command=""
quit="false"
input=""

#process command line arguments (args)
while [ $# -gt 0 ]; do

#get the current arg
opt=$1;

#shift args by 1, so that the next arg is $1
shift;

#if this option is the command option
if [ "$opt" = "-c" ]; then
#get the next arg. It should be the command
command=$1;
#discard the command name from arg list
shift;
#if this arg is a valid filename
elif [ -f "$opt" ]; then
#use the arg as the dbfile
dbfile=$opt;
else
#if the arg is not either '-c' or a filename, then
#it is invalid. Simply shift it away
shift;
fi
done

#main command loop
#prompt for and process commands until quit
until [ "$quit" = "true" ]; do
#do we have a command to execute already?
#if not, then get one

OCB 4/16/2000
13
if [ -z "$command" ]; then
#prompt user for command
echo "Enter a command[evsqh?]:"

#read in result
read input

#if so, then prepare to execute command
else
#if there was a command from the command line, prepare to
#do it, and clear the command variable, to show that the
#command was done
input="$command"
command=""
fi


#select action based on command input
case $input in
"e")
#use vi command to edit file
vi $dbfile;;
"v")
#clear screen (keeps things neat)
clear;

#use cat and more commands to view file
cat $dbfile | more;;
"s")
#prompt user for search expression
echo "Enter expression to search for:"

#read in search expression
read expression

#clear screen
clear;

#use grep and more programs to search database for expression
#and display results
grep -ni "$expression" $dbfile | more;;
"h"|"?")
#clear screen
clear;

#print help message
#use here document to make things easier.

OCB 4/16/2000
14
cat <<- here
Available commands:
v View database contents
e Edit database
s Search database
h or ? display this Help message
q quit

Command line arguments:
-c com execute the command com (see above command list) upon
startup, and before prompting

dbfile use dbfile as the database
here
;;


"q")
#print closing message
echo "Thank you for using this program"

#set quit variable. While loop will terminate when it
#sees this.
quit="true";;
esac

done
############### End of Scripts #################
Going Further

Obviously, this handout does not cover everything you may need to know about UNIX scripts. Once
you understand how scripting works, however, you should be able to pick up details from the system manual
pages (man command). If you feel you still need help, there are a number of good books on UNIX that explain
scripting, and there are many tutorials and resources on the web. I encourage you to take the time to learn this
very important skill, in order to become a truly proficient UNIX user.
Books
I highly recommend the O’reilly book series in general. These are the books with the black and white
animal pictures on the cover. Check out www.oreilly.com

Also, there are good (but more advanced) books on shell scripting from Wrox Press (www.wrox.com
) ,
that you may want to check out.
If anyone has any comments or suggestions on how to improve this handout, drop me a line at bready@ieee.org
.