Chinaunix首页 | 论坛 | 博客
  • 博客访问: 102006160
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-04-18 22:04:06

 
 
在DOS 中,你可能会从事一些例行的重覆性工作,此时你会将这些重覆性的命令写成批次档,只要执行这个批次档就等於执行这些命令。大家会问在UNIX中是否有批次处理这个东东,答案是有的。在UNIX中不只有如DOS 的批次处理,它的功能比起DOS 更强大,相对地也较复杂,已经和一般的高阶语言不相上下。在UNIX中大家都不叫做批次档,而叫做Shell script。  

一般而言,Shell script的地位和其它的可执行档(或命令)是完全相同的,只不过Shell script是以文字档的方式储存,而非二进位档。而执行Shell script时,必须有一个程式将其内容转成一道道的命令执行,而这个程式其实就是Shell ,这也就是为什麽我们叫做Shell script的原因(往後我们称为script)。不同Shell 的script基本上会有一些差异,所以我们不能将写给A shell 的script用B shell 执行。而在UNIX中大家最常使用Bourne Shell以及C Shell ,所以这堂课就介绍这两种script的写法。  

将文字档设为可执行的Shell script  

如果我们已经写好script,如何将其设成可执行档呢?因为script其实是一个可执行档,所以必须将其存取权设定成可执行。我们可以使用下列命令更改存取权:  
chmod u+x filename 只有自己可以执行,其它人不能执行  
chmod ug+x filename 只有自己以及同一群可以执行,其它人不能执行  
chmod +x filename 所有人都可以执行  

而我们如何指定使用那一个Shell 来解释所写的script呢?几种基本的指定方式如下所述:  
1. 如果script的第一个非空白字元不是"#",则它会使用Bourne Shell。  
2. 如果script的第一个非空白字元是"#"时,但不以"#!"开头时,则它会使用C Shell。  
3. 如果script"#!"开头,则"#!"後面所写的就是所使用的Shell,而且要将整个路径名称指出来。  

这里建议使用第三种方式指定Shell ,以确保所执行的就是所要的。Bourne Shell的路径名称为/bin/sh ,而C Shell 则为/bin/csh。  


1. 使用Bourne Shell 
┌——————————┐ ┌——————————┐ 
│echo enter filename │ │#!/bin/sh │ 
│ . │ or │ . │ 
│ . │ │ . │ 
│ . │ │ . │ 
└——————————┘ └——————————┘ 

2. 使用C Shell 
┌——————————┐ ┌——————————┐ 
│# C Shell script │ │#!/bin/csh │ 
│ . │ │ . │ 
│ . │ │ . │ 
│ . │ │ . │ 
└——————————┘ └——————————┘ 

3. 使用/etc/perl 
┌——————————┐ 
│#! /etc/perl │ 
│ . │ 
│ . │ 
│ . │ 
└——————————┘ 

除了在script内指定所使用的Shell 外,你也可以在命令列中强制指定。比如你要用C Shell 执行某个script,你可以下这个命令:  
csh filename  

此时的script的存取权就不一定要为可执行档,其内部所指定的Shell 也会无效,详细的情形後面会讨论。  

script的基本结构及观念  

script是以行为单位,我们所写的script会被分解成一行一行来执行。而每一行可以是命令、注解、或是流程控制指令等。如果某一行尚未完成,可以在行末加上"\" ,这个时候下一行的内容就会接到这一行的後面,成为同一行,如下  

┌———————————┐ 
│echo The message is \ │ 
│too long so we have \ │ 
│to split it into \ │ 
│several lines │ 
└———————————┘ 

script中出现"#" 时,再它後面的同一行文字即为注解,Shell 不会对其翻译。  

script中要执行一个命令的方法和在命令列中一样,你可以前景或背景执行,执行命令时也会需要设定一些环境变数。  

script的流程控制和一般高阶语言的流程控制没有什麽两样,也和高阶语言一样有副程式。这些使得script的功能更加强大。  

为了达到与高阶语言相同的效果,我们也可以在script中设定变数,如此使script 成为一个名付其实的高阶语言。  

□Bourne Shell  

一、变数  

Bourne Shell的变数型态只有字串变数,所以要使用数值运算则必须靠外部命令达 成目的。而其变数种类有下列几种:  

1. 使用者变数  

这是最常使用的变数,我们可以任何不包含空白字元的字串来当做变数名称。 设定变数值时则用下列方式:  
var=string  

取用变数时则在变数名称前加上一"$" 号。  


┌———————┐ 
│name=Tom │ 
│echo name │ 
│echo $name │ 
└———————┘ 
结果如下: 
name 
Tom 

2. 系统变数(环境变数)  

和使用者变数相似,只不过此种变数会将其值传给其所执行的命令。要将一使 用者变数设定为系统变数,只要加上:  
export var  

