版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第11章bshell编程《Linux操作系统(第5版)》学习目标理解正则表达式的语法,掌握sed、awk及正则表达式的应用理解bshell的特殊字符、位置参数及特殊变量,掌握脚本调用及变量传递机制理解bshell脚本的书写格式,掌握bshell脚本的设计、执行与调试技术章节概述/Summary本章聚焦Bourneshell编程核心技能。首先介绍正则表达式的元字符、字符集及匹配规则。随后讲解流编辑器sed的地址定位、脚本命令及文本转换操作。模式搜索与处理语言awk涵盖记录与域、内置变量、控制语句和函数,适用于格式化数据处理。Bshell编程部分包括:特殊字符、位置参数与特殊变量、变量条件替换、运算符与控制结构、函数定义与调用、命令分组、递归及程序调试方法。还介绍了shift、eval、exec、test、expr等实用命令,并通过综合示例演示实际应用。最后强调脚本书写规范,为自动化运维奠定基础。目录0102正则表达式流编辑(sed)03
模式搜索与处理(awk)04
Bourneshell及其编程正则表达式11.111.1正则表达式
正则表达式是由一个或多个字符或元字符组成的串。普通字符包括大、小写字母和数字,而元字符则是具有特殊意义的字符。支持模式搜索的命令一般都支持正则表达式。正则表达式在不同的系统中的实现可能是不同的。有三种形式的正则表达式:(1)SRE(simpleregularexpression):传统UNIX支持这种正则表达式;(2)BRE(basicregularexpression):ISOPOSIX-2支持这种正则表达式;(3)ERE(extendedregularexpression):ISOPOSIX-2也支持这种正则表达式。ERE与BRE在用法上不尽相同,且ERE比BRE有更丰富的语法,但是ERE不是BRE的超集,一些BRE的表达式若不加修改,则在ERE中可能不能正常使用。正则表达式11.1正则表达式
在UNIX系统中awk,egrep使用ERE,而其他程序(比如,ed,ex,expr,grep,sed和vim等)使用BRE。一个正则表达式包含以下内容。(1)字符集:在指定位置上匹配的一个或多个字符;(2)计数:指定其前面的字符重复的次数,比如“*”、“?”、“+”等;(3)位置字符:特殊字符集,用于标明位置,比如行首、行末等;(4)具有特殊意义的字符。正则表达式元字符是指具有特殊功能的字符,它在正则表达式中不代表自己,而具有特殊的意义。11.1.1字符集元字符元字符意义示例.匹配除换行后以外的其他任意字符a.c匹配abc,adc等*匹配其前面正则表达式的0次或多次出现(ERE)aa*匹配a,aa,aaa,…?匹配其前面正则表达式的0次或1次出现(ERE)bb?匹配b或bb+匹配其前面正则表达式的至少1次出现(ERE)(ab)+匹配ab,abab,…^位置字符表示行首^abc匹配行首为abc的行$位置字符表示行末ter$匹配行末为ter的行[],[^]匹配或不匹配其中的一个字符。如果在选择集中包含[],则它必须是第一个。^为[]内首字符,表示相反的操作[Bb]y匹配By或by。a[^Bb]c不匹配abc或aBc|从多个选项选择(ERE)((auto)|(dog)matic)匹配automatic或dogmatic\转义字符;将其后面的一个字符变为普通字符\”匹配字符”()BRE组或子表达式。定义在圆括号的字符串以备后用。以后1~9表示前面保存的正则表达式((auto)|(dog)matic)匹配automatic或dogmatic类字符用于表示某一种类型的字符(集),可以用来简化正则表达式的长度。11.1.1字符集类字符类
字
符意
义示
例[:alnum:]字符和数字0~9,a~z,A~Z[:alpha:]字符A~Z,a~z[:blank:]白空格。POSIX只包括空格和tab键空格键、tab键等[:cntrl:]控制字符[:digit:]数字0~9[:xdigit:]十六进制字符0~9,a~f[:graph:]制表符BRE字符集是指用于BRE正则表达式的特殊字符集,或在BRE正则表达式中具有特殊意义的字符集。11.1.1字符集1.继承的概念1.什么是异常BRE使用的字符集元字符意义元字符意义[=,=];[:,:];[.,.]等价类、字符类和联合类\(,\),\#子表达式,后向引用\character(通过\实现)特殊字符*,\{l\},\{l,\},\{l,u\}匹配0个或多个[]括号表达式^,$位置字符ERE字符集是指用于ERE正则表达式的特殊字符集,或在ERE正则表达式中具有特殊意义的字符集。11.1.1字符集1.什么是异常ERE匹配字符集元字符意义元字符意义[=,=];[:,:];[.,.]等价类、字符类和联合类()\#子表达式,后向引用\character(通过\实现)特殊字符*,+,?,{l},{l,}{l,u}匹配0个或多个[]方括号表达式^,$位置字符|多种选择
shell正则表达式和正则表达式在某些方面存在不同。例如case模式匹配的是shell变量,文件内容模式匹配与搜索时,应用程序ex、vim、view、grep、sed、awk等有的用的是BRE,有的用的是ERE,要视具体情况而定。11.1.2shell正则表达式1.继承的概念1.什么是异常shell正则表达式流编辑(sed)11.2sed是个流编辑器,其功能是对它的输入流进行按指定格式的转换。sed使用脚本语言对输入流进行加工,脚本可以在命令行或文件中输入。在工作时它只对输入流处理一遍,但却效率非常高。在sed的命令行中,若没有使用-e或-f指定脚本,则它的第一个非选项变量将作为脚本,而其他的变量将作为输入文件,若无输入文件,则使用标准输入。sed的输出对象为标准输出,若输出到文件可使用输出重定向。用法:sed[options]{scripts}[input-file]…11.2流编辑(sed)1.继承的概念1.什么是异常功能及用法参数描述-n,--quiet,--silent抑制不必要的输出,若不使用-n,则输出操作过程的每一行-escript,--expression=script将命令行参数script加入脚本-fscript-file从指定文件中读入脚本-E,-r,--regexp-extended使用扩展的正则表达式参数与说明11.2.3流编辑(sed)1.继承的概念1.sed的地址形式在编写脚本程序时,要用到地址范围的概念。脚本命令地址范围意义地址范围意义N第N行(N为数字)$最后一行/regexp/与模式regexp匹配的行\cregexpc与模式regexp匹配的行,c为任意字符begin~step从begin开始每step行执行一次L1,L2从L1到L2行L1,~N从L1到行号为N的一个倍数的行L1,+N从L1到L1+N行11.2.3流编辑(sed)1.继承的概念1.什么是异常脚本命令2.sed脚本中的部分命令sed之所以能够处理脚本,是因为它有自己的内部命令命令功能命令功能;脚本命令分隔符#注释{}命令组:lbl标号,设置跳转目标=显示当前行行号d删除指定行(不影响原输入文件)l显示当前行内容q[code]立即退出sed(返回码为code)p显示模式空间内容(一般与-n配合使用)!func不执行fun(func的值有:=,l,d和p等)11.2.3流编辑(sed)脚本命令R/rfile从文件中追加文本/行wfile将当前行内容写入文件fileT/t/b[lbl]跳转到标号lbl,若不指定则跳转到脚本末i\text在指定行前插入text文本a\text在指定行后追加text文本c\text以字符串text取代当前行s/rexp1/replace/[flag]查找rexp1并用replace替换。flag:n-整数,只替换第n次出现;g-全部;p-如果替换则显示;wfile-如果替换则写入文件filey/src/dst/在指定范围内用dst内的字符集对应替代src的对应位置的字符。dst与src的字符个数必须相等11.2.4流编辑(sed)1.继承的概念1.什么是异常sed示例(1)将ifile文件中的所有sysman替换成SystemManager并输出到ofile
$sed-e's/sysman/SystemManager/g'<ifile>ofile(2)计算件文件ifile的行数
$sed-n'$='</etc/passwd(3)删除文件/etc/profile中的空行,并将其他的内容输出到ofile
$sed'/^$/d/^[[:space:]]*$/d'</etc/profile>ofile
##也可写为:
$sed'/^$/d;/^[[:space:]]*$/d'</etc/profile>ofile #或
$sed'/^$/d;/^[[:blank:]]*$/d'</etc/profile>ofile
#其实,/^$/d是多余的,因为/^[[:blank:]]*$/中已经包含了/^$/。11.2.4流编辑(sed)1.继承的概念1.什么是异常sed示例(4)显示ifile文件中的所有注释行/非注释行
$sed-e'/^[[:space:]]*#/!d'<ifile #显示注释行。参见不执行某命令!func
$sed-e'/^[[:space:]]*#/d'<ifile #显示非注释行(5)报告系统中所有(非)root用户的活动$ps-ef|sed-e'/^[[:space:]]*root/!d' #显示系统中root用户进程$ps-ef|sed-e'/^[[:blank:]]*root/d' #显示系统中的非root用户进程11.2.4流编辑(sed)sed示例(6)去除文本文件file.txt中的所有行首和行末的白空格,并删除其中的空行
行首、行末的的白空格和空行可分别表示为^[[:space:]]+、[[:space:]]+$和/^$/,若要实现任
务,删除它们的sed而脚本命令可以依次写为
s/^[[:space:]]+//g
s/[[:space:]]+$//g
/^$/d
放在一行上,就是
s/^[[:space:]]+//g;s/[[:space:]]+$//g;/^$/d
因此,整个命令可写为
sed-r-e's/^[[:space:]]+//g;s/[[:space:]]+$//g;/^$/d'file.txt11.2.4流编辑(sed)sed示例
这条命令的脚本内容有点长,若再长些,放在命令行就不合适了。如果认为脚本内容较多,可以将其存入脚本文件,比如efile.sed中,此时的执行方法为:$sed-r-fefile.sedfile.txt #或$sed-r-fefile.sed<file.txt如果再在efile.sed文件的开始处添加如下一“第一行”:#!/usr/bin/sed-rf使用其内容为:
#!/usr/bin/sed-rf
s/^[[:space:]]+//g
s/[[:space:]]+$//g
/^$/d11.2.4流编辑(sed)sed示例
则可以在为其添加执行权后直接执行之。操作过程如下:$chmod+xefile.sed #为efile.sed添加执行权$./efile.sedfile.txt #直接执行efile.sed, 或$./efile.sed<file.txt模式搜索与处理(awk)11.311.3模式搜索与处理(awk)1.继承的概念1.什么是异常功能及用法
awk是一个用于模式搜索和处理的编程语言。它最早的设计目的是针对报表生成的一种小巧且具表达力的语言,因此awk对于格式化结构的文件特别有效。用法:awk[options]-fprogfile[--]file…awk[options][--]programfile…11.3模式搜索与处理(awk)1.继承的概念1.什么是异常参数说明
awk有很多参数用于工作过程的控制。参数描述参数描述-fprogfile指定awk程序文件-vvar=value定义并指定变量的值-Ffs指定格式化文件的记录中域分隔符--结束选项,其后面的为参数11.3模式搜索与处理(awk)1.继承的概念1.什么是异常记录和域
awk记录和域的概念与数据库里记录和域的概念类似。在一个格式记录的文件里,它的每一行称为一个记录,而每一个记录又由域分隔符分隔的不同字段组成,这种字段就是记录中的域。在awk中整个记录用$0表示,而组成记录的每个域依次表示为$1,$2,…。11.3模式搜索与处理(awk)1.继承的概念1.什么是异常变量
在awk中变量是动态定义,只有当它第一次被使用时变量才存在。由于是解释语言,变量可以是任何类型的,这取决于用户如何使用它。在awk中还可以使用数组。在awk中引用自定义变量时不需在其名前面加上标志符“$”。在对变量赋值、操作或处理上awk支持C语言语法。1.内部变量变量说明变量说明ARGC命令行参数的个数ARGV命令行参数数组ARGIND命令行参数数组指针ENVIRON环境变量数组ERRNO文件等操作时出现错误的错误号FILENAMEawk浏览的当前文件名FIELDWIDTHS…指定域宽,不使用域分隔符NF当前文件记录中的域个数FNR当前文件的记录数或号NR当前记录数或号FS域分隔符,默认为白空格RS输入时记录分隔符,默认为回车键OFS输出域分隔符,默认为空格ORS输出记录分隔符,默认为回车键BEGIN定义开始块。只在awk开始时执行END定义结束块。只在awk结束前执行11.3模式搜索与处理(awk)1.继承的概念变量2.用户自定义变量在awk中,变量的变量不用声明类型,awk可以从变量在表达式中的上下文推导出它的数据类型。如果变量未被初始化,awk会将字符串变量初始化为空串,将数值变量初始化为0。必要时,awk会将字符型变量转换为数值型变量,或者反向转换。3.域变量域变量是awk中定义用来存储每条记录各域值的变量,每条记录的第一个域用$1表示,第二个域用$2,依次类推。特殊变量$0总是表示整条记录及内容。域变量可以像用户自定义的变量一样使用,唯一的区别是它们引用了域。新的域可以通过赋值来创建。域变量引用的域如果没有值,则被赋值为空串。域的值发生变化时,awk会以内置变量OFS的值作为域分隔符重新计算$0的值。相反地,当$0发生变化时,也会重新计算每一个域变量。
11.3模式搜索与处理(awk)1.继承的概念1.什么是异常控制语句1.awk的模式awk的模式(pattern)简单说就是正则表达式或常数表达式。awk脚本程序中的表达式是简单表达式或简单表达式的组合。常见模式操作有:/regularexpression/:匹配正则表达式regularexpression(pattern):组表达式,匹配patternpattern1,pattern2:范围表达式。表示从匹配pattern1到匹配pattern2的行relationalexpression:由&&、||和!组成的关系表达式,比如pattern&&pattern、pattern||pattern、!pattern和pattern?pattern:pattern11.3模式搜索与处理(awk)1.继承的概念1.什么是异常控制语句2.awk的控制语句(1)if语句:if(condition)statement[elsestatement](2)while语句:while(condition)statement(3)do语句:dostatementwhile(condition)(4)for语句:for(expr1;expr2;expr3)statement
for(varinarry)statement11.3模式搜索与处理(awk)1.什么是异常控制语句(5)控制转移和退出语句:break,continue,exit(6)语句组:{statements}3.语句块语句块是指用“{}”括起来的语句组或复合语句,上面的语句组就是其中之一。经常用到的还有BEGIN块和END块。(1)BEGIN块一般形式为BEGIN后面跟了一个操作块。awk必须在对输入文件进行任何处理之前先执行BEGIN块。实际上,不需要任何输入文件,也能对BEGIN块进行测试,因为awk要在执行完BEGIN操作块后才开始读取输入。BEGIN操作常常被用来修改内置变量(比如OFS、RS、FS等)的值,或为用户自定义变量赋初值。(2)END块一般形式为END后面跟一个操作块。awk在处理完所有输入行之后才处理END块。11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk的常用函数awk有很多内部函数,用户也可定义自己的函数。函数功能函数功能gsub(r,s,)在$0中搜索字符串r并用s替代gsub(r,s,t)在字符串t中搜索字符串r并用s替换index(s,t)查找字符串s中t的位置length(s)返回字符串s的长度match(s,t)测试字符串s中是否包含匹配模式t的字符串print显示其变量的内容sub(r,s)在$0中用s替换第一次出现的rsubstr(s,p)返回字符串s中从p开始的子字符串substr(s,p,n)返回字符串s中从p开始的长度为n的子串toupper(s)返回字符串s的大写形式tolower(s)返回字符串s的小写形式atan2(x,y)y/x的反正切sin(x)x的正弦cos(x)x的余弦11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk的常用函数exp(x)e^xlog(x)x的自然对数rand()返回一个0~1之间的随机数int(x)x的整数部分and(x,y)x和y的与or(x,y)x和y的或xor(x,y)x和y的异或compl(x)x的补lshift(x,cnt)将x左移cnt位rshift(x,cnt)将x右移cnt位sqrt(x)x的平方根system(cmd)执行shell命令cmd11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk程序的执行
awk脚本或程序由模式操作和函数语句序列组成,格式为:pattern{actionstatement}functionfunc_name(param_list){statements}模式操作包括模式和行为对,其中每个模式都是正则表达式。在awk执行时首先从命令行或程序文件中读取程序,然后解释并执行之。在执行过程中,awk读取输入文件或标准输入,按顺序检查其中每一个记录是否能进行模式匹配,若能则执行行为操作。awk把输入文件的数据读入内存,然后操作内存中的输入数据副本,awk不会修改输入文件的内容。awk总是输出到标准输出,如果想让awk输出到文件,可以使用输出重定向。11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk程序的执行awk对脚本程序处理的过程如下:(1)如果BEGIN块存在,先执行BEGIN块指定的操作。(2)awk从输入文件中读取一行,称为一条输入记录(若文件省略,将从标准输入读)。(3)awk将读入的记录分隔成域,将第1个域放入变量$1中,第2个域放入$2,依次类推。$0表示整条记录。域分隔符由awk内置变量FS的值来指定。(4)把当前输入记录依次与每一个awk命令中的pattern比较,看是否匹配,如果相匹配,就执行对应的操作。如果不匹配,就跳过对应的操作,直到比较完所有的awk命令。(5)当一条输入记录处理了所有的awk命令后,awk读取输入的下一行,继续重复步骤(3)和(4),这个过程一直持续,直到awk读取到文件结束。(6)当awk读完所有的输入行后,如果存在END,就执行END块。11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk使用示例(1)显示系统内root用户进程的进程号和进程名$ps-ef|awk'/root/{print$1,$2,$8}'(2)显示系统中所有非root用户进程,它们是谁的,PID是多少,在干什么$ps-ef|awk'!/root/{print$1,$2,$8}'(3)计算文件infile内数字的和。##假设infile内容为数字串,每行可有任意多个数字串$awk'{for(i=1;i<=NF;i++)s+=$i}END{prints}'ifile(4)显示系统内已经注册(创建)的所有用户名(/etc/passwd内的所有用户)$awk-F:'{print$1}'/etc/passwd#-F:指定域分隔符为":"11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk使用示例(5)为文本文件增添等号$awk'{printNR,$0}'ifile(6)对文本文件行中具有某特殊字符串的行执行shell命令##比如,当遇到有date的行执行date命令。$awk'/date/{system("date+%D%t%T")}'ifile(7)实现wc的功能:统计文本文件中的行数、字数和字符数#实现wc的基本功能,可由以下代码来完成:11.3模式搜索与处理(awk)1.继承的概念1.什么是异常awk使用示例由于内容较多,代码较长,写在命令行有所不便,故可将其保存到文件,比如wc.awk,此时执行方法为:$awk-fwc.awk/etc/passwd在为wc.awk增加执行权后,就可直接执行它,方法是:$chmod+xwc.awk$./wc.awk/etc/passwd#!/usr/bin/awk-fBEGIN{w=0;L=0;c=0}#初始化:不必须{w+=NF;L++;c+=length($0)+1}END{printL,w,c}Bourneshell及其编程11.411.4Bourneshell及其编程1.继承的概念1.什么是异常特殊字符shell使用的特殊字符如下:(1)*,?,[],[!]:通配符(2)<,<<,>,2>,&>,>>,2>>,&>>:I/O重定向符(3)x>&y:标准流间重定向,比如2>&1,将标准错误重定向到标准输出(4)|:管道字符(5);:命令分隔符(6)&:命令后台执行(7)",',`:三种引号(8)\:转义字符(9)$:变量引用前导符(10){,},[,],(,):三对括号(11)~:波浪号扩展(为用户的家目录),与$HOME相同(12)&&,||:命令的与或结构及条件执行11.4Bourneshell及其编程1.继承的概念1.什么是异常变量与参数
1.位置参数当一个shell程序被调用时,shell隐含地为它建立一系列位置参数。这些参数按它们排列的位置被编号为0,1,…,其中0号参数为命令本身的名字,1号,2号,…等分别为它所携带的第一个参数,第二个参数,…。比如,在命令myprogfirstxyz123中,0号参数为程序名myprog,1号参数为first,2号参数为xyz,3号参数为123。Shell脚本程序的位置变量依次表示为$1,$2,…,而脚本程序名表示为$0。就上例而言,$0=myprog,$1=first,$2=xyz,$3=123,…位置参数可以很多,但能直接访问的只有$0~$910个。对第10个以后位置参数,需要使用${}的办法来处理,比如第11个位置参数为${11}。当命令行参数超过10个时,访问不便,可使用shift进行调整,将后边的前移。11.4Bourneshell及其编程1.继承的概念1.什么是异常变量与参数2.特殊变量变量意义变量意义$#传递给shell程序的变量个数$?上一条命令执行返回值$@“$@”为shell程序的一个个命令行参数;$@为一个个的单词$*“$*”为所有命令行参数由分隔符$IFS连接的单个字符串;$*为一个个单词$n第n个位置参数,n为小于$#的非负整数$-由set设置的参数串$$当前进程的进程号$!最后一次后台执行进程的pid说明:$@与$*的区别主要表现在对“$@”与“$*”的处理上,如果不考虑双引号问题,两者是相同的。11.4Bourneshell及其编程变量与参数3.变量的条件替换由于shell是解释语言,变量不需要声明,也不需要明显的初始化,但应该为变量的初次使用提供某种控制机制,于是就有了变量的条件替换。变量条件替换的形式如下所述。(1)${var:-val}:当变量var未被赋值时而访问时,取默认值val。(2)${var:=val}:当变量var未被赋值时而访问时,取默认值val,并把val赋给var。(3)${var:+val}:当var已赋值时,才取val的值。(4)${var:?message}:当变量已设置且不为空则正常替换,则否将message送到标准输出后退出shell程序。11.4Bourneshell及其编程shell的状态在任意时刻,shell都处于某种状态。这些状态包括各种参数、变量、执行方式和当前工作目录等,可以使用set命令来改变或显示shell的工作状态。1.工作目录及改变shell的工作目录可用cd命令来改变或设置,但在shell程序(包括C等语言)中,cd命令改变的目录只对当前进程及其子shell进程中有效。当子程序退出后,它改变的工作目录将被自动恢复到原来的位置,或者说,当前进程的父进程并不知道目录的改变。2.set与shell状态还有一些与shell程序执行相关的状态,它们可以通过set命令来设置。set的用法为:set[-abefhkmnptuvxBCEHPT][-ooption-name][--][-][arg...]set[+abefhkmnptuvxBCEHPT][+ooption-name][--][-][arg...]11.4Bourneshell及其编程shell的状态参数意义-a设置自动定义环境变量标志,之后定义或修改的变量或函数将作为环境变量-f取消路径通配符扩展-n只读取shell程序的命令做语法等检查而不真正执行,可用于测试shell程序是否正确-v执行过程中显示读入的行,可用于shell程序的跟踪与调试-xshell程序执行过程中,在命令被扩展后、执行前显示该命令及其参数的值,可用于shell程序的跟踪与调试--如果args不存在则取消所有环境变量,否则将args设置为新的环境变量+<参数>取消以-<参数>方式设置的参数11.4Bourneshell及其编程shell程序的调用与变量传递1.调用shell程序调用shell脚本程序,或从一个当前shell程序调用另一个shell程序时,可让被调用者在子shell或当前shell中执行,默认是在子shell执行。方法如下所述。(1)在子shell执行shell_progargs #未带路径,也可指定路径,比如:./shell_progargs(2)在当前shell执行.shell_progargs #使用点命令或sourceshell_progargs11.4Bourneshell及其编程shell程序的调用与变量传递(3)显式解释执行上述方法中调用的子shell程序是具有可执行权的,对于无执行权限且具有可执行内容的shell程序,可用下面方法来实现:shshell_progargs #使用sh或bashshell_progargs11.4Bourneshell及其编程shell程序的调用与变量传递2.向子shell程序传递变量当前shell的环境变量会自动传递给自己的子shell程序,但是自己定义的变量却不能自动传递到下一级,除非子shell程序是在当前shell执行的。若让在当前shell或进程定义的变量传递到下一级,就必须使用export等命令将其设为环境变量。11.4Bourneshell及其编程shell程序设计1.运算符传统bshell的算术运行符有+、−、\*、/、%,更多的其他运行符参见test和expr。在bash中,可通过$(())使用C语言的语法和运算符。2.常用命令1)::空命令。什么都不做,只有返回一个值0。2).shell_prog:在当前shell中执行脚本程序shell_prog。其同效命令为source。3)exit[n]:强迫shell退出[退出码为n]。4)return[n]:函数内强制返回[返回码为n]。5)readonly[arg1…]:将变量arg1等标识为只读,只读变量的值不能再被修改。6)break[n]:从for、while或until循环内跳出,n代表跳出的层数。7)continue[n]:从for、while或until循环的开始处执行,n代表循环的层数。8)times:显示本shell和其所执行的程序在系统和用户态执行的累计时间。9)timecmd[args]:执行命令cmd,并显示其在系统、用户态的执行时间和累计时间。11.4Bourneshell及其编程shell程序设计10)wait[n]:等待某个进程的结束,并报告它的结束状态。若不指定n,则所有子进程都将被等待。如果n为某个不存在的进程,则wait返回127,其他情况下,wait的返回值与被等待进程的返回值相同。11)dirname,basename:确定文件的路径名和基本名dirname和basename用于确定一个带有路径文件名的路径名和文件名,其用法为:dirnamenamesbasename[-a|-s,--suffix=suffix]namesnames为带有路径的文件名;-a用于多文件名处理,若不使用-a则只处理第一个;-s,--suffix=suffix用于在输出中删除扩展名。11.4Bourneshell及其编程shell程序设计dirname用于输出带有路径文件名的路径名,若没有路径则输出“.”,表示当前路径。basename用于输出带有路径文件名的纯文件名,若使用了-ssuffix或--suffix=suffix,则只输出文件的主名。示例如下:#dirname/usr/bin//usr/include/stdio.h #输出/usr和/usr/include#basename-a/usr/bin//usr/include/stdio.h #输出bin和stdio.h#basename-a-s.h/usr/bin//usr/include/stdio.h #输出bin和stdio11.4Bourneshell及其编程shell程序设计12)expr:用于对表达式进行运算expr的功能是计算表达式的值,并将结果显示到标准输出上。其用法为:exprEXPRexprOPTION #--help:用于帮助;--version:显示版本信息当expr操作的表达式EXPR既不为空也不为0时,返回0;为空或0时返回1;无效语句返回2,其他错误返回3。expr要求每个参数或运算元素之间必须用空格分隔,且一些操作符需要使用“\”进行转义。11.4Bourneshell及其编程shell程序设计expr命令的运算符类
型运
算
符功
能算术运算+、-加、减运算符\*、/、%乘、除、求余运算符逻辑运算ARG1|ARG2若ARG1非空亦非0则返回ARG1,否则返回ARG2ARG1&ARG2若ARG1和ARG2均非空亦非0则返回ARG1,否则返回0<<====!=>=>比较运算符。真时返回1,否则返回0,=与==意义相同字符串string:regex在string中匹配正则表达式regexmatchstringregex在string中匹配正则表达式regex*substrstringposlength在string中从pos开始取最多length个字符*indexstringchars在string中查找char的开始位置*lengthstring求string的长度*+token将token作为普通字符串,其中可能含有关键字或运算符*注:表中标有*的项目在早期的版本中不支持。11.4Bourneshell及其编程shell程序设计expr示例如下:①计算表达式"50*(40-25)"的值$expr50\*\(40-25\) #输出750②更新某个变量的值$num=1;num=`expr$num+5` #num的值6③模式匹配$exprabc:'a\(.\)c' #输出b$exprabc:'\(a.c\)' #输出abc$exprindex"WhoisMrShao"Mr #输出8$exprindex"WhoisMrShao"Shao #输出2$expr"$x":"^[[:digit:]]*$" #判断$x中是否全是数字:若是输出x的长度,否则为0$expr"$x":"^[+-]*[[:digit:]]*$" #判断$x是否是"[+-]数字"的形式:输出如上11.4Bourneshell及其编程shell程序设计13)eval[arg…]:从它的参数读内容,并将它们传送到shell执行,而eval再把shell的输出结果作为命令进行执行。例如:$cmd=ls;eval`echo$cmd` #执行ls$eval`id|awk'/uid=0/{print"isroot=T"}'` #动态定义变量isroot,其值为T$evalecho\$$# #输出最后一个命令行参数(若此为shell脚本的一句)$evalecho\$$(($#-1)) #输出倒数第二个命令行参数(若此为shell脚本的一句)11.4Bourneshell及其编程shell程序设计14)exec:在当前shell中执行命令,当前shell将被新的执行程序替换或覆盖。exec的用法为:exec[-cl][-aname][cmd[args]]若没有指定选项,则exec在当前shell内执行命令cmdargs;若指定-l则shell在向cmd传递第0号参数时,在它的前面加上一个“-”号。若指定-aname,则shell在向cmd传递环境变量时将把name作为cmd命令的第0号参数。若指定-c,则shell只是向cmd传递一个空环境。例如:#设有一个二进制可执行程序/usr/bin/DispAllParams专用来显示自己的全部命令行参数$exec-lDispAllParamsa1b2c3 #输出为:-DispAllParamsa1b2c3(窗口或界面被关闭)$exec-axyzDispAllParamsx1y2 #输出为:xyza1x1y2(窗口或界面被关闭)11.4Bourneshell及其编程shell程序设计15)shift:调整位置参数,其用法为:shift[n]shift[n]的功能为将第n+1,n+2,…变量的值变为$1,$2,…,n必须为大于0的整数,默认值为1。若n的值等于0或大于$#,命令执行失败,返回非零值。若执行成功则$#=$#-n。16)test:用于测试条件表达式的值。test命令主要用于测试某条件的返回值,成功或真时为0,不成功或假时为非0,其用法为:testconditiontest可用于以下4种条件的测试:两字符串间的关系;两整数间的关系;文件是否存在及属性;以上几种条件通过“与”或“或”的组合。11.4Bourneshell及其编程shell程序设计表字符串间关系测试
运算符意义示例str1=str2或str1==str2相等时为真x=abc;y=acd;$x=$y为假str1!=str2不等时为真x=abc;y=acd;$x!=$y为真str非空时为真x=abc;[$x]为真-nstr长度大于0时为真x=abc;-n$x为真-zstr长度等于0时为真x=abc;-z$x为假str1<str2排序后str1在str2前为真x=abc;y=acd;$x<$y为真str1>str2排序后str1在str2后为真x=abc;y=acd;$x>$y为假11.4Bourneshell及其编程shell程序设计②用于两整数间关系的运算符运算符意义示例-eq两数相等时为真x=1;y=2;$x-eq$y为假-ne两数不等时为真x=1;y=2;$x-ne$y为真-gt前数大于后数时为真x=1;y=2;$x-gt$y为假-ge前数大于或等于后数时为真x=1;y=2;$x-eq$y为假-lt前数小于后数时为真x=1;y=2;$x-lt$y为真-le前数小于或等于后数时为真x=1;y=2;$x-le$y为真11.4Bourneshell及其编程11.4.5shell程序设计③有关文件的测试表达式测试符意义测试符意义-rfile可读时为真-wfile可写时为真-xfile可执行时为真-ffile存在且为普通文件时为真-cfile字符设备文件时为真-bfile块设备文件时为真-dfile目录文件时为真-efile文件存在时为真-h/-L符号链接时为真-HfileSemaphore时为真-ufile设置suid时为真-gfile设置sgid时为真-kfile设置sticky时为真-pfile命名pipe时为真-sfile长度>0时为真-Mfile共享内存文件时为真-Gfile存在且被EGID拥有为真-Ofile存在且被EUID拥有为真-SfileSocket为真f1-otf2文件f1比f2旧时为真f1-eff2f1与f2在同设备且i节点号相同时为真f1-ntf2文件f1比f2新为真11.4Bourneshell及其编程shell程序设计④复杂条件测试。当测试语句中的条件不止一个时,可根据实际情况使用逻辑与(-a)、或(-o)、非(!)进行组合。例如:$test-sfile-a-rfile #如果文件长度>0且可读时为真$test$val-lt0-o$val-gt7 #如果变量val的值<0或>7时为真17)[]:另一种执行test命令的方法条件判断是经常用到的,因此test语句在shell程序设计时是非常有用的。但是,采用这种方法操作显得有些不大习惯,于是又引入了另一种既方便又直观的办法。具体做法是把判断条件语句放在一对方括号中,来替代test命令。若表达式中用于测试的条件不止一个,则两个用于测试的式子间要用另一种逻辑运算符来组合,它们是与(&&)、或(||)和非(!)。例如,上面的test测试命令,可以改为:$[-sfile]&&[-rfile] #如果文件长度>0且可读时为真$[$val-lt0]||[$val-gt7] #如果变量val的值<0或>7时为真11.4Bourneshell及其编程shell程序设计3.bash的其他扩展1)参数或变量条件扩展除了${var:-val},${var:=val},${var:+val}和${var:?val}外,bash又增加了下以扩展:${#var}:变量var值的长度。${var:off}、${var:off:len}:var中从off开始长度为len的子串。${var#val}、${var##val}:从var头部去除由模式val匹配的最小、最大部分,返回剩余部分。${var%val}、${var%%val}:从var尾部去除由模式val匹配的最小、最大部分,返回剩余部分。${var/pattern/str}、${var//pattern/str}:pattern为像文件扩展一样被扩展的匹配模式,var也被扩展,且将pattern在var中的最大匹配替换为str。前者仅进行首次匹配替换,后者将进行所有的匹配替换。如果pattern的首字符为#/%,则从行首/尾进行匹配和替换。当str为NULL时,删除匹配的串。如果var为*或@,则替换将在位置参数上依次进行。11.4Bourneshell及其编程shell程序设计2)“{}”扩展与路径名扩展bash还支持“{}”扩展,比如a{a,b,c,}d将被替换为aad,abd,acd,这种替换常用在命令行参数替换中。比如mkdira{1,2,3}dir将在当前目录内创建a1ddir、a2dir和a3dir目录。3)$'str'和$"str"与字符串扩展形如$'str'结构将会被扩展成一个字符串,其中的转义字符会按照ANSIC的标准被替换。c=$'\a' #\a为响铃,而非字符串'\a'str=$'\n\r' #str为回车换行,而非字符串'\n\r'形如$"str"结构会使字符串str根据当前locale来翻译。事实上,这种替换就可以理解为双引号内的替换11.4Bourneshell及其编程shell程序设计4)“(())”和“[[]]”“((expr))”对算术表达式expr求值,如果这个值不是零,则返回状态是零,否则返回1。比如:x=5;((y=x++));echo"x=$xy=$y" #x=6y=5“[[expr]]”对条件表达式expr求值,并据其结果返回0或者1。在“[[”和“]]”中间的内容不进行文件名扩展,但可进行“~”扩展、参数和变量扩展、算术扩展、命令替换、进程替换。如果使用了“==”或“!=”,则其右边会被看成一个模式,并且按照模式匹配规则进行匹配。11.4Bourneshell及其编程shell程序设计5)$((expression)):计算表达式expression的值这是一个比expr计算能力更强的工具,在expression中可以包括C语言除指针以外的几乎所有运算符。例如:$x=3;y=4;z=5;r=$((z**2-x*y));echo$r #结果为13$echo$((z**2-(x**2+y**2))) #结果为06)$(cmds):命令替换的另一种形式$(cmds)是命令替换的另一种形式,且比`cmds`具有更好的表现。等同于#tarcvfj/tmp/`whoami`.tbz2~/* #同上11.4Bourneshell及其编程shell程序设计7)进程替换进程替换的一般形式为:>(command)<(command)前者用于向命令command提供输出,后者用于从command得到输入。说明:在"<"或">"与圆括号“(”之间不能有空格。进程替换经常出现在文件名作为参数的地方。比如:#diff<(ls-a/home/test)<(ls-a/home/test1) #比较/home/test与/home/test1内容的差别#tar-cvf>(bzip2-c>/tmp/`whoami`.tbz2)~/* #将~/*打包备份为/tmp/`whoami`.tbz2,等同于#tarcvfj/tmp/`whoami`.tbz2~/* #同上11.4Bourneshell及其编程shell程序设计4.函数在shell编程时可以使用函数,但在使用之前函数必须是先定义好的,且必须出现在调用语句的前面,函数支持递归调用。函数定义时不必使用形式参数,对函数参数的引用如同在shell中使用位置参数一样。在函数体内的$1代表函数的第一个参数,$i代表函数的第i个参数,$#代表函数参数的个数,$*代表所有参数,$0不代表函数名,而是它所在的shell程序名。函数定义方法为:func_name(){ 函数体}函数调用方法为:func_nameparam1param2...函数被定义以后可以作为命令来使用。现在我们定义函数sysinfo(),如下所示。11.4Bourneshell及其编程shell程序设计可以像使用shell命令一样使用函数,方法是:$sysinfox1x2x3 #函数的使用。观察参数的内容,并注意输出中的$0$sysinfox1x2x3|wc #函数与shell命令的管道链接sysinfo(){echo"$0has$#param(s),theyare:$*"echo"Ihave$#param(s),andthe1'stparamis:$1"date+"%D%t%T"uname-s}11.4Bourneshell及其编程shell程序设计5.控制结构1)if-then-fi结构if-then-fi结构的语法为:iftest_cmdthen cmd_1 ...... cmd_nfi11.4Bourneshell及其编程shell程序设计在此结构中,if和then是两个语句,若写在同一行则必须用“;”分隔。上述结构可写为:iftest_cmd;thencmd_1;…;cmd_n;fi设计一个程序,带一个(目录)参数,实现功能:(1)首先判断是否带有一个参数,若无,则显示用法信息后报错返回1;(2)若带有参数,则首先判断该参数作为文件是否存在,若不存在,报错返回2;shell程序设计11.4Bourneshell及其编程假设脚本sh01.sh程序编辑完成,执行方法如下(以下各脚本处理方法相似,不再重复):①直接解释执行#!/usr/bin/sh#让系统调用bshell解释程序,此行不能直接跟在#!/bin/sh的后面if[$#-lt1] #判断是否有参数then #无参数echo-e"Usage:\n$0dir" #显示用法信息exit1 #返回1fiif[!-e"$1"];then #判断是否存在,若不存在返回2echo-e"$1doesn'texist!";exit2fiif[-d"$1"];then #判断是否为目录ls-a$1;exit0; #列目录内容,返回0fiecho"$1isnotadirectory" #警告信息exit3 #返回311.4Bourneshell及其编程shell程序设计假设脚本sh01.sh程序编辑完成,执行方法如下(以下各脚本处理方法相似,不再重复):①直接解释执行 $bashsh01.sh;echo$? #无参数执行(输出略,下同) $shsh01.sh$HOME;echo$? #家目录为参数②添加执行权后执行 $chmod+xsh01.sh #添加执行权 $./sh01.shsh01.sh;echo$? #文件名为参数11.4Bourneshell及其编程shell程序设计2)if-then-elif-else-fi结构if-then-elif-else-fi结构为: iftest_cmd then statements eliftest_cmd then statements else statements fi采用此结构,sh01.sh可改写为如下所示的sh02.sh:11.4Bourneshell及其编程shell程序设计
#!/usr/bin/bashif[$#-ne1];thenecho-e"Usage:\n$0dir";exit1elif[!-e"$1"];thenecho-e"$1doesn'texist!";exit2elif[-d"$1"];thenls-a$1;exit0elseecho"$1isnotadirectory";exit3fi说明:if-fi结构可以嵌套多层。11.4Bourneshell及其编程shell程序设计3)case结构case结构的格式为:casevarinval1) cmd11;…;cmd1n1;;val2) cmd21;…;cmd2n2;; ………………*) default_action;;esac注意:esac为case的倒序书写。11.4Bourneshell及其编程shell程序设计4)while结构while结构的格式如下:whileconditiondo do_list #命令或语句系列done11.4Bourneshell及其编程shell程序设计设计一个菜单程序,循环接收键盘输入,根据所输入的键来执行不同的功能,直到接收到0、q或Q键退出。要求捕获信号2。为了简化设计又能说明问题,这里提供3个功能:func1、func2和Quit。实现func1和func2的功能键分别“1”和“2”,对应函数分别为func1()和func2();用于退出的功能键为“0”、“q”或“Q”。设程序名为sh03.sh。#!/usr/bin/shfunc1(){ #定义菜单功能函数func1,用于完成功能1 echo"Thisisfunction1!"}func2(){ #定义菜单功能函数func2,用于完成功能2 echo"Thisisfunction2!"}trap""2 #捕获信号2,忽略^C#主程序whiletrue #循环接收用户输入do clear #清屏,然后显示菜单 echo-n"+----------------------------------------------+|1.func12.func20/q/Q.Exit|+----------------------------------------------+Pleasegetachoice:" readx #读键盘 case$xin 1) #处理键1,执行func1 func1;; 2)func2;; #处理键2,执行func2 0|Q|q)exit$?;; #处理0、q或Q:退出并返回上条命令执行结果 *)echo-e"\a";; #响铃表示非法输入 esac #case结构结束;esac为case的倒序书写 read-p"PressEntertocontinue:"x #读键盘暂停,供观察用。回车继续下一轮循环donesh03.sh只接收0、1、2、q和Q,输入其他键时响铃报警。11.4Bourneshell及其编程shell程序设计5)until结构until结构如下:untilconditiondo do_list #命令或语句系列done设计一个程序,计算前10个正整数的和。设程序名为sh04.sh,代码如下所示。#!/usr//bin/bashx=0;y=1 #x和y分别为和和控制变量,分别被初始化为0和1until[$y-gt10]do x=`expr$x+$y` #x=x+y y=`expr$y+1` #y=y+1doneecho"1+...+10=$x" #输出结果11.4Bourneshell及其编程shell程序设计6)for结构for结构如下:forvarinval1val2…do do_listdone设计一个程序,从命令行接收一些数值参数并计算这些参数的和。设程序名为sh05.sh,代码如下。#!//usr/bin/shif[$#-lt1];thenecho-e"Usage:\n$0Number...\n";exit1;fi#无参数时,显示用法信息x=0 #x为和变量,初始化为0foryin$* #遍历所有命令行参数do #没有判断参数的合法性,因为它们可能是非数值数据 x=`expr$x+$y` #累加x=x+ydone #do结构结束echo"TheSumis:$x" #输出结果11.4Bourneshell及其编程7)从循环中跳出或继续下一个循环从循环中跳出:break[n]继续下一个循环:continue[n]设计程序,计算前n个正整数的和,n由命令行输入。设程序名为sh06.sh,代码如下shell程序设计#!/bin/shif[$#-lt1];thenecho-e"Usage:\n$0Integer\n";exit1;fi#无参数时,显示用法信息x=0;y=1 #x和y分别为和与控制变量,分别被初始化为0和1whiletrue;do x=`expr$x+$y`;y=`expr$y+1` #x=x+y;y=y+1 if[$y-gt$1];thenbreak;fi #$1为命令行参数,要为正整数doneecho"1+...+$1=$x" #输出结果11.4Bourneshell及其编程shell程序设计8)使用与或结构有条件执行命令(1)与结构的使用与结构是用&&连接的命令序列:cmd1&&cmd2&&cmd3…在此结构中的语句是按从前到后的顺序执行的,若执行中某个命令执行失败(返回码不为0),则后面的命令不再执行。(2)或结构的使用或结构是用||连接的命令序列:cmd1||cmd2||cmd3…在此结构中,只有前面的命令执行失败(返回值非0),后面的命令才会得到执行,但若有一条命令执行成功了,后面的命令就不再执行。使用与或结构有条件执行命令的示例:[-fz]&&ls-liz||touchz其功能是,若文件z存在就长格式列出其文件名及属性,否则创建之。9)命令分组在shell中,若干个命令可以放在一个执行单元内组成命令组执行,组成命令组可用以下两种形式。(cmds):在子shell中执行。子shell执行的结果不影响当前shell程序变量的值。{cmds;}:在当前shell中执行。当前shell中执行的结果将影响当前shell的变量值。命令分组简单示例程序为sh07.sh。
#!/usr/bin/shN=2;printf"Before():N=%d|"$N;(a=2;b=3;N=`expr$a+$b`;printf"In():N=$N|")printf"After():N=$N\n" #结果为2N=2;printf"Before{}:N=%d|"$N;{a=2;b=3;N=`expr$a+$b`;printf"In{}:N=$N|";}printf"After{}:N=$N\n" #结果为511.4Bourneshell及其编程shell程序设计sh07.sh的输出结果如下,根据输出结果,可以比较(cmds)与{cmds;}的区别。Before():N=2|In():N=5|After():N=2Before{}:N=2|In{}:N=5|After{}:N=511.4Bourneshell及其编程shell程序设计10)递归shell支持递归调用。但由于shell程序及函数的返回值只有8位,故不能希望像C语言那样通过函数的返回值来实现递归计算数的阶乘等。如下所示的脚本程序sh08.sh代码为通过递归实现递归列目录的一种方法。#!/bin/shls_r(){forfin$1/*;do ls-d$f if[-d$f];then ls_r$f; #递归调用自己 fidone}if[$#-eq0-o!-e"$1"];thenecho-e"Usage:\n$0dir";exit1;fiif[-d"$1"];thenls_r$1 #列目录及其子目录elsels$1fi11.4Bourneshell及其编程shell程序设计6.shell编程综合示例示例1即时文档使用与系统用户查询使用即时文档编写一个shell程序,要求:①在即时文档内部进行参数或命令替换,显示操作者的用户名,并提示操作者输入一个用户名。②接收用户从键盘读入一个字符串(用户名),首先判断该输入内容是不是系统中已创建的用户,若不是则显示错误“Xisnotauser”并返回2;若是,再判断该用户是否已登录到系统中,若已登录则显示“Xislogined”,并返回0;否则输出“Xnotinsystemnow!”,并返回1(说明:X为用户的输入内容)。11.4Bourneshell及其编程shell程序设计设脚本程序名为sh09.sh。#!/usr/bin/shcat<<! #开始(首次)使用即时文档 Iam"`whoami`",pleasegivemeausername:!readx #读键盘输入的用户名if[-z"$x"];thenecho"Invalidusername";exit3;fi #判断$x是否为空grep-qw"$x"<<EOF #开始(第2次)使用即时文档`cut-d:-f1/etc/passwd` #获取所有用户或使用#`awk-F:'{print$1}'/etc/passwd` #功能同上一行EOFif[$?-ne0];thenecho"\"$x\"isnotauser";exit2;fi#不是一个用户who|awk'{print$1}'|grep"$x">/dev/nullif[$?-eq0];then #正在工作 echo"\"$x\"islogined!";exit0;else #未登录 echo"\"$x\"isnotinsystemnow!";exit1;fi11.4Bourneshell及其编程shell程序设计示例2为chroot做准备设计一个bshell程序,为chroot做准备(参见6.5.3)。具体的,对于chrootnewrootcmds的执行环境做自动化准备。这里,cmds为要切换根目录的命令(可以指定多个),newroot为cm
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年道法教学困惑问题及对策
- 四川省放射医学技术师专业知识考试试题
- 双重预防体系建设知识题库
- 苏教版科学小学五年级上册 期末测试卷12 (二)
- 苏教版二年级数学上册教案教学课题100以内的加法.减法
- 双减作业设计总结(3篇)
- 素质拓展周的心得体会8篇
- 苏教版小学五年级上册数学口算练习题
- 素质拓展活动总结
- 数字化新基建 2026年浙江省碳足迹管理SaaS种子轮融资商业计划
- 2026年广东省深圳市南山实验教育集团中考英语二检试卷
- 老年人志愿者服务与社会贡献
- 清华大学 -2026年OpenAIFDE研究报告
- 城市给水厂课程设计
- (2026年)急性缺血性脑卒中侧支循环评估与干预专家共识指南
- 2025年青岛旅游集团有限公司招聘笔试参考题库附带答案详解
- 观书有感其二其一课件
- 2025年征兵入伍心理测试试题及答案
- 危重患者急救处理课件
- 医院药品质量与安全管理
- 体态康复模板
评论
0/150
提交评论