Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.08 MB, 487 trang )
Korn Shell: Unix and Linux Programming Manual, Third Edition, The
By Anatole Olczak
Table of Contents
Chapter 8. Writing Korn Shell Scripts
Executing Korn Shell Scripts
Let's make a Korn shell script out of the print Hello world command by putting it into a file like this:
$ print "print Hello world" >prhello
Before Korn shell scripts can be executed, they must be made executable by setting the execute and
read bits with the chmod command:
$ chmod 755 prhello
or
$ chmod +rx prhello
Assuming that the current directory is in the search path $PATH, prhello can now be executed by simply
invoking it by name:
$ prhello
Hello world
Korn shell scripts can also be executed by invoking them as the first argument to ksh:
$ ksh prhello
Hello world
Now we can use prhello like any other command. The output can be directed to a file:
$ prhello >p.out
$ cat p.out
Hello world
It can be used with a pipe:
$ prhello | wc
1
2 12
or with command substitution:
$ print "We always say \"$(prhello)\""
We always say "Hello world"
By default, Korn shell scripts are run in a separate environment. This means that variables from the
current environment are not available to Korn shell scripts unless explicitly exported, and variables
defined in Korn shell scripts are not passed back to the parent shell. Just to prove it, here is a
demonstration. The checkvar Korn shell script does just one thing: it prints the value of LOCALVAR.
$ cat checkvar
print "LOCALVAR is set to: $LOCALVAR"
If LOCALVAR is set to something in the current environment, and checkvar is run, we see that
LOCALVAR is not defined:
$ LOCALVAR="This is the original value"
$ checkvar
LOCALVAR is set to:
If we export LOCALVAR, then its value will be available to checkvar:
$ typeset —x LOCALVAR
$ checkvar
LOCALVAR is set to: This is the original value
To show that Korn shell script environments cannot modify variable values in the parent shell, we'll
change checkvar to reassign a value to LOCALVAR.
$ cat checkvar
print "LOCALVAR is set to: $LOCALVAR"
LOCALVAR="This is a new value"
print "The new LOCALVAR is set to: $LOCALVAR"
Now when it is run, LOCALVAR is set to the new value:
$ checkvar
LOCALVAR is set to: This is the original value
The new LOCALVAR is set to: This is a new value
Meanwhile, back in the parent shell, LOCALVAR has not been affected.
$ print $LOCALVAR
This is the original value
If the allexport option is enabled (set –a, or set ?o allexport), variables are automatically exported
when defined. By default, this option is disabled.
Positional Parameters
Positional parameters are special variables used to keep track of arguments to the Korn shell, scripts,
and functions. Positional parameter names contain only digits and cannot be set directly using
variable=value syntax. By default, parameter zero (or $0) is set to the name of the shell, script or
function.
$ print $0
/bin/ksh
The remaining parameters 1 to n are set to each of the arguments passed to the shell, script or function.
For example, if you invoke a Korn shell script called ptest and pass the arguments A, B, and C, then in
the script ptest, $0 would be set to ptest, $1 to A, $2 to B, and $3 to C.
Table 8.1. Positional Parameters
$0
name of script or function or pathname of Korn shell for set
$n
nth argument to script, function, or set
${n}
nth argument to script, function, or set when n is greater than 9
$#
number of positional parameters
$*, $@
all positional parameters separated with a blank
"$*"
all positional parameters enclosed in double quotes
${*:X}
all X to the last positional parameters
"${*:X}"
all X to the last positional parameters enclosed in double quotes
${*:X:n}
n positional parameters beginning with Xth
"${*:X:n}"
n positional parameters beginning with Xth enclosed in double quotes
There are three special Korn shell variables that provide information about the current positional
parameters. The first is $#, and it contains the number of positional parameters. The other two are $@
and $*, and they both contain all the positional parameters. So in the above ptest example, $# would be
3, and both $* and $@ would be A B C. Here is a Korn shell script that manipulates positional
parameters. It displays the name of the current script, the number of positional parameters, and the value
of each of the positional parameters:
$ cat check_params
print "Script name:
$0"
print "Number of args passed: $#"
print "Arguments passed:
$*"
print "Arg 1=$1, Arg 2=$2, Arg 3=$3"
If executed with no arguments, this is the output:
$ check_params
Script name:
check_params
Number of args passed: 0
Arguments passed:
Arg 1=, Arg 2=, Arg 3=
while if executed with the arguments A and B, the output is:
$ check_params A B
Script name:
check_params
Number of args passed: 2
Arguments passed:
AB
Arg 1=A, Arg 2=B, Arg 3=
Modifying Positional Parameters
By default, $0 is set to the name of the shell, script or function. It cannot be set or modified. The
remaining parameters from $1 to $n can be reassigned with the shift command.
The shift command, with no arguments, shifts positional parameters left once, so that $1 takes the value
of $2, $2 takes the value of $3, and so on. The original value of $1 is lost.
Let's change the check_params script so that it shifts the positional parameters left once:
$ cat check_params
print "Script name:
$0"
print "Number of args passed: $#"
print "Arguments passed:
$*"
print "Arg 1=$1, Arg 2=$2, Arg 3=$3"
shift
print "Number of remaining args: $#"
print "Remaining args:
$*"
print "Arg 1=$1, Arg 2=$2, Arg 3=$3"
When we run it again with the arguments A B, we get:
$ check_params A B
Script name:
check_params
Number of args passed:
2
Arguments passed:
AB
Arg 1=A, Arg 2=B, Arg 3=
Number of remaining args:
Remaining args:
B
Arg 1=B, Arg 2=, Arg 3=
1
After the shift command, $1 is set to B and $2 is unset. The original value of $1 is lost.
The positional parameters can be shifted left more than once by providing an integer argument to the
shift command: shift n.
Now let's try something else with positional parameters. Here is a Korn shell script called kuucp. It uses
uucp to copy a file to the public directory on the rnihd system.
$ cat kuucp
PUBDIR=${PUBDIR:?usr/spool/uucppublic}
uucp $1 rnihd!$PUBDIR/$1
print "Copied $1 to rnihd!$PUBDIR/$1"
So instead of typing this long command line:
$ uucp n.out rnihd!/usr/spool/uucppublic/n.out
we can do this:
$ kuucp n.out
and in the script, $1 gets substituted with n.out in both the source and target file arguments. We could
extend this further to be able to uucp files to any system by having a system name given as another
command-line argument. Now $1 is used for the source and target file, and $2 for the remote system
name.
$ cat kuucp
PUBDIR=${PUBDIR:?usr/spool/uucppublic}
uucp $1 $2!$PUBDIR/$1
print "Copied $1 to $2!$PUBDIR/$1"
To send the file msg.c to the uucp public directory on the unisf system, kuucp would be invoked like
this:
$ kuucp msg.c unisf
$1 would be substituted with the msg.c, and $2 with unisf. Notice that the destination directory is taken
from PUBDIR variable. If it's not set, the default uucp public directory is used.
The exit command
In Chapter 2, we learned that Unix programs return an exit status. And that a zero exit status indicates
successful execution, while a non-zero exit status indicates failure. The exit command allows you to
terminate execution from anywhere in a Korn shell script and return an exit value using this format:
exit
or
exit n
where n is the exit status to return. If n is not specified, the exit status of the previous command is used. If
you don't use exit, then scripts finish after the last command is executed.
Take a look at the kuucp script again. What happens if an error occurs? For example, if the file argument
is entered incorrectly, or it doesn't exist? The uucp command will fail, but the status message following
will still get displayed. Here is a good place for exit. It could be used to terminate execution and return a
non-zero exit status if for some reason the uucp command failed. To get the exit status, $? is checked
after uucp is run. If it is non-zero, then we display our own error message and exit. Otherwise, the next
command is executed and the script terminates successfully.
$ cat kuucp
PUBDIR=${PUBDIR:?usr/spool/uucpublic}
uucp $1 $2!$PUBDIR/$1 2>&?/span>
(($? != 0)) && {print "Got uucp error"; exit 1;}
print "Copied $1 to $2!$PUBDIR/$1"
By the way, the 2>&?/span> just traps the uucp error messages. We don't need to see them
anymore, since kuucp is now doing its own error processing. Now when kuucp is run on a
non-existent file, this is what happens:
$ kuucp nofile unisf
Got uucp error
$ print $?
1
The exit command does one more thing. If given at the command prompt, it terminates your login shell.
Top
Korn Shell: Unix and Linux Programming Manual, Third Edition, The
By Anatole Olczak
Table of Contents
Chapter 8. Writing Korn Shell Scripts
The [[...]] Command
The [[...]] command is used to evaluate conditional expressions with file attributes, strings, integers, and
more. The basic format is:
[[expression]]
where expression is the condition you are evaluating. There must be whitespace after the opening
brackets, and before the closing brackets. Whitespace must also separate the expression arguments and
operators. For example, these are incorrect:
[[$X=$Y]]
[[$X = $Y]]
while this is correct:
[[ $X == $Y ]]
Notice that there is white space between $X, $Y, and the = operator.
If the expression evaluates to true, then a zero exit status is returned, otherwise the expression evaluates
to false and a non-zero exit status is returned.
If you are familiar with the test and [...] commands, then you'll recognize that [[...]] is just a new and
improved version of the same commands. It basically functions the same way, except that a number of
new operators are available.
Table 8.2. [[...]] String Operators
–n string
true if length of string is not zero
–o option
true if option is set
–z string
true if length of string is zero
string1 = string2
true if string1 is equal to string2
string1 != string2
true if string1 is not equal to string2
string = pattern
true if string matches pattern
string != pattern
true if string does not match pattern
string1 < string2
true if string1 less than string2
string1 > string2
true if string1 greater than string2
Checking Strings
We could use the [[...]] command to check if a variable is set to a certain value. Here, variable X is
assigned abc, then evaluated in this expression:
$ X=abc
$ [[ $X = abc ]] && print "X is set to abc"
X is set to abc
Using the test and [...] commands, the same command could be written as:
test "$X" = abc && print "X is set to abc"
or
[ "$X" = abc ] && print "X is set to abc"
To check if a variable is set to null, the –z option can be used:
[[ —z $VAR ]] && print "VAR is set to null"
or it could be compared to the null string like this:
[[ $VAR = "" ]] && "VAR is set to null"
Checking Patterns
The Korn shell also lets you compare strings to patterns. We could check if X begins with a 'a' like this:
$ X=abc
$ [[ $X = a* ]] && print "$X matches a*"
abc matches a*
or if it's a three-character string:
$ [[ $X = ??? ]] && print "$X has exactly 3 \
characters"
abc has exactly 3 characters
Using the +([0?]) pattern, we could check if X is set to a number:
$ X=123
$ [[ $X = +([0?]) ]] && print "$X is a number"
123 is a number
Table 8.2 lists the most commonly used [[...]] string operators.
Table 8.3. Some [[...]] File Operators
–a file
true if file exists.
–d file
true if file exists and is a directory.
–f file
true if file exists and is a regular file.
–G file
true if file exists and its group id matches the effective group id of the current process.
–L file
true if file exists and is a symbolic link.
–Ofile
true if file exists and its user id matches the effective user id of the current process.
–r file
true if file exists and is readable.
–s file
true if file exists and its size is greater than zero.
–S file
true if file exists and is a socket.
–u file
true if file exists and its set user-id bit is set.
–w file
true if file exists and is writable.
–x file
true if file exists and is executable. If file is a directory, then true indicates that the
directory is searchable.
file1 –ef
true if file1 exists and is another name for file2.
file2
file1 –nt
true if file1 exists and is newer than file2.
file2
file1 ?ot
true if file1 exists and is older than file2.
file2
Checking File Attributes
Because manipulating files is so important in programming, the Korn shell provides a whole range of file
operators. The most basic operation to perform on a file is to see if it exists, and that can be done using
the –a operator. This is a new Korn shell file operator. Make sure you don't get it confused with the logical
AND operator used by the test and [...] commands, which is also written as –a.
$ touch tmp
$ [[ —a tmp ]] && print "File tmp exists"
File tmp exists
This only indicates that it exists, but not much else. It may be a directory, or a symbolic link, but using this
operator, that's all we know. If we wanted more information, the –f or ?span class="docEmphStrong">d
operators could tell us if a file existed and was a normal file (–f) or if it was just a directory (–d). Let's try
the ?span class="docEmphStrong">f operator on the tmp file:
$ [[ –f tmp ]] && print "File tmp exists and \
is a regular file"
File tmp exists and is a regular file
If we tried the –d operator on the tmp file, it would evaluate to false, because it isn't a directory:
$ [[ –d tmp ]] && print "File tmp exists and \
is a regular file"
$
While on a directory it would evaluate to true:
$ mkdir tmpdir
$ [[ –d tmpdir ]] && print "Directory tmp exists"
Directory tmp exists
This conditional command checks if $FILE is readable, and if not, prints an error message and exits:
[[ –r $FILE ]]||{ print $FILE not readable; exit 1; }