《Linux系统下Shell编程基础教程.pdf》由会员分享,可在线阅读,更多相关《Linux系统下Shell编程基础教程.pdf(25页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Dschool 开发者学院 http:/LinuxLinuxLinuxLinux 系统下系统下 ShellShellShellShell 编程基础教程编程基础教程我们可以使用任意一种文字编辑器,比如 gedit、kedit、emacs、vi 等来编写 shell 脚本,它必须以如下行开始(必须放在文件的第一行):#!/bin/bash.注意:最好使用“!/bin/bash”而不是“!/bin/sh”,如果使用 tc shell 改为 tcsh,其他类似。符号#!用来告诉系统执行该脚本的程序,本例使用/bin/bash。编辑结束并保存后,如果要执行该脚本,必须先使其可执行:chmod+x fil
2、ename此后在该脚本所在目录下,输入./filename 即可执行该脚本。一、一、变量赋值和引用变量赋值和引用Shell 编程中,使用变量无需事先声明,同时变量名的命名须遵循如下规则:1首个字符必须为字母(a-z,A-Z)或者_2中间不能有空格,可以使用下划线(_)3不能使用其他标点符号需要给变量赋值时,可以这么写:变量名=值要取用一个变量的值,只需在变量名前面加一个$(注意:给变量赋值的时候,不能在=两边留空格)#!/bin/bash#对变量赋值:Dschool 开发者学院 http:/a=hello world#等号两边均不能有空格存在#打印变量 a 的值:echo Ais:$a挑个自己
3、喜欢的编辑器,输入上述内容,并保存为文件 first,然后执行 chmod+x first 使其可执行,最后输入./first 执行该脚本。其输出结果如下:Ais:hello world有时候变量名可能会和其它文字混淆,比如:num=2echo this is the$numnd上述脚本并不会输出this is the 2nd而是this is the;这是由于 shell 会去搜索变量 numnd的值,而实际上这个变量此时并没有值。这时,我们可以用花括号来告诉 shell 要打印的是num 变量:num=2echo this is the$numnd其输出结果为:this is the 2n
4、d注意花括号的位置:num=2Dschool 开发者学院 http:/echo this is the$numnd其输出结果为:this is the 2nd需要注意 shell 的默认赋值是字符串赋值。比如:var=1var=$var+1echo$var打印出来的不是2而是11。为了达到我们想要的效果有以下几种表达方式:let var+=1var=$var+1(var+)var=$($var+1)var=$(expr$var+1)#不建议使用var=expr$var+1#强烈不建议使用,注意加号两边的空格,否则还是按照字符串的方式赋值,为 Esc 下方的,而不是单引号。注意:前2种方式在 b
5、ash 下有效,在 sh 下会出错。let 表示数学运算,expr 用于整数值运算,每一项用空格隔开,$将中括号内的表达式作为数学运算先计算结果再输出。Shell 脚本中有许多变量是系统自动设定的,我们将在用到这些变量时再作说明。除了只在脚本内有效的普通 shell 变量外,还有环境变量,即那些由 export 关键字处理过的变量。本文不讨论环境变量,因为它们一般只在登录脚本中用到。二、二、ShellShellShellShell里的流程控制里的流程控制Dschool 开发者学院 http:/if if if if 语语 句句if表达式如果条件为真,则执行 then 后的部分:if.;then
6、.elif.;then.else.fi大多数情况下,可以使用测试命令来对条件进行测试,比如可以比较字符串、判断文件是否存在及是否可读等等通常用 来表示条件测试,注意这里的空格很重要,要确保方括号前后的空格。-f somefile :判断是否是一个文件-x/bin/ls :判断/bin/ls 是否存在并有可执行权限-n$var :判断$var 变量是否有值$a=$b :判断$a 和$b 是否相等执行 man test 可以查看所有测试表达式可以比较和判断的类型。下面是一个简单的 if 语句:#!/bin/bashif$SHELL=/bin/bash;thenecho your login she
7、ll is the bash(bourne again shell)elseecho your login shell is not bash but$SHELLfiDschool 开发者学院 http:/变量$SHELL包含有登录shell 的名称,我们拿它和/bin/bash进行比较以判断当前使用的 shell是否为 bash。三、三、&和和|操作符操作符熟悉 C 语言的朋友可能会喜欢下面的表达式:-f/etc/shadow&echo This computer uses shadow passwords这里的&就是一个快捷操作符,如果左边的表达式为真则执行右边的语句,你也可以把它看作逻辑
8、运算里的与操作。上述脚本表示如果/etc/shadow 文件存在,则 打印“Thiscomputer uses shadow passwords”。同样 shell 编程中还可以用或操作(|),例如:#!/bin/bashmailfolder=/var/spool/mail/james-r$mailfolder|echo Can not read$mailfolder;exit 1;echo$mailfolder has mail from:grep From$mailfolder该脚本首先判断 mailfolder 是否可读,如果可读则打印该文件中的From 一行。如果不可读则或操作生效,打
9、印错误信息后脚本退出。需要注意的是,这里我们必须使用如下两个命令:-打印错误信息-退出程序我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用;普通函数稍后再作说明。即使不用与和或操作符,我们也可以用 if 表达式完成任何事情,但是使用与或操作符会更便利很多。casecasecasecase 语句语句case 表达式可以用来匹配一个给定的字符串,而不是数字(可别和 C 语言里的 switch.case混淆)。case.inDschool 开发者学院 http:/.)do something here;esacfile 命令可以辨别出一个给定文件的文件类型,如:file lf.gz,
10、其输出结果为:lf.gz:gzip compressed data,deflated,original filename,last modified:Mon Aug 27 23:09:18 2001,os:Unix我们利用这点写了一个名为 smartzip 的脚本,该脚本可以自动解压 bzip2,gzip 和 zip 类型的压缩文件:#!/bin/bashftype=$(file$1)case$ftype in$1:Zip archive*)unzip$1;$1:gzip compressed*)gunzip$1;$1:bzip2 compressed*)bunzip2$1;*)echo Fi
11、le$1 can not be uncompressed with smartzip;esac你可能注意到上面使用了一个特殊变量$1,该变量包含有传递给该脚本的第一个参数值。也就是说,当我们运行:Dschool 开发者学院 http:/smartzip articles.zip$1 就是字符串 articles.zip。selectselectselectselect 语句语句select 表达式是 bash 的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择:select var in.;dobreak;done.now$var can be used.下面是一个简单的示例:
12、#!/bin/bashecho What is your favourite OS?select var in Linux Gnu Hurd Free BSD Other;dobreak;doneechoYouhave selected$var该脚本的运行结果如下:What is your favourite OS?1)LinuxDschool 开发者学院 http:/2)Gnu Hurd3)Free BSD4)Other#?1Youhave selected Linuxwhile/forwhile/forwhile/forwhile/for 循环循环在 shell 中,可以使用如下循环:wh
13、ile.;do.done只要测试表达式条件为真,则 while 循环将一直运行。关键字break用来跳出循环,而关键字”continue”则可以跳过一个循环的余下部分,直接跳到下一次循环中。for 循环会查看一个字符串列表(字符串用空格分隔),并将其赋给一个变量:for var in.;do.done下面的示例会把AB C 分别打印到屏幕上:#!/bin/bashfor var inAB C;doecho var is$varDschool 开发者学院 http:/done下面是一个实用的脚本 showrpm,其功能是打印一些 RPM 包的统计信息:#!/bin/bash#list a con
14、tent summary of a number of RPM packages#USAGE:showrpm rpmfile1 rpmfile2.#EXAMPLE:showrpm/cdrom/RedHat/RPMS/*.rpmfor rpmpackage in$;doif -r$rpmpackage;thenecho=$rpmpackage=rpm-qi-p$rpmpackageelseecho ERROR:cannot read file$rpmpackagefidone这里出现了第二个特殊变量$,该变量包含有输入的所有命令行参数值。如果你运行showrpm openssh.rpm w3m.
15、rpm webgrep.rpm,那么$(有引号)就包含有 3 个字符串,即 openssh.rpm,w3m.rpm 和 webgrep.rpm。$*的意思是差不多的。但是只有一个字串。如果不加引号,带空格的参数会被截断。四、四、ShellShellShellShell里的一些特殊符号里的一些特殊符号引号引号在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓的扩展是指程序会把通配符(比如*)替换成适当的文件名,把变量替换成变量值。我们可以使用引号来防止这种扩展,先来看一个例子,假设在当前目录下有两个 jpg 文件:mail.jpg 和 tux.jpg。Dschool 开发者学院 http
16、:/#!/bin/bashecho*.jpg运行结果为:mail.jpg tux.jpg引号(单引号和双引号)可以防止通配符*的扩展:#!/bin/bashecho*.jpgecho*.jpg其运行结果为:*.jpg*.jpg其中单引号更严格一些,它可以防止任何变量扩展;而双引号可以防止通配符扩展但允许变量扩展:#!/bin/bashDschool 开发者学院 http:/echo$SHELLecho$SHELLecho$SHELL运行结果为:/bin/bash/bin/bash$SHELL此外还有一种防止这种扩展的方法,即使用转义字符反斜杆::echo*.jpgecho$SHELL输出结果为
17、:*.jpg$SHELL五、五、HereHereHereHere DocumentDocumentDocumentDocument当要将几行文字传递给一个命令时,用 here document 是一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果使用 here document 就不必用 echo 函数一行行输出。Here document 以 开头,后面接上一个字符串,这个字符串还必须出现在 here documentDschool 开发者学院 http:/的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且使用 here document打印帮助:#!/bin/
18、bash#we have less than 3 arguments.Print the help text:if$#-lt 3 ;thencat HELPren-renames a number of files using sed regular expressions USAGE:ren regexp replacementfiles.EXAMPLE:rename all*.HTM files in*.html:ren HTM$html*.HTMHELP#这里 HELP 要顶格写,前面不能有空格或者 TAB 制表符。如果 cat一行写成 cat -HELP,前边可以带 TAB.exit
19、0fiOLD=$1NEW=$2#The shift command removes one argument from the list of#command line arguments.shiftshift#$contains now all the files:Dschool 开发者学院 http:/for file in$;doif -f$file ;thennewfile=echo$file|sed s/$OLD/$NEW/gif -f$newfile;thenecho ERROR:$newfile exists alreadyelseecho renaming$file to$ne
20、wfile.mv$file$newfilefifidone示例有点复杂,我们需要多花点时间来说明一番。第一个 if 表达式判断输入命令行参数是否小于3个(特殊变量$#表示包含参数的个数)。如果输入参数小于3个,则将帮助文字传递给 cat 命令,然后由 cat 命令将其打印在屏幕上。打印帮助文字后程序退出。如果输入参数等于或大于3个,我们 就将第一个参数赋值给变量 OLD,第二个参数赋值给变量 NEW。下一步,我们使用shift命令将第一个和第二个参数从参数列表中删除,这样原来的第三个 参数就成为参数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。接着
21、我们判断该文件是否存在,如果存在则 通过 sed 命令搜索和替换来产生新的文件名。然后将反短斜线内命令结果赋值给 newfile。这样我们就达到了目的:得到了旧文件名和新文件名。然后使用 mv 命令进行重命名六、六、ShellShellShellShell里的函数里的函数如果你写过比较复杂的脚本,就会发现可能在几个地方使用了相同的代码,这时如果用上函数,会方便很多。函数的大致样子如下:functionname()#inside the body$1 is the first argument given to the functionDschool 开发者学院 http:/#$2 the se
22、cond.body函数没有必要声明。只要在执行之前出现定义就行下面是一个名为 xtitlebar 的脚本,它可以改变终端窗口的名称。这里使用了一个名为 help的函数,该函数在脚本中使用了两次:#!/bin/bashhelp()cat HELPxtitlebar-change the name of an xterm,gnome-terminal or kde konsoleUSAGE:xtitlebar-h string_for_titelbarOPTIONS:-h help textEXAMPLE:xtitlebar cvsHELPexit 0#in case of error or if
23、-h is given we call the function help:-z$1&help$1=-h&help#send the escape sequence to change the xterm titelbar:Dschool 开发者学院 http:/echo-e 0330;$1007#在脚本中提供帮助是一种很好的编程习惯,可以方便其他用户(和自己)使用和理解脚本。七、七、命令行参数命令行参数我们已经见过$*和$1,$2.$9 等特殊变量,这些特殊变量包含了用户从命令行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的参数和查看帮助的-h 选项)。但是在编写
24、更复杂的程序时,您可能会发现您需要更多的自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值(比如文件名)。有好多方法可以实现对输入参数的分析,但是下面的使用 case 表达式的例子无疑是一个不错的方法。#!/bin/bashhelp()cat shift by 2-)shift;break;#end of options-*)echo error:no such option$1.-h for help;exit 1;*)break;esacdoneecho opt_f is$opt_fecho opt_l is$opt_lecho first arg is$1echo
25、 2nd arg is$2你可以这样运行该脚本:cmdparser-l hello-f-somefile1 somefile2返回结果如下:opt_f is 1opt_l is hellofirst arg is-somefile12nd arg is somefile2Dschool 开发者学院 http:/这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与 case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据 unix 系统的惯例,首先输入的应该是包含减号的参数。八、八、ShellShellShellShell脚本示例脚本示例一般编程步骤一般编程步骤
26、现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。写一个框架脚本(framework.sh),该脚本包含了大多数脚本需要的框架结构,是一个非常不错的主意。这样一来,当我们开始编写新脚本时,可以先执行如下命令:cp framework.sh myscript然后再插入自己的函数。让我们来看看如下两个示例。二进制到十进制的转换二进制到十进制的转换脚本 b2d 将二进制数(比如 1101)转换为相应的十进制数。这也是一个用 expr 命令进行数学运算的例子:#!/bin/bash#vim:set sw=4 ts=4 et:help()cat HELPb2d-convert
27、 binary to decimalUSAGE:b2d-h binarynumOPTIONS:-h help textDschool 开发者学院 http:/EXAMPLE:b2d 111010will return 58HELPexit 0error()#print an error and exitecho$1exit 1lastchar()#return the last character of a string in$rvalif -z$1;then#empty stringrval=returnfi#wc puts some space behind the output this
28、 is why we need sed:numofchar=echo-n$1|sed s/g|wc-c Dschool 开发者学院 http:/#now cut out the last charrval=echo-n$1|cut-b$numofcharchop()#remove the last character in string and return it in$rvalif -z$1;then#empty stringrval=returnfi#wc puts some space behind the output this is why we need sed:numofchar
29、=echo-n$1|wc-c|sed s/g if$numofchar=1;then#only one char in stringrval=returnfinumofcharminus1=expr$numofchar-1#now cut all but the last char:rval=echo-n$1|cut-b-$numofcharminus1#原来的 rval=echo-n$1|cut-b 0-$numofcharminus1运行时出错.#原因是 cut 从1开始计数,应该是 cut-b 1-$numofcharminus1Dschool 开发者学院 http:/while -n$
30、1;docase$1 in-h)help;shift 1;#function help is called-)shift;break;#end of options-*)error error:no such option$1.-h for help;*)break;esacdone#The main programsum=0weight=1#one arg must be given:-z$1&helpbinnum=$1binnumorig=$1while -n$binnum;dolastchar$binnumif$rval=1;thensum=expr$weight+$sumfiDscho
31、ol 开发者学院 http:/#remove the last position in$binnumchop$binnumbinnum=$rvalweight=expr$weight*2doneecho binary$binnumorig is decimal$sum#该脚本使用的算法是利用十进制和二进制数权值(1,2,4,8,16,.),比如二进制10可以这样转换成十进制:0*1+1*2=2为了得到单个的二进制数我们是用了 lastchar 函数。该函数使用 wc c 计算字符个数,然后使用 cut 命令取出末尾一个字符。Chop 函数的功能则是移除最后一个字符。文件循环拷贝文件循环拷贝你可
32、能有这样的需求并一直都这么做:将所有发出邮件保存到一个文件中。但是过了几个月之后,这个文件可能会变得很大以至于该文件的访问速度变慢;下 面的脚本 rotatefile 可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为 outmail)为 outmail.1,而原来的 outmail.1就变成了 outmail.2 等等.#!/bin/bash#vim:set sw=4 ts=4 et:ver=0.1Dschool 开发者学院 http:/help()cat HELProtatefile-rotate the file nameUSAGE:rotatefile-h filenameOPT
33、IONS:-h help textEXAMPLE:rotatefile outThis will e.g rename out.2 to out.3,out.1 to out.2,out to out.1BRand create an empty out-fileThe max number is 10version$verHELPexit 0error()echo$1Dschool 开发者学院 http:/exit 1while -n$1;docase$1 in-h)help;shift 1;-)break;-*)echo error:no such option$1.-h for help
34、;exit 1;*)break;esacdone#input check:if -z$1 ;thenerror ERROR:you must specify a file,use-h for helpfifilen=$1#rename any.1,.2 etc file:for n in 9 8 7 6 5 4 3 2 1;doif -f$filen.$n;thenp=expr$n+1echo mv$filen.$n$filen.$pmv$filen.$n$filen.$pDschool 开发者学院 http:/fidone#rename the original file:if -f$fil
35、en;thenecho mv$filen$filen.1mv$filen$filen.1fiecho touch$filentouch$filen这个脚本是如何工作的呢?在检测到用户提供了一个文件名之后,首先进行一个 9到1的循环;文件名.9重命名为文件名.10,文件名.8重命名为文件 名.9等等。循环结束之后,把原始文件命名为文件名.1,同时创建一个和原始文件同名的空文件(touch$filen)九、九、脚本调试脚本调试最简单的调试方法当然是使用echo 命令。你可以在任何怀疑出错的地方用echo打印变量值,这也是大部分 shell 程序员花费80%的时间用于调试的原因。Shell 脚本的好处在于无需重新编译,而插入一个 echo 命令也不需要多少时间。shell 也有一个真正的调试模式,如果脚本strangescript出错,可以使用如下命令进行调试:sh-x strangescript7 上述命令会执行该脚本,同时显示所有变量的值。shell 还有一个不执行脚本只检查语法的模式,命令如下:Dschool 开发者学院 http:/sh-n your_script这个命令会返回所有语法错误。我们希望你现在已经可以开始编写自己的 shell 脚本了,尽情享受这份乐趣吧!:)
限制150内