Shell 编程
Shell 概述
Shell的概念最初是在Unix操作系统中形成和得到广泛应用的。Unix的Shell有很多种类,Linux系统继
承了Unix系统中Shell的全部功能,现在默认使用的是bash。
1.Shell 的特点
Shell具有如下突出特点:
(1)把已有命令进行适当组合构成新的命令。
(2)提供了文件名扩展字符(通配符,如* 、?、[ ]),使得用单一的字符串可以匹配多个文件名,省去键
入一长串文件名的麻烦。
(3)可以直接使用Shell的内置命令,而不需创建新的进程,如Shell中提供的cd、echo、exit、pwd、kill等命令。
为防止因某些Shell不支持这类命令而出现麻烦,许多命令都提供了对应的二进制代码,从而也可以在新进程中
运行。
(4)Shell允许灵活地使用数据流,提供通配符、输入/输出重定向、管道线等机制,方便了模式匹配、I/O处理
和数据传输。
(5)结构化的程序模块,提供了顺序流程控制、条件控制、循环控制等。
(6)Shell提供了在后台执行命令的能力。
(7)Shell提供了可配置的环境,允许创建和修改命令、命令提示符和其它的系统行为。
(8)Shell提供了一个高级的命令语言,能够创建从简单到复杂的程序。这些Shell程序称为Shell脚本,利用Shell
脚本,可把用户编写的可执行程序与Unix命令结合在一起,当作新的命令使用,从而便于用户开发新的命令。
2.常用Shell 类型
Linux系统提供多种不同的Shell以供选择。常用的有Bourne Shell(简称sh)、C-Shelll(简称csh)、
Korn
Shell(简称ksh)和Bourne Again Shell (简称bash)。
(1)Bourne Shell是AT&T Bell实验室的 Steven Bourne为AT&T的Unix开发的,它是Unix的默认Shell,也
是其它Shell的开发基础。Bourne Shell在编程方面相当优秀,但在处理与用户的交互方面不如其它几种Shell。
(2)C Shell是加州伯克利大学的Bill Joy为BSD Unix开发的,与sh不同,它的语法与C语言很相似。它提
供了Bourne Shell所不能处理的用户交互特征,如命令补全、命令别名、历史命令替换等。但是,C Shell与
BourneShell并不兼容。
(3)Korn Shell是AT&T Bell实验室的David Korn开发的,它集合了C Shell和Bourne Shell的优点,并且
与Bourne Shell向下完全兼容。Korn Shell的效率很高,其命令交互界面和编程交互界面都很好。
(4)Bourne Again Shell (即bash)是自由软件基金会(GNU)开发的一个Shell,它是Linux系统中一个默认
的Shell。Bash不但与Bourne Shell兼容,还继承了C Shell、Korn Shell等优点。
3. Shell 脚本的建立
Shell程序可以存放在文件中,这种被Shell解释执行的命令文件称为Shell脚本 (Shell scrīpt)。Shell
脚本可以包含任意从键盘键入的Linux命令。
建立Shell脚本的步骤同建立普通文本文件的方式相同,利用编辑器(如vi)进行程序录入和编辑加工。
例如,要建立一个名为ex1的Shell脚本,可在提示符后输入命令:
$ vi ex1
进入vi的插入方式后,就可录入程序行。完成编辑之后,将编辑缓冲区内容写入文件中,返回到Shell命
令状态。
4.执行Shell 脚本的方式
执行Shell脚本的方式基本上有三种:
(1)输入定向到Shell脚本
这种方式是用输入重定向方式让Shell从给定文件中读入命令行并进行相应处理。其一般形式是:
$ bash < 脚本名
例如:$ bash
Shell从文件ex1中读取命令行,并执行它们。当Shell到达文件末尾时就终止执行,并把控制返回到Shell
命令状态。此时,脚本名后面不能带参数。
(2)以脚本名作为参数
其一般形式是:
$ bash 脚本名[参数]
PDF 文件使用 "pdfFactory Pro" 试用版本创建
例如:$ bash ex2 /usr/meng /usr/zhang
其执行过程与上一种方式一样,但这种方式的好处是能在脚本名后面带有参数,从而将参数值传递给程
序中的命令,使一个Shell脚本可以处理多种情况,就如同函数调用时可根据具体问题给定相应的实参。
如果以目前Shell(以·表示)执行一个Shell脚本,则可以使用如下简便形式:
$ · 脚本名[参数]
以Shell脚本作为Shell的命令行参数,这种方式可用来进行程序调试。
(3)将Shell脚本的权限设置为可执行,然后在提示符下直接执行它。
通常用户是不能直接执行由正文编辑器(如vi)建立的Shell脚本的,因为直接编辑生成的脚本文件没有
“执行”权限。如果要把Shell脚本直接当作命令执行,就需要利用命令chmod 将它置为有“执行”权限。例如,
$ chmod a+x ex2
就把Shell脚本ex2置为对所有用户都有“执行”权限。然后,在提示符后输入脚本名ex2就可直接执行该
文件。
注意,此时该脚本所在的目录应被包含在命令搜索路径(PATH)中。例如:
$ ex2
Shell接收用户输入的命令(脚本名),并进行分析。如果文件被标记为可执行的,但不是被编译过的程
序,Shell就认为它是一个Shell脚本。Shell将读取其中的内容,并加以解释执行。所以,从用户的观点看,执
行Shell脚本的方式与执行一般的可执行文件的方式相似。因此,用户开发的Shell脚本可以驻留在命令搜索路
径的目录之下(通常是“/bin”、“/usr/bin”等),像普通命令一样使用。这样,也就开发出自己的新命令。
如果打算反复使用编好的Shell脚本,那么采用这种方式就比较方便。
Shell脚本经常被用来执行重复性的工作,例如,当进入系统时要查看有无信件、列出谁在系统中、将工
作目录改到指定目录并予以显示、印出当前日期等。完成这些工作的命令是固定的。为了减少录入时间,可把
这些命令建立在一个Shell脚本中,以后每次使用该文件名就可执行这些工作。
另外,完成某些固定工作时需输入的命令很复杂,例如文件系统的安装(mount),要带多个选项和参数。
此时,利用Shell脚本存放该命令,以后用时就很方便了。
Shell 变量
Shell中也采用变量,用来存放字符串。Shell变量比C语言中的变量简单得多,没有众多存储类及类型的
限制,也不需要预先定义、然后才能赋值,可以在使用时“现定义、现赋值”。
Shell有环境变量和临时变量。环境变量是永久性变量,其值不会随Shell脚本执行结束而消失。临时变
量是在Shell程序内部定义的,其使用范围仅限于定义它的程序,出了本程序就不能再用它;而且当程序执行完
毕,它的值也就不存在了。
1 .用户定义的变量
用户定义的变量是最普通的Shell变量。变量名是以字母或下线符打头的字母、数字和下线符序列,并且
大小写字母意义不同。如dir与Dir是不同的变量。这与C语言中标识符的定义相同。变量名的长度不受限制。
定义变量并赋值的一般形式是:
变量名=字符串
例如:
myfile=/usr/meng/ff/m1.c
注意,在赋值语句中,赋值号“=”的两边没有空格,否则在执行时会引起错误。
变量的值可以改变,只须利用赋值语句重新给它赋值即可。一个未明确赋过值的变量仅含一个空字符串。
在程序中使用变量的值时,要在变量名前面加上一个符号“$”。例如,
$ dir=/usr/meng/ff
$ echo $dir
/usr/meng/ff ←——— 显示结果
$ echo dir
dir ←——— 显示结果
如果在赋给变量的值中要含有空格、制表符或换行符,那么,就应该用双引号把这个字符串括起来。例
如,
names="Zhangsan Lisi Wangwu"
可以将一个命令的执行结果赋值给变量。有两种形式的命令替换:一种是使用倒引号引用命令,其一般
形式是
: `命令表`。
PDF 文件使用 "pdfFactory Pro" 试用版本创建
例如:将当前工作目录的全路径名存放到变量dir中,输入以下命令行:
$ dir=`pwd`
另一种形式是:$(命令表)。上面的命令行也可以改写为:
$ dir=$(pwd)
2 .数组
bash只提供一维数组,并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数
组中的元素要利用下标。下标可以是整数或算术表达式,其值应大于或等于0。用户可以使用赋值语句对数组变
量赋值。对数组元素赋值的一般形式是:数组名[下标]=值,例如:
$ city[0]=Beijing
$ city[1]=Shanghai
$ city[2]=Tianjin
$
也可以用declare命令显式声明一个数组,一般形式是:
declare -a 数组名
读取数组元素值的一般格式是:
${数组名[下标]}
例如:
$ echo ${city[0]}
Beijing
一个数组的各个元素可以利用上述方式一个元素一个元素地赋值,也可以组合赋值。定义一个数组并为
其赋初值的一般形式是:
数组名=(值1 值2 ... 值n)
其中,各个值之间以空格分开。
例如:
$ A=(this is an example of shell script)
$ echo ${A[0]} ${A[2]} ${A[3]} ${A[6]}
this an example scrīpt
$ echo ${A[8]}
$
由于值表中初值共有7个,所以A的元素个数也是7。A[8]超出了已赋值的数组A的范围,就认为它是一个
新元素,由于预先没有赋值,所以它的值是空串。
若没有给出数组元素的下标,则数组名表示下标为0的数组元素,如city就等价于city[0]。
使用*或@做下标,则会以数组中所有元素取代[*]或[@]。
3 .变量引用
除了上面所介绍的变量引用方式外,在bash中还有其它的引用方式。归纳起来,有效的变量引用表达式
有以下形式:
$name ${name#pattern}
${name} ${name##pattern}
${name[n]} ${name % pattern}
${name[*]} ${name %% pattern}
${name [@]} ${#@}
${name:-word} ${$#*}
${name:=word} ${# name }
${name:?word} ${# name[*]}
${name:+word} ${#name[@]}
(1)表达式$name表示变量name的值,若变量未定义,则用空值替换。
(2)表达式${name}将被变量name的值替换。用花括号括起name,目的在于把变量名与后面的字符分隔
开,避免出现混淆。替换后花括号被取消。
(3)${name[n]}表示数组变量name中第n个元素的值。
(4)表达式${name[*]}和${name[@]}都表示数组name中所有非空元素的值,每个元素的值用空格分开。
如果用双引号把它们都括起来,那么二者的含义就有区别:对于"${name[*]}",它被扩展成一个词(即字符串),
PDF 文件使用 "pdfFactory Pro" 试用版本创建
这个词由以空格分开的各个数组元素组成;对于"${name[@]}",它被扩展成多个词,每个数组元素是一个词。
如果数组name中没有元素,则${name[@]}被扩展为空串。
(5)表达式${name:-word}、${name:=word}、${name:+word}、${name:?word}的计算方法在下面介绍。
(6)表达式${name#pattern}和${name##pattern},如果pattern(表示匹配模式)与name值的开头匹配,
那么name的值去掉匹配部分后的结果就是该表达式的值;否则, name的值就是该表达式的值。在第一种格式中,
name值去掉的部分是与pattern匹配的最少的部分;而第二种格式中,name值去掉的部分是与pattern匹配的最
多的部分。
(7)表达式${name % pattern}和${name %% pattern},如果pattern与name值的末尾匹配,那么name的
值中去掉匹配部分后的结果就是该表达式的值;否则,该表达式的值就是name的值。在第一种格式中,去掉的
部分是最少的匹配部分;而第二种格式中,去掉的部分是最多的匹配部分。
(8)表达式${#@}和${#*}的值分别是由$@和$*返回的参数的个数。
(9)表达式${#name[i]}的值是数组name第i个元素值的长度(字符个数)。
(10)表达式${#nane[*]}和${#name[@]}的值都是数组name中已经设置的元素的个数。
4 .交互输入变量值
利用read命令可以从键盘上读取数据,然后赋给指定的变量。read命令的一般格式是:
read 变量1 [ 变量2 ?]
例如:
read a b c
输入数据时,数据间以空格或制表符作为分隔符。如果变量个数与给定数据个数相同,则依次对应赋值;
如果变量个数少于数据个数,则从左至右对应赋值,但最后一个变量被赋予剩余的所有数据;如果变量个数多
于给定数据个数,则依次对应赋值,而没有数据与之对应的变量取空串。
5 .位置参数
执行Unix/Linux命令或Shell 脚本时可以带有实参。相应地,在Shell脚本中应有变量。执行Shell程序
时,用实参去替代这些变量。在Shell脚本中这类变量的名称很特别,分别是0、1、2??这类变量称作位置变量,
因为它们与命令行上具体位置的实参相对应:命令名(脚本名)对应位置变量0,第一个实参对应位置变量1,
第二个实参对应位置变量2??如果位置变量是由两个或更多个数字构成,那么,必须把它们用一对花括号括起来,
如{10}、{11}。命令行实参与脚本中位置变量的对应关系。
例如:
$ set `pwd;ls;date`
$ echo $1 $2 $3 $9 ${10} ${11}
/home/mengqc bash_1 ex1 12 3 21:52:32
如果在脚本中使用的位置参数不超过9个,那么只用$1~$9即可。但是,实际给定的命令行参数有可能多
于9个,此时就需要用shift命令移动位置参数。每执行一次shift命令,就把命令行上的实参向左移一位,即相
当于位置参数向右移动一个位置。可以看出,shift命令执行后新$1的值是原$2的值,新$2的值是原$3的值,依
此类推。
shift命令不能将$0移走,所以经shift右移位置参数后, $0的值不会发生变化。
6. 预先定义的特殊变量
在Shell中,预先定义了几个有特殊含义的Shell变量,它们的值只能由Shell根据实际情况进行赋值,而
不能通过用户重新设置。下面给出这些特殊变量的表示形式及意义:
$# 命令行上实际参数的个数,但不包含Shell脚本名。
$?上一条命令执行后的返回值(也称作“退出码”)。它是一个十进制数。多数Shell命令执行成功时,
则返回值为0;如果执行失败,则返回非0值。
$$ 当前进程的进程号。
$! 上一个后台命令对应的进程号,这是一个由1~5位_________数字构成的数字串。
$- 由当前Shell设置的执行标志名组成的字符串。例如:
set -xv
这个命令行给Shell设置了标志-x和-v(用于跟踪输出)。
$* 表示在命令行中实际给出的所有实参字符串,它并不仅限于9个实参。
$ @ 它与$*基本功能相同,但“$@”与“$*”不同。
7 .环境变量
Linux环境(也称为Shell环境)由许多变量及这些变量的值组成。这些变量和变量的值决定了用户环境
PDF 文件使用 "pdfFactory Pro" 试用版本创建
的外观。注意,如果要使用环境变量或其它Shell变量的值,必须在变量名之前加上一个“$”符号,不能直接
使用变量名。
常用的环境变量如下:
HOME 用户主目录的全路径名。
LOGNAME 即注册名,由Linux自动设置。它是与系统交互的名字或字符串。
MAIL 系统信箱的路径。
PATH 查找命令的目录列表。PATH变量包含带冒号分界符的字符串,这些字符串指向含有所使用命令的目
录。
PS1 Shell的主提示符。bash默认的主提示符一般为“\s-\v\$ ”。其中,\s表示Shell的名称;\v表示
bash的版本号。当然,也可以随意设置PS1的值,例如:
PS1="Enter Command> "
则主提示符改成“Enter Command> ”。
PWD 当前工作目录的路径,它指出目前在Linux文件系统中处在什么位置。
SHELL 当前使用的Shell,它也指出Shell解释程序放在什么地方。
TERM 终端类型。
可以使用unset命令删除一个环境变量(如NAME):
$ unset NAME
可以创建一个新的环境变量,或者改变一个已有的环境变量的值,其形式与设置一般变量相同。如果变
量值的
字符串中带有空格等特殊字符,需要用引号把整个字符串括起来。利用export命令可以将这些变量导出,
使它们成为公用量,如:
export HOME HZ LOGNAME TERM
可以利用env命令列出所有的环境变量,包括本进程及以前的“祖先进程”所导出的变量。
8 .参数置换变量
参数置换变量是另一种为变量赋值的方式,其一般形式是:
变量2=$ {变量1 op 字符串}
其中,op表示操作符,它可以是下列四个操作符之一“: :-”、“: =” 、“: +” 和“: ?”。变量
2的值取决于变量1(参数)是否为空串、利用哪个操作符及字符串的取值。在操作符的前后不留空格。
命令历史
bash提供了命令历史功能,即系统为每个用户维护一个命令历史文件(~/.bash_history),它在注册用户
的主目录(用~表示)之下。该文件由编号的表格构成。每当注册后,用户输入命令并执行它时,该命令就自动
地加到这个命令历史表中。
使用命令历史机制,用户可以方便地调用或者修改以前的命令,可以把全部或部分先前命令作为新命令
予以快捷执行。这一功能称为历史替换(history substitution)。
◆ history命令可以显示命令历史表中的命令。其语法格式是:
history [option][arg ?]
(1)如果不带任何参数,则history命令会显示历史命令的清单(包括刚输入的history命令)。所有这些
命令都被称作事件,一个事件表示一个操作已经发生,即一个命令已被执行。显示的各行命令之前的数字表示
相应命令行在命令历史表中的序号,称为历史事件号。历史事件号从1开始顺序向下排,最后执行的命令的事件
号最大。
(2)如果history 后给出一个正整数,例如:
history 50
那么,就只显示历史表中的最后50行命令。
(3)如果history后给出一个文件名,例如:
history al
那么,就把al作为历史文件名。
◆ 执行历史命令是命令替换之一,它以字符“!”开头、后随1个或多个字符来定义用户所需的某种类型
的历史命令。它可以出现在输入行的任何地方,除非已在进行历史替换。如果在“!”之前加上反斜线“\”,
或者在其后跟着空格、制表符、换行符、等号“=”或开括号“(”,那么“!”就作为普通字符对待,失去特
殊意义。
历史替换可以作为输入的命令行的一部分或全体。当输入行的正文中包含历史替换时,将在完成相应的
PDF 文件使用 "pdfFactory Pro" 试用版本创建
替换后,在终端上显示输入的命令行,从而用户可以看到实际执行的命令(显示命令后立即执行)。
◆ 在默认方式下,bash使用用户主目录下面的文件“.bash_history”来保存命令历史。但是,用户也可
以通过重新为环境变量HISTFILE赋值来改变存放历史命令的文件。例如:
$ HISTFILE="/home/mengqc/.myhistory"
历史文件中能够保留的命令个数有限,其默认值是500。如果用户输入的命令太多,超过限定值,那么最
早输入的命令就会从历史表中删除,而新输入的命令会加到该表尾部。用户可以利用HISTSIZE变量重新设定该
值。例如:
$ HISTSIZE=600
通常用户不必对命令历史表进行管理(如设置HISTSIZE的值等),由系统自动管理。利用命令历史功能,
用户可以对先前输入的命令重新进行编辑、修改和执行,从而简化用户的操作。
别名
使用别名可以简化输入,方便用户。对于常用的选项或参数较多的固定命令采用别名替换,既缩短击键
次数,又减少出错机率。
定义别名要使用Shell内部命令alias,其一般语法格式为:
alias [name[=value]]?
如果没有指定参数,将在标准输出(屏幕)上显示别名清单,其格式为name=value,其中name是用户(或系
统)定义的别名名称,value是别名所代表的内容。注意,在赋值号“=”两边不能有空格。例如:
$ alias ll=' ls -l '
定义了别名ll,它代表“ls -l”。当输入ll命令后,Shell将寻找它们所维护的别名表(放在内存中的一
个内部表格)。若在该表中找到命令行的第一个字段(即“ll”)时,该命令就会被别名定义的内容所替换。定义
别名时,往往用单引号将它代表的内容括起来,从而防止Shell对其中的内容产生歧义,如对空格和特殊字符另
作解释。
如果想取消先前定义的别名,可使用如下命令:
unalias name?
执行后,就从别名表中删除由name指定的别名。
unalias也可以在一个命令上同时取消多个别名的定义,只须在unalias之后依次列出要取消的别名名称。
也可以一次将所有的别名都从别名表中删除,使用如下命令:
unalias -a
Shell 特殊字符
Shell中除使用普通字符外,还使用了一些特殊字符,它们有特定的含义,如通配符“*”和“?”、管
道线(|)及单引号、双引号等。在使用时应注意它们表示的意义和作用范围。
1 .一般通配符
通配符用于模式匹配,如文件名匹配、路径名搜索、字符串查找等。常用的通配符有四种:
* 匹配任意字符0次或多次出现。例如,f*可以匹配以f 打头的任意字符串。但应注意,文件名前面的圆
点( . ) 和路径名中的斜线( / )必须显式匹配。
? 匹配任意一个字符,例如,f ?匹配f1、fa、fb等,但不能匹配 f 、fabc、 f12等。
[ ] 其中有一个字符组,它匹配该字符组所限定的任何一个字符。该字符组可以由直接给出的字符组
成,也可以由表示限定范围的起始字符、终止字符及中间一个连字符(-)组成。例如,f[a-d]与f[abcd]作用
相同。
! 表示不在一对方括号中所列出的字符。例如,f[!1—9].c 表示以f打头,后面一个字符不是数字1至9
的.c文件名,它匹配fa.c、fb.c、fm.c等。在一个正则表达式中,可以同时使用“*”和“?”。
2 .模式表达式
模式表达式是那些包含一个或多个通配符的字。bash除支持一般通配符外,还提供了特有的扩展模式匹
配表达式,其形式和含义如下:
(1)*(模式表) 匹配给定模式表中“模式”的0次或多次出现,各模式之间以“|”分开。例如,file*(.c|.o)
将匹配文件file、file.c、file.o、file.c.c、file.0.0、file.c.o、file.o.c等,但不匹配file.h或file.s
等。
(2)+(模式表) 匹配给定模式表中“模式”的1次或多次出现,各模式之间以“|”分开。例如,file+(.c
| .o)匹配文件file.c、file.o、file.c.o、file.c.c等,但不匹配file。
(3)?(模式表) 匹配模式表中任何一种“模式”的0次或1次出现,各模式之间以“|”分开。例如,
file?(.c|.o)只匹配file、file.c和file.0,它不匹配多个模式或模式的重复出现,即不匹配file. c. c、file.
PDF 文件使用 "pdfFactory Pro" 试用版本创建
c. 0等。
(4)@(模式表) 仅匹配模式表中给定“模式”的一次出现,各模式之间以“|”分开。例如,file@(.c|.0)
匹配file.c和file.0,但不匹配file、file.c.c、file.c.o等。
(5)!(模式表) 除给定模式表中的一个“模式”之外,它可以匹配其它任何东西。
可以看出,模式表达式的定义是递归的,每个表达式中都可以包含一个或多个模式。例如
file*( .[cho]|.sh)是合法的模式表达式。但在使用时应注意,由于带“*”和“+”的表达式可以匹配给定模
式的组合,若利用此种表达式去删除文件就存在危险,有可能误将系统配置文件删除。因此,必须小心使用。
3 .引号
在Shell中引号分为三种:单引号、双引号和倒引号。
(1)双引号
由双引号括起来的字符,除$、倒引号(`)和反斜线(\)仍保留其特殊功能外,其余字符均作为普通字符
对待。
“$”表示变量替换,即用其后指定的变量的值来代替$和变量;倒引号表示命令替换;仅当“\”后面的
字符是下述字符之一时,“\”才是转义字符,这些字符是:“$”、“`”、“"”、“\”或换行符。转义字符
告诉Shell不要对其后面的那个字符进行特殊处理,只是当作普通字符。例如:
$ echo "My current dir is `pwd` and logname is $LOGNAME"
My current dir is /home/mengqc and logname is mengqc
(2)单引号
由单引号括起来的字符都作为普通字符出现。例如,
$ echo 'The time is ` date ` , the file is $HOME/abc '
The time is ` date ` , the file is $HOME/abc
(3)倒引号
倒引号括起来的字符串被shell解释为命令行,在执行时,Shell会先执行该命令行,并以它的标准输出
结果取代整个倒引号部分。在前面示例中已经见过。例如,
$ echo current directory is ` pwd `
current directory is /home/mengqc
4 .注释
Shell程序中以“#”开头的正文行表示注释。例如:
#!/bin/bash
# If no arguments, then listing the current directory.
# Otherwise, listing each subdirectory.
if test $# = 0
then ls ·
else
for i
do
ls -l $i | grep '^d'
done
fi
上面程序由if语句构成,其中else部分是for循环语句。其功能是检测位置参数个数,若等于0,则列出
当前目录本身;否则,对于每个位置参数显示其所包含的子目录。
上面代码中,第一行#!/bin/bash表示下面的脚本是用bash编写的,必须调用bash程序对它解释执行。后
面两行以“ # ”开头,表示这是注释行。注释行可用来说明程序的功能、结构、算法和变量的作用等,增加程
序的可读性。在执行时Shell将忽略注释行。
命令执行操作符
◆多条命令可以在一行中出现。它们可以从左到右顺序执行。此时,各条命令之间应以分号( ;)隔开,
如:
pwd ; who | wc -l ; cd /usr/bin
◆在相邻命令间可存在逻辑关系,即逻辑“与”和逻辑“或”。
逻辑与操作符“&&”可把两个命令联系在一起,其一般形式:
命令1 && 命令2
PDF 文件使用 "pdfFactory Pro" 试用版本创建
其功能是先执行命令1,如果执行成功,才执行命令2;否则,若命令1执行不成功,则不执行命令2。例
如:
cp ex1 ex10 && rm ex1
如果成功地把文件ex1拷贝到文件ex10中,则把ex1删除。
逻辑或操作符“||”可把两个命令联系起来,其一般形式是:
命令1 || 命令2
其功能是先执行命令1,如果执行不成功,则执行命令2;否则,若命令1执行成功,则不执行命令2。例
如:
cat abc || pwd
如果不能将文件abc的内容列出来,则显示当前工作目录的路径。
◆在Shell中有两种方式可以将若干命令组合在一起,使其在逻辑上被视为一条命令,它们是用花括号{ }
将各命令括起来和用圆括号( )括起来。
以花括号括起来的全部命令可视为语法上的一条命令,出现在管道符的一边。成组命令的执行顺序是根
据命令
出现的先后次序,由左至右执行。在管道线中,成组命令把各命令的执行结果汇集在一起,形成一个输
出流,这个流作为该管道线中下一个命令的输入。例如,
{ echo "User Report for ` date ` . "; who ; } | pr
使用花括号时在格式上应注意:左括号“{ ”后面应有一个空格;右括号“}”之前应有一个分号( ;)。
成组命令也可以用圆括号括起来。例如:
(echo "Current directory is ` pwd ` . "
cd /home/mengqc ; ls -l ;
cp m1 em1 && rm m1
cat em1) | pr
如上所示,在用圆括号括起成组命令时,左括号后不必有空格,右括号之前也不需加上分号。
二者执行过程相同,但是存在重要区别:用花括号括起来的成组命令只是在本Shell内执行命令表,不产
生新的进程;而用圆括号括起来的成组命令是在新的子Shell内执行,要建立新的子进程。因此,在圆括号内的
命令不会改变父Shell的变量值及工作目录等。
算术运算
bash中执行整数算术运算的命令是let,其语法格式为:
let arg ?
其中,arg是单独的算术表达式。这里的算术表达式使用C语言中表达式的语法、优先级和结合性。除++、
--和逗号(,)之外,所有整型运算符都得到支持。此外,还提供了方幂运算符“**”。命名的参数可以在算术
表达式中直接利用名称访问,不要前面带有“$”符号。当访问命名参数时,就作为算术表达式计算它的值。算
术表达式按长整数进行求值,并且不检查溢出。当然,用0作除数就产生错误。
let 命令的替代表示形式是:
((算术表达式))
例如,let ″j=i*6+2″等价于((j=i*6+2))。
如果表达式的值是非0,那么返回的状态值是0;否则,返回的状态值是1。
当表达式中有Shell的特殊字符时,必须用双引号将其括起来。例如,let ″val=a|b″。如果不括起来,
Shell会把命令行let val=a|b中的“|”看成管道符,将其左右两边看成不同的命令,因而无法正确执行。
控制结构
Shell具有一般高级程序设计语言所具有的控制结构和其它的复杂功能,如if语句、 case语句、循环结
构、函数
等。其实在Shell中,这些控制结构也称作“命令”。为了程序设计的习惯,才把它们称作语句。
1.if 语句
if语句用于条件控制结构中,其一般格式为:
if 测试条件
then 命令1
else 命令2
fi
其中,if、then、else和fi是关键字。例如:
PDF 文件使用 "pdfFactory Pro" 试用版本创建
if test -f "$1"
then echo "$1 is an ordinary file . "
else echo "$1 is not an ordinary file . "
fi
应该注意,if语句中else部分可以缺省。另外,if 语句的else部分还可以是else—if结构,此时可以用
关键字“elif”代替“ else if”。
通常,if的测试部分是利用test命令实现的。其实,条件测试可以利用一般命令执行成功与否来作判断。
如果命令正常结束,则表示执行成功,其返回值为0,条件测试为真;如果命令执行不成功,其返回值不等于0,
条件测试就为假。所以if的语句的更一般形式是:
if 命令表1
then 命令表2
else 命令表3
fi
2 .条件测试
条件测试有三种常用形式:一种是用test命令,如上所示。另一种是用一对方括号将测试条件括起来。
这两种形式是完全等价的。例如,测试位置参数$1是否是已存在的普通文件,可写为test -f "$1"。也可写成[ -f
"$1" ]。利用一对方括号表示条件测试时,在左方括号“[”之后、右方括号“]”之前各应有一个空格。
第三种形式是:
[[条件表达式]]
test命令可以和多种系统运算符一起使用。这些运算符可以分为四类:文件测试运算符(文件的属性及
权限等)、字符串测试运算符(两个串是否相同及是否为空)、数值测试运算符(大小关系)和逻辑运算符(逻
辑与、或、非)。
3.case 语句
case语句允许进行多重条件选择。其一般语法形式是:
case 字符串 in
模式字符串1) 命令
?
命令;;
模式字符串2) 命令
?
命令;;
?
模式字符串n) 命令
?
命令;;
esac
其执行过程是用“字符串”的值依次与各模式字符串进行比较,如果发现同某一个匹配,那么就执行该
模式字符串之后的各个命令,直至遇到两个分号为止。如果没有任何模式字符串与该字符串的值相符合,则不
执行任何命令。
在使用case语句时应注意:
(1)每个模式字符串后面可有一条或多条命令,其最后一条命令必须以两个分号(即;;)结束。
(2)模式字符串中可以使用通配符。
(3) 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表示各模式是“或”
的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令表。
(4)各模式字符串应是惟一的,不应重复出现。并且要合理安排它们的出现顺序。例如,不应将“*”
作为头一个模式字符串,因为“*”可以与任何字符串匹配,它若第一个出现,就不会再检查其它模式了。
(5)case语句以关键字case开头,以关键字esac(是case倒过来写!)结束。
(6)case的退出(返回)值是整个结构中最后执行的那个命令的退出值。若没有执行任何命令,则退出
值为零。
4. while 语句
Shell中有三种用于循环的语句,它们是while语句、for语句和until语句。
PDF 文件使用 "pdfFactory Pro" 试用版本创建
while语句的一般形式是:
while 测试条件
do
命令表
done
其执行过程是,先进行条件测试,如果结果为真,则进入循环体(do—done之间部分), 执行其中命令;
然后再做条件测试??直至测试条件为假时才终止while语句的执行。例如:
while [ $1 ]
do
if [ -f $1 ]
then echo "display : $1 "
cat $1
else echo "$1 is not a file name . "
fi
shift
done
这段程序对各个给定的位置参数,首先判断其是否是普通文件,若是,则显示其内容;否则,显示它不
是文件名的信息。每次循环处理一个位置参数$1,利用shift命令可把后续位置参数左移。
测试条件部分除使用test命令或等价的方括号外,还可以是一组命令。根据其最后一个命令的退出值决
定是否进入循环体执行。
5.until 语句
until语句的一般形式是:
until 测试条件
do
命令表
done
它与while语句很相似,只是测试条件不同:当测试条件为假时,才进入循环体,直至测试条件为真时终
止循环。
6.for 语句
for语句是最常用的建立循环结构的语句。其使用格式主要有三种,取决于循环变量的取值方式。
格式一:
for 变量 in 值表
do
命令表
done
例如:
for day in Monday Wednesday Friday Sunday
do
echo $day
done
其执行过程是,变量day依次取值表中各字符串,即第一次将“Monday”赋给day,然后进入循环体,执
行其中的命令,显示出Monday。第二次将“Wednesday”赋给day,然后执行循环体中命令,显示出Wednesday。
依次处理,当day把值表中各字符串都取过一次之后,下面day的值就变为空串,从而结束for循环。因此,值表
中字符串的个数就决定了for循环执行的次数。在格式上,值表中各字符串之间以空格隔开。
格式二:
for 变量 in 文件正则表达式
do
命令表
done
其执行过程是,变量的值依次取当前目录下(或给定目录下)与正则表达式相匹配的文件名,每取值一
次,就进入循环体执行命令表,直至所有匹配的文件名取完为止,退出for循环。
PDF 文件使用 "pdfFactory Pro" 试用版本创建
格式三:
for i in $* 或者 for i
do do
命令表命令表
done done
这两种形式是等价的。其执行过程是,变量i 依次取位置参数的值,然后执行循环体中的命令表,直至
所有位置参数取完为止。
7.break命令和continue 命令
break命令可以使我们从循环体中退出来。其语法格式是:
break [ n ]
其中,n表示要跳出几层循环。默认值是1,表示只跳出一层循环。
continue命令跳过循环体中在它之后的语句,回到本层循环的开头,进行下一次循环。其语法格式是:
continue [ n ]
其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环。默认值为1。循环层数是由内向
外编号。
函数
在Shell脚本中可以定义并使用函数。其定义格式为:
[function]函数名( )
{
命令表
}
其中,关键字function可以缺省。
函数应先定义,后使用。调用函数时,直接利用函数名,如showfile,不必带圆括号,就像一般命令那
样使用。Shell脚本与函数间的参数传递可利用位置参数和变量直接传递。变量的值可以由Shell脚本传递给被
调用的函数,而函数中所用的位置参数$1、$2,等对应于函数调用语句中的实参,这一点是与普通命令不同的。
下面是使用函数的示例:
#func is a function name
# it echos the values of variables and arguments
func( )
{
echo "Let's begin now. "
echo $a $b $c
echo $1 $2 $3
echo "The end. "
}
a=" Working directory "
b="is"
c=`pwd`
func Welcome You Byby
echo "Today is ` date ` "
Shell中的函数把若干命令集合在一起,通过一个函数名加以调用。如果需要,还可被多次调用。执行函
数并不创建新的进程,是通过Shell进程执行。通常,函数中的最后一个命令执行之后,就退出被调函数。也可
利用return命令立即退出函数,其语法格式是:
return [ n ]
其中,n值是退出函数时的退出值(退出状态),即$?的值。当n值缺省时,则退出值是最后一个命令执
行后的退回值。
PDF 文件使用 "pdfFactory Pro" 试用版本创建
shell操作db结果
COMMAND="select auth_user_query_id, auth_user_secretkey from auth_users where auth_user_name=\"admin\" "
declare count=`$MYSQL -u${USER} -p${PASSWORD} -D ${DB} -e "${COMMAND}" --skip-column-name`
for list in $count
do
echo " the item is $list"
done
阅读(1994) | 评论(0) | 转发(0) |