830 likes | 977 Views
第三讲 shell 程序设计. 3.1 概述. shell 是 UNIX 系统用用户与操作系统交互的最基本工具,实际上 shell 除了有命令解释执行的功能外,还具备有其他的功能,如:. 系统环境的设置. 输入输出的重新定向. shell 程序语言的设计等。. 3.1 概述.
E N D
3.1 概述 shell是UNIX系统用用户与操作系统交互的最基本工具,实际上shell除了有命令解释执行的功能外,还具备有其他的功能,如: 系统环境的设置 输入输出的重新定向 shell程序语言的设计等。
3.1概述 在UNIX系统中使用shell程序设计,可以完成比较复杂并且自动执行的任务。UNIX系统上的shell程序在本质上是一种解释执行的脚本语言,使用的元素是UNIX系统的命令集,它与windows系统中的bat批处理文件原理相同,但是比批处理文件更强大,也更灵活。
举例 3.1 概述 # append.sh case $# in 1 ) cat >> data.txt;; 2 ) cat $1 >> $2 ;; * ) echo “Incorrect usage ! ” ;; esac
举例 3.1 概述 cd $HOME for dir in cc work do echo “…. In $dir….” cd $dir for file in *.[c] do ls –l $file done cd done
3.2 Shell程序的执行方式 Shell程序有多种执行方式,比如,我们建立一个比较简单的shell程序脚本,文件保存命名为 test.sh,内容如下: date who ps 执行这个test.sh可以有多种方式。 1)将test.sh作为shell命令的执行参数来完成该shell的执行: $ sh test.sh 2)利用输入输出的重定向方式 $ sh <test.sh 3)直接执行该shell程序,但首先要保证该shell程序具备可执行的权限,可以用chmod来修改: chmod a+x test.sh, 之后在命令行执行它 $ test.sh
3.3 Shell程序的变量 • 如果shell程序只能简单地执行一些命令,那shell的功能无疑是非常弱的,幸运的是,shell程序被设计为可以支持变量的使用,比如定义变量,应用变量的值等等。这使得shell程序具有异常强大的功能。需要注意的是,shell的变量只有字符串型,在shell程序中出现的数值计算都是基于字符串类型来完成的。 • Shell有以下几种基本类型的变量: • shell定义的环境变量 • 用户定义的变量 • 位置参数 • 预定义变量
3.3 Shell程序的变量:变量赋值 1)shell中的变量和变量赋值 shell 变量定义由字母开始,可以包含数字,字母和下划线,例如var1, username,pass_word等。 变量的赋值可以在程序运行中进行,也可以在变量定义时进行,例如UNIX=“SYSTEM V”,如果字符串中间没有空格和制表符,则可以不要双引号。 值得注意的是B-shell中等号=的两边是不允许有空格的。
3.3 Shell程序的变量:变量访问 2)shell中变量的访问和参数替换 在shell程序中,或者是shell命令中,使用操作符$来对shell变量进行访问。例如,要显示某个变量时,可以输入: $ echo $UNIX (系统将会显示SYSTEM V ) 在某些场合下,为了明确表示对shell变量的引用,可以用花括弧{ }或者双引号将变量括起来,例如: $ echo ${UNIX} $ echo ${UNIX}Aaaaaa $ echo “$UNIX” is very good SYSTEM V SYSTEM VAaaaaa SYSTEM V is very good
3)shell中引号的说明: 单引号‘’、 双引号“”、 反引号` (a)单引号’ :使用单引号时,shell将一对单引号之间的内容看成是纯粹的字符串信息,一对单引号之间可以包含更多的单引号。 you=”kkk” echo ‘this is just a ‘test’, can $you tell me the time ?’ 将会显示this is just a ‘test’, can $you tell me the time ?, 它不会将$you替换成“kkk“, 同时‘test’也是作为字符串内容来处理的。 (b)双引号“ : 它将双引号之间的特殊字符作为shell中的含义来解释。 $ file=unixshell $ echo “the content of ‘date’ is in $file” 这个例子将会显示 the content of wed Aug 16 16:20:12 is in unixshell,这个例子中,’date’被解释成了当前时间的返回值,$file被解释成了unixshell字符串。 (c)反引号`用它来赋值时,表示变量中保存的是执行对应命令的结果。 name=`whoami` echo $name 这个时候显示的不是字符串whoami,而是执行字符串whoami的返回结果,当然,要先保证字符串是一个合法的命令。
4)变量的定义域 同其他的编程语言如C,pascal一样,shell的变量也有局部变量和全局变量的区别。通常在一个shell程序中,通过形如var1=abc方式定义的都是局部变量,只能在该shell程序的进程中起作用,要定义全局变量,需要在局部变量定义之后使用export 命令来将这个变量声明为全局的: export $var1 以下面的例子var.sh,我们来说明全局变量和局部变量的区别和使用 $ var1=csu $ echo $var1 $ sh $ echo $var1 $ exit $ echo $var1 $ export $var1 $ sh $ echo $var1 第一个echo显示的是 csu,这是通常的用法 第三行,调用sh进入一个新的shell进程 第四行的echo显示为空,因为var在这个进程中并没有进行定义 第五行,调用exit退出新建的shell回到原来shell的进程中 第六行的echo又能够正常显示了 第七行调用export,将var1定义为全局的,这样在新建的shell中,var1也能访问, 最后一行显示出csu
3.3 Shell程序的变量:环境变量 5)环境变量 shell在开始执行时就已经定义了一些和系统的工作环境有关的变量,这些变量用户还可以重新定义,常用的shell环境变量有:
3.3 Shell程序的变量:环境变量 HOME:用于保存注册目录的完全路径名。 PATH:用于保存用冒号分隔的目录路径名,shell将按PATH变量中给出的顺序搜索这些目录,找到的第一个与命令名称一致的可执行文件将被执行。 TERM:终端的类型。 UID:当前用户的标识符,取值是由数字构成的字符串。 PWD:当前工作目录的绝对路径名,该变量的取值随cd命令的使用而变化。 PS1:主提示符,在特权用户下,缺省的主提示符是“#”,在普通用户下,缺省的主提示符是“$”。 PS2:在shell接收用户输入命令的过程中,如果用户在输入行的末尾输入“\”然后回车,或者当用户按回车键时shell判断出用户输入的命令没有结束时,显示这个辅助提示符,提示用户继续输入命令的其余部分,缺省的辅助提示符是“>”。
3.3 Shell程序的变量:位置变量 6)Shell中命令的位置变量 位置参数是一种在调用shell程序的命令行中按照各自的位置决定的变量,是在程序名之后输入的参数。位置参数之间用空格分隔,shell取第一个位置参数替换程序文件中的$1,第二个替换$2,依次类推。$0是一个特殊的变量,它的内容是当前这个shell程序的文件名,所以,$0不是一个位置参数,在显示当前所有的位置参数时是不包括$0的。 #listargs.shargv1 argv2 argv3 argv4 $0 $1 $2 $3 $4
3.3 Shell程序的变量:预定义变量 预定义变量和环境变量相类似,也是在shell一开始时就定义了的变量,所不同的是,用户只能根据shell的定义来使用这些变量,而不能重定义它。所有预定义变量都是由$符和另一个符号组成的,常用的shell预定义变量有:
3.3 Shell程序的变量:变量替换 7).Shell中的变量替换 在shell编程中,有时候会遇到这种情况,一个变量在尚未赋值的时候,就被程序进行了输出处理,这样输出的变量将会得到一个空字符串的结果。为了避免这种情况的发生,shell中引入了变量替换的概念,在变量没有赋值的时候可以用一个预先定义的字符串来替换。 替换的形式有三种,列举如下: $ {var:-word}表示如果var已经被赋值,则取它的值,否则取word的值,但var不改变。 $ {var:=word} 表示如果var已经被赋值,则取它的值,否则取word的值,同时将word赋给 var。 $ {var:+word} 表示如果var已经被赋值,则取它的值,否则var变量置为空。
3.4 test命令的使用 在shell中经常要对某些变量的值进行判断来决定分支程序的走向,如同C中使用 if( a == 0) 一样。Shell中通过test命令来完成这个功能。 test的格式表示为: test expre 或者 test [expre] test命令的执行结果是,如果表达式为真,则返回真值0,否则返回非0的假值。 test命令可以对文件,字符串内容,整数n等变量进行测试。
3.4 test命令的使用 1)对文件特性的测试 语法: test [-dfrwxs] file -e文件名:如果文件存在则为真 -r文件名:如果文件存在且可读则为真 -w文件名:如果文件存在且可写则为真 -x文件名:如果文件存在且可执行则为真 -s文件名:如果文件存在且至少有一个字符则为真 -d文件名:如果文件存在且为目录则为真 -f文件名:如果文件存在且为普通文件则为真 -c文件名:如果文件存在且为字符型特殊文件则为真 -b文件名:如果文件存在且为块特殊文件则为真
3.4 test命令的使用 例如: $ test –d /home/user && echo “directory exists” 判断/home/user 是否为一个目录文件,如果为真(&&)则执行echo进行显示 $ test –x /home/user/sh1.sh || echo “sh1.sh dos not exist or is not runnable !” 判断文件/home/user/sh1.sh是否可执行,如果为假(||)则执行echo进行显示
3.4 test命令的使用 2)对字符串内容的测试 语法: test s(字符串s为有值时返回真,为空则返回假) test -zs与test s 相反,为空时返回真,不空则返回假 test s1 = s2当字符串s1和s2相等时返回真,不相当则返回假 test s1 != s2与test s1=s2相反。 例 x1=“005” x2 =5,进行字符串测试 #test “$x1” = “$x2” #echo $? 系统将显示0, 结果为假值,表示x1和x2不相等
3.4 test命令的使用 3)对于整数n的的测试 格式 test n1 [-eq/ne/it/ie/gt/ge] n2 它们分别表示n1和n2之间 相等 /不相等 /n1<n2/n1<=n2 /n1>n2 /n1>=n2 时返回真值。 例 x1=“005” x2 =5,进行数字式测试 #test “$x1” -eq “$x2” #echo $? 系统将显示1, 结果为真值,表示数字x1和数字x2相等
3.5 条件控制语句 在结构化的程序设计中,三种基本的语句结构是顺序结构,分支结构,循环结构。其中的分支结构,在shell中常用if和case语句来实现。 1)if语句 if语句的分支可以有 无分支条件语句,二分支条件语句,多分支条件语句。 无分支条件语句格式如下: if [condition] then 命令1 命令2 … 命令n fi 当条件condition为真时,执行then后面的所有命令,condition为假则执行fi后面的语句。
3.5 条件控制语句 1)if语句 例:edit.sh if [ $#=1 ] then cp $1 $HOME/user1 fi vi $1 exit 0
3.5 条件控制语句 多分支结构:分支大于2的条件语句。 if [condition1] then elif [contition2] then elif [condition3] then … else command_n fi 二分支条件语句: 格式为 if then else 结构 if [condition] then 命令1 命令2 else. 命令n fi
3.5 条件控制语句 例:假设一个连续运行系统,将建立错误记录文件errorfile 并不断写入错误信息。现在要求编写一个shell程序来产生定时错误日志文件datelog,能在datelog 中体现每个时间段中的错误信息 # checkerr.sh To check error information with time stamp !!! date >> datelog if test –r errorfile then cat errorfile >> datelog rm errorfile else echo “No error occurs during this hour!“ >> datelog fi
3.5 条件控制语句 2)case语句 可以实现多选一的控制结构 case语句的语法为: case word in 模式1 ) 命令 ;; 模式2 ) 命令 ;; 模式3 ) 命令 ;; esac 注意,在case中,每个命名命令的结束用;;表示模式匹配已经完成,不再匹配其他的模式,相当于C中的break。
3.5 条件控制语句 例:编写一段shell程序,根据执行时获取的当前时间显示不同的问候信息 • # hello.sh • hour = `date +%H` • case $hour in • 0[1-9] | 1[01] ) echo “Good morining !!” ;; • 1[234567] ) echo “Good afternoon !!” ;; • ) echo “Good evening !! ” ;; • esac
例:利用shell的位置变量参数编写一个shell,当只有一个参数时,从键盘输入文本保存到data.txt中,当有两个参数时,将参数1代表的文件内容添加到参数2表示的文件,其他情况则给出错误提示信息例:利用shell的位置变量参数编写一个shell,当只有一个参数时,从键盘输入文本保存到data.txt中,当有两个参数时,将参数1代表的文件内容添加到参数2表示的文件,其他情况则给出错误提示信息 3.5 条件控制语句 # append.sh case $# in 1 ) cat >> data.txt;; 2 ) cat $1 >> $2 ;; * ) echo “Incorrect usage ! ” ;; esac
3.6 循环语句 shell中的循环有for和while两种循环方式。 1)for的语法格式为: for var in list-of-vars do command1 command2 … commandn done for的语法说明为:shell程序扫描变量列表list-of-vars,将其中的每一个字依次存放在var中,并执行do和done之间的语句。如果list-of-vars中有n个字,则这个for循环将执行n次。
3.6 循环语句 在需要的时候,也可以进行for循环的嵌套。 见下面的例子: cd $HOME for dir in cc work do echo “…. In $dir….” cd $dir for file in *.[c] do ls –l $file done cd done
3.6 循环语句 2)while循环 while - do - done结构 while循环中,只要循环条件为真就一直循环下去。 格式: while [condition] do command1 command2 ….. done
3)until循环 until-do-done until循环和while循环类似,所不同的是until循环只要循环条件为假,就一直执行循环体。 格式: until [condition] do command1 commandn done 如果第一次执行时,循环条件condition就为真,那循环体将得不到执行。 同时还要注意,必须在程序中设置条件condition为真的因素,否则循环将一直进行下去,从而进入了死循环。 3.6 循环语句
3.6 循环语句 例:编写shell程序一次性创建若干个新文件,如file1, file2, file3. . . . . . file10 两个相关命令 touch 命令,改变文件的访问和修改时间 格式: touch [option][time] filename [option] -a 只修改文件的访问时间 -m 只修改文件的修改时间 当指定文件filename不存在,则创建一个具有默认权限和当前时间的新文件
3.6 循环语句 例:编写shell程序一次性创建若干个新文件,如file1, file2, file3. . . . . . file10 两个相关命令 expr 命令,对shell变量进行算术运算的操作,因为shell变量都是字符型,进行算术运算必须使用expr命令 shell中错误的算术运算: #count = 9 #count = $count + 11 #echo count shell中正确的算术运算: #count = 9 #count = `expr $count + 11` #echo count 系统将显示:9+11 系统将显示:20
3.6 循环语句 例:编写shell程序一次性创建若干个新文件,如file1, file2, file3. . . . . . file10 # genfiles.sh NUMBER = 1 While [ $NUMBER –lt 11] do touch file$NUMBER NUMBER = `expr $NUMBER + 1` done
break用于立即终止当前循环的执行,而contiune用于不执行循环中后面的语句而立即开始下一个循环的执行。这两个语句只有放在do和done之间才有效。break用于立即终止当前循环的执行,而contiune用于不执行循环中后面的语句而立即开始下一个循环的执行。这两个语句只有放在do和done之间才有效。 3.7无条件控制语句break和continue
在shell中还可以定义函数。函数实际上也是由若干条shell命令组成的,因此它与shell程序形式上是相似的,不同的是它不是一个单独的进程,而是shell程序的一部分。函数定义的基本格式为:在shell中还可以定义函数。函数实际上也是由若干条shell命令组成的,因此它与shell程序形式上是相似的,不同的是它不是一个单独的进程,而是shell程序的一部分。函数定义的基本格式为: functionname { 若干命令行 } 3.8 函数
函数使用:1、调用函数之前,必须先定义函数。2、函数的参数的传递:functionname para1 para2 …. 3、在函数内部参数的读取: $0-$9 , $@(所有参数) $#(参数总个数)4、函数的返回值:可能使用return命令返回数字值;要返回字符串值,可以字符串保存在一个全局性的变量中,该变量在函数结束后能被外界使用;如果没有使用return命令,则函数返回值是函数中最后执行的一条命令的退出状态码。5、变量使用:函数内部声明的变量默认为全局变量,使用local关键字声明的变量为局部变量(如 local var=“var”)。如果局部变量与全局变量同名,则在函数内部局部变量覆盖全局变量。6、返回值的获取,当执行完函数后,函数的返回值被存放在$?中,可以通过它来获取函数的返回值。 3.8函数
举例 #HELLO.sh function hello( ) { echo “hi” } hello echo $? 执行结果如下:# sh hello.shhi 3.8 函数
举例 #This is a example for testing the return value of function function fun() { return 10} fun echo $? exit 0 3.8 函数 执行结果如下:# sh fun.sh10
举例 #funsample1.sh function add( ) { declare -i sum sum=$1+$2 return $sum} add 10 20 echo $? 执行结果如下:# sh funsample1.sh30 3.8 函数
举例 function find_file(){ if [ $# -lt 1 ];then return 1; fi if [ find -name $1 –print2>/dev/null ] then : else echo "File $1 not found" fi} 3.8 函数
举例 #_MAIN_ if [ $# -lt 1 ];then echo "Usage: $0 filename" return 1; fi for loop in $@ do find_file $loop done 3.8 函数
举例 #_MAIN_ if [ $# -lt 1 ];then echo "Usage: $0 filename" return 1; fi for loop in $@ do find_file $loop done 3.8 函数
举例 #test1.sh function hello() { echo “hi” } #test.sh #注意.后面要有一个空格 . test1.sh hello 执行结果: hi 3.8 函数
在shell中有两种命令分组的方法:“()”和“{}”,前者当shell执行()中的命令时将再创建一个新的子进程,然后这个子进程去执行圆括弧中的命令。当用户在执行某个命令时不想让命令运行时对状态集合(如位置参数、环境变量、当前工作目录等)的改变影响到下面语句的执行时,就应该把这些命令放在圆括弧中,这样就能保证所有的改变只对子进程产生影响,而父进程不受任何干扰;{}用于将顺序执行的命令的输出结果用于另一个命令的输入(管道方式)。当我们要真正使用圆括弧和花括弧时(如计算表达式的优先级),则需要在其前面加上转义符(\)以便让shell知道它们不是用于命令执行的控制所用。在shell中有两种命令分组的方法:“()”和“{}”,前者当shell执行()中的命令时将再创建一个新的子进程,然后这个子进程去执行圆括弧中的命令。当用户在执行某个命令时不想让命令运行时对状态集合(如位置参数、环境变量、当前工作目录等)的改变影响到下面语句的执行时,就应该把这些命令放在圆括弧中,这样就能保证所有的改变只对子进程产生影响,而父进程不受任何干扰;{}用于将顺序执行的命令的输出结果用于另一个命令的输入(管道方式)。当我们要真正使用圆括弧和花括弧时(如计算表达式的优先级),则需要在其前面加上转义符(\)以便让shell知道它们不是用于命令执行的控制所用。 3.9 命令分组
shell程序是一种解释性语言,它的执行是逐行进行的,程序中是否有语法错误,不执行到那一行系统是无法知道的。C,pascal等高级语言是有编译过程的,程序中的语法错误在编译阶段就能检测出来,因此比shell更容易调试 shell程序通常有三种调试方法: 3.10 Shell程序调试 1)交互式调试 shell程序中使用的命令都能在命令行中运行,所以对于一些不很熟悉的命令和语法可以先用命令进行验证,然后再编写到shell程序中,这种调试方法称为交互式调试,也是最简单的一种调试方法。
2)在编辑过程中不断调试执行所编辑的shell程序2)在编辑过程中不断调试执行所编辑的shell程序 在UNIX系统中可以打开多个工作窗口。我们可以在一个窗口中用vi等编辑器进行编辑,然后在另一个连接窗口中执行该shell程序,这样可以随时观察到已经编写的部分是否正确。最终完成这个shell程序的编写。 3.9 Shell程序调试 3)用shell程序提供的跟踪功能进行调试 shell程序可以使用 -v -x 选项对shell程序进行跟踪 -v:当读入shell输入行时把它们显示出来,完成详细跟踪。Shell程序在完成这段程序执行时首先逐行读入执行的命令,并在标准输出上显示该命令要执行的实际内容,然后执行该命令。如果没有语法错误,执行将一直进行下去直到完成shell的执行。 -x:执行命令前先把命令和它们的参数显示出来
假设有这么一个简单的shell,叫test1.sh date echo $PATH 我们用-v参数来调试, 执行sh -v test1.sh那么输出就应该是: date Mon Sep 8 9:55:01 Linux9 2003 echo $PATH /usr/bin:/usr/ucb/bin:/home/user1 shell在执行date前先显示该命令然后显示date的执行结果,接着显示echo $PATH以及它的执行结果。 3.9 Shell程序调试 如果用-x参数来执行这个shell,执行 sh -x test1.sh则系统的输出将是: +date Mon Sep 8 9:55:01 Linux9 2003 +echo /usr/bin:/usr/ucb/bin:/home/user1 /usr/bin:/usr/ucb/bin:/home/user1 date这一行没有参数,所以只显示命令 echo这一行有参数$path,被直接替换成了参数的值。
3.9 Shell程序调试 在调试一个比较大的shell时,没有必要对整个shell程序都进行跟踪,可以只对其中较为负责的部分进行跟踪,这时可以通过 set –v ……… set +v或者set –x …………set +x来把需要跟踪的部分包含进来。