┌———————┐ 
│name=Tom │ 
│export name │ 
└———————┘ 

以下是使用者一进入系统之後就已设定好的系统变数:  
$HOME 使用者自己的目录  
$PATH 执行命令时所搜寻的目录  
$TZ 时区  
$MAILCHECK 每隔多少秒检查是否有新的信件  
$PS1 在命令列时的提示号  
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号  
$MANPATH man 指令的搜寻路径  

3. 唯读的使用者变数  

和使用者变数相似,只不过这些变数不能被改变。要将使用者变数设成唯读的 ,只要加上:  
readonly var  

而若只打readonly则会列出所有唯读的变数。还有一点,系统变数不可以设定 成唯读的。  

┌———————┐ 
│name=Tom │ 
│readonly name │ 
│echo $name │ 
│name=John │ 
│readonly │ 
└———————┘ 

结果如下: 
Tom 
name: is read only 
readonly name 
readonly ...... 

4. 特殊变数  

有些变数是一开始执行script时就会设定,并且不以加以修改,但我们不叫它 唯读的系统变数,而叫它特殊变数(有些书会叫它唯读的系统变数),因为这 些变数是一执行程式时就有了,况且使用者无法将一般的系统变数设定成唯读 的。以下是一些等殊变数:  
$0 这个程式的执行名字  
$n 这个程式的第n个参数值,n=1..9  
$* 这个程式的所有参数  
$# 这个程式的参数个数  
$$ 这个程式的PID  
$! 执行上一个背景指令的PID  
$? 执行上一个指令的返回值  

当你执行这个程式时的参数数目超过9 个时,我们可以使用shift 命令将参数 往前移一格,如此即可使用第10个以後的参数。除此之外,吾人可以用set 命 令改变$n及$*,方法如下:  
set string  

如此$*的值即为string,而分解後则会放入$n。如果set 命令後面没有参数, 则会列出所有已经设定的变数以及其值。  

档名:ex1 参数:this is a test  

┌———————————┐ 
│echo Filename: $0 │ 
│echo Arguments: $* │ 
│echo No. of args.: $# │ 
│echo 2nd arg.: $2 │ 
│shift │ 
│echo No. of args.: $# │ 
│echo 2nd arg.: $2 │ 
│set hello, everyone │ 
│echo Arguments: $* │ 
│echo 2nd arg.: $2 │ 
└———————————┘ 
结果如下: 
Filename: ex1 
Arguments: this is a test 
No. of args.: 4 
2nd arg.: is 
No. of args.: 3 
2nd arg.: a 
Arguments: hello, everyone 
2nd arg.: everyone 

值得一提的是,当你想从键盘输入一变数值时,你可以使用下面的命令:  
read var1 var2.....  

这时read会将一个字分给一个变数。如果输入的字比变数还多,最後一个变数会将剩下的字当成其值。如果输入的字比变数还少,则後面的变数会设成空字串。 如果需要处理数值运算,我们可以使用expr命令。其参数及输出列於附录A。  

二、执行命令  

在Bourne Shell中有五种方法执行一个命令,而这五种方式所产生的果有些许的不 同。  

1. 直接下命令  
这个方式和在命令列中直接下命令的效果一样。  

2. 使用sh命令  
sh command  
这个档案必须是Bourne Shell的script,但这个档案并不一定要设成可执行。 除此之外和直接下命令的方式一样。  

3. 使用"."命令  
. command  

这时和使用sh命令相似,只不过它不像sh一般会产生新的process ,相反地, 它会在原有的process 下完成工作。  

4. 使用exec命令  
exec command  
此时这个script将会被所执行的命令所取代。当这个命令执行完毕之後,这个 script也会随之结束。  

5. 使用命令替换  
这是一个相当有用的方法。如果想要使某个命令的输出成为另一个命令的参数 时,就一定要使用这个方法。我们将命令列於两个"`" 号之间,而Shell 会以 这个命令执行後的输出结果代替这个命令以及两个"`" 符号。  

str='Current directory is '`pwd`  
echo $str  
结果如下:  
Current directory is /users/cc/mgtsai  
这个意思是pwd 这个命令输出"/users/cc/mgtsai",而後整个字串代替原 来的`pwd` 设定str 变数,所以str 变数的内容则会有pwd 命令的输出。  

number=`expr $number + 1`  
这就是先前所提要作数值运算的方法,基本上expr命令只将运算式解,而 後输出到标准输出上。如果要将某变数设定成其值,非得靠命令替换的方 式不可。这个例子是将number变数的值加1 後再存回number变数。  

三、流程控制  

在介绍流程控制之前,我们先来看看test命令。test命令的参数是条件判断式,当 条件为真时则传回非零值,而条件为伪时则传回零。在所有的流程控制都必须用到 test命令来判断真伪。而test命令的使用方法则列於附录B。  

test $# = 0  

如果执行这个程式没有参数时,会传回非零值代表"$# = 0"这个条件成立。反 之则会传回零。  

以下介绍各种流程控制:  

1. if then语法以及流程图如下  

│ FALSE 
if (condition) <condition>—┐ 
then │TRUE │ 
then-commands then-commands │ 
fi ├————┘ 
│ 


condition 是一个test命令。往後所介绍的各种流程中的condition 都是test 命令。  
档名:chkarg  

┌———————————┐ 
│if (test $# != 0) │ 
│ then │ 
│ echo Arg1: $1 │ 
│fi │ 
└———————————┘ 
$ chkarg Hello 
Arg1: Hello 
$ chkarg 


2. if then else语法以及流程图如下  

│ FALSE 
if (condition) <condition>—————┐ 
then │TRUE │ 
then-commands then-commands else-commands 
else ├————————┘ 
else-commands │ 
fi 

3. if then elif语法以及流程图如下  

│ FALSE 
if (condition1) <condition1>—┐ 
then │TRUE │ FALSE 
commands1 commands1 <condition2>—┐ 
elif (condition2) │ │ TRUE │ 
then │ commands2 commands3 
commands2 ├—————┴————┘ 
else │ 
commands3 

commands3 
fi 


echo 'word 1: \c' 
read word1 
echo 'word 2: \c' 
read word2 
echo 'word 3: \c' 
read word3 
if (test "$word1" = "$word2" -a "$word2" = "$word3"
then 
echo 'Match: words 1, 2, & 3' 
elif (test "$word1" = "$word2"
then 
echo 'Match: words 1 & 2' 
elif (test "$word1" = "$word3"
then 
echo 'Match: words 1 & 3' 
elif (test "$word2" = "$word3"
then 
echo 'Match: words 2 & 3' 
else 
echo 'No match' 
fi 

4. for in语法以及流程图如下  

│ FALSE 
for var in arg-list ┌—<arg-list还有东西吗?>—┐ 
do │ │TRUE │ 
commands │ 从arg-list取得一项 │  
 done │ 放到变数var │ 
│ │ │ 
│ commands │ 
└——————┘ │ 
┌———————————┐ ┌—————┘ 
│for a in xx yy zz │ │ 
│ do │ 
│ echo $a │ 
│done │ 
└———————————┘ 
结果如下: 
xx 
yy 

yy 
zz 

5. for语法以及流程图如下  

│ FALSE 
for var ┌—<参数中还有东西吗?>—┐ 
do │ │TRUE │ 
commands │ 从参数中取得一项 │ 
done │ 放到变数var │ 
│ │ │ 
│ commands │ 
└—————┘ │ 
档名:lstarg ┌—————┘ 
┌———————————┐ │ 
│for a │ 
│ do │ 
│ echo $a │ 
│done │ 
└———————————┘ 
$lstarg xx yy zz 
xx 
yy 

yy 
zz 

6. while 语法以及流程图如下  


│ FALSE 
while (condition) ┌—<condition>—┐ 
do │ │TRUE │ 
commands │ commands │ 
done └————┘ │ 
┌————┘ 
│ 

┌———————————————┐ 
│number=0 │ 
│while (test $number -lt 10) │ 
│ do │ 
│ echo "$number\c" │ 
│ number=`expr $number + 1` │ 
│done │ 
│echo │ 
└———————————————┘ 
结果如下: 
0123456789 

7. until语法以及流程图如下  


│ TRUE 
until (condition) ┌—<condition>—┐ 
do │ │FALSE │ 
commands │ commands │ 
done └————┘ │ 
┌————┘ 
│ 

它和while 的不同只在於while 是在条件为真时执行回圈,而until 是在条件 为假时执行回圈。  

8. break及continue  
这两者是用於for, while, until 等回圈控制下。break 会跳至done後方执行 ,而continue会跳至done执行,继续执行回圈。  

9. case语法以及流程图如下  

│ TRUE 
case str in <str=pat1>————commands1—┐ 
pat1) commands1;; │FALSE TRUE │ 
pat2) commands2;; <str=pat2>————commands2—┤ 
pat3) commands3;; │FALSE TRUE │ 
esac <str=pat3>————commands3—┤ 
│FALSE │ 
├————————————┘ 
│ 

而pat 除了可以指定一些确定的字串,也可以指定字串的集合,如下  
* 任意字串  
? 任意字元  
[abc] a, b, 或c三字元其中之一  
[a-n] 从a到n的任一字元  
| 多重选择  

┌———————————————┐ 
│echo 'Enter A, B, or C: \c' │ 
│read letter │ 
│case $letter in │ 
│ A|a) echo 'You entered A.';;│ 
│ B|b) echo 'You entered B.';;│ 
│ C|c) echo 'You entered C.';;│ 
│ *) echo 'Not A, B, or C';; │ 
│esac │ 
└————————  
阅读(256) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~