1.13k likes | 1.46k Views
第 9 章 shell 编程. 9.1 shell 概述 9.2 shell 的基本功能 9.3 shell 启动及其命令 9.4 shell 命令的集成 9.5 shell 变量 9.6 shell 的控制结构 9.7 shell 的运行环境 9.8 shell 应用实例 9.9 小结 习题.
E N D
第9章 shell编程 9.1 shell概述 9.2 shell的基本功能 9.3 shell启动及其命令 9.4 shell命令的集成 9.5 shell变量 9.6 shell的控制结构 9.7 shell的运行环境 9.8 shell应用实例 9.9 小结 习题
shell是一种命令语言,同时又是一种程序设计语言。shell的语言处理能力,使得用户能够方便地定义各种变量、参数,并使用各种控制语句编写复杂的命令程序完成多种工作。本章介绍shell、shell程序设计语言、shell的运行环境及shell程序的调试。shell是一种命令语言,同时又是一种程序设计语言。shell的语言处理能力,使得用户能够方便地定义各种变量、参数,并使用各种控制语句编写复杂的命令程序完成多种工作。本章介绍shell、shell程序设计语言、shell的运行环境及shell程序的调试。
9.1 shell概述 9.1.1 什么是shell shell是一种命令解释程序(命令解释器),shell解释用户输入的命令行,提交系统内核处理,并将结果返回给用户。与Linux命令一样都是实用程序,但两者又有区别。一旦用户注册到系统后,shell就被系统装入内存,并一直运行到用户退出系统之止;而一般命令仅当被调用时,才由系统装入内存执行。 shell本身也是一种可编程的程序设计语言。用shell写的程序(shell脚本)相当于dos/windows下的批处理文件,它可以简单到只有一条命令,也可以复杂到包括大量循环、条件语句、数学运算、控制结构,也可以是介于两者之间的程序。
9.1.2 shell程序语言的特点 shell允许通过编程来完成复杂的功能处理,但其作为语言与高级语言相比较具有不同的特点: (1) shell是解释性的,多数高级语言是编译性的; (2) shell语言与高级语言处理的对象不同; (3) shell与系统有密切的关系; (4) shell易编写、调试、灵活性较强,但速度低; (5) shell作为命令级语言,命令组合功能很强。
9.1.3 shell的版本 shell有两种主要语法类型: Bourne shell和C shell,彼此不兼容。Bourne shell家族:sh ksh bash psh zsh;C shell家族:csh tcsh。 其中bash和 zsh在不同程度上支持 csh 的语法。 这里,我们再着重介绍一下bash的特点: (1) 自动补全功能 假设要输入的命令很长,或者命令后面要给的文件名很长。这个时候只要按一个Tab键,bash就会在可能的命令或文件名里面找寻匹配的命令,找到的话就会自动帮你补齐。
(2) 命令行编辑程序 bash的命令行编辑是在提示符下,可对未执行的命令字符任意地修改。 (3) 命令历史(command history) 所谓的命令历史就是把曾经输入过的命令记录起来,方便日后的查询与使用。只要按向上键就可以调出前一个命令,再按一次向上键就可以调出更前一个命令,依此类推,用向下键可以回到下个命令,所以用上、下键就可以选择以前输入过的命令。
9.2 shell的基本功能 9.2.1 程序的运行 当用户输入一行命令后,shell负责解释、分析输入的内容,并且决定做什么,同时替系统内核删除命令行中不必要的信息。命令是一个可执行的Linux命令、程序、工具或shell脚本。 例1: $ls -l file1 file2 file3 例2: $echo ′Welcome to Tsinghua University.′ 例3: $echo Welcome to Tsinghua University. 注意: 例2和例3的结果是完全一样的。
9.2.2 使用保留字和元字符 shell有一些具有特殊意义的字(保留字) ,如在shell脚本中,像do、done、for、while等保留字控制循环操作,if、then、else等保留字进行条件控制。保留字随shell的不同而不同。 在Linux系统里,有一组特殊意义字符,这就是所谓的元字符(通配符)。 现在列出一些常用的通配符的意义,供参考。 .. 上一层目录,与cd命令配合用得比较多 . 目前工作的目录 * 任意长度的字符 ? 长度为一个的任意字符
[..] 括号内的一个字符 \m 等于某个通配符,如*、?等 [a-z]* 小写字母开头的所有字符串 \ 转义符号,用以解除特殊字符的特殊意义 ~ 用户目录 ; 分隔符,当命令行有多个命令时,做分隔用 $ BourneShell的提示符,同时也作为shell语言的位置变量参数 # 做注释用 | 建立一个管道,使一命令的输出作为另一个命令的输入 & 将命令以后台方式执行
> 将命令的输出重导入文件中 < 将命令的输入流指定为由文件中加载,和>相反 >> 将命令的输出加在一个已经存在的文件后面 {..} 括号内的一个字符串 例: $ls -x t*显示当前目录下以t打头的所有文件。
9.2.3 变量、文件名的替换 1. 变量的替换 shell允许对变量赋值。shell一旦在命令行中发现$变量名时,将在$变量名的位置上用以前赋给该变量的值替代$变量名。 例: $myhome=/usr/app1 $echo $myhome /usr/app1 $ls -x $myhome file1 file2 file3 file4
2. 文件名的替换 shell在命令行中将文件名进行替换。事实上,shell在确定要执行的程序名和它的自变量之前,要对命令行扫描,找出元字符进行相应的文件名的替换。 例: $ls -x file1 file2 file3 file4 $echo* file1 file2 file3 file4
9.2.4 输入输出重定向(改向) shell处理命令行的输入输出重定向,它扫描命令行中特殊改向字符“>”、“<”、“>>”、“<<”,将输入或输出改向到指定的文件中。 例: $echo “Please call me:62781849” > msg $cat msg Please call me:62781849 注意: 就程序或命令本身而言,它并不知道其标准输出已被改向,只是简单地按照自身的方式向标准输出输送信息。
正如shell扫描命令行查找重定向字符一样,它也查找管道字符“|”。对于所发现的每个管道字符,它将管道字符前面的命令的标准输出连接到管道字符后面的命令的标准输入中,然后启动两个程序的执行。正如shell扫描命令行查找重定向字符一样,它也查找管道字符“|”。对于所发现的每个管道字符,它将管道字符前面的命令的标准输出连接到管道字符后面的命令的标准输入中,然后启动两个程序的执行。 例: $w|wc -l(假设有8个用户上机) 8
9.2.5 运行环境的控制 当用户登录到Linux系统中,系统启动一个交互式的shell命令解释器(称为注册shell)。该shell为此用户创建工作环境。shell提供了一定的命令,允许用户对自己运行的环境进行控制,即对运行环境实现客户化。用户的运行环境包括:主目录、终端类型、输入命令提示符、查找命令的路径名、以及其他全局变量等。例如,Bourne again Shell、Bourne Shell、Korn Shell和C Shell的环境文件分别为.bash-profile、.profile、.kshrc和.cshrc。
9.2.6 支持shell的编程 shell除了具有命令解释器的功能外,它本身就是一种程序设计语言,这种语言也由shell解释执行的。用户可以在文件中编写一组shell命令,该文件称为shell脚本或shell程序。通过把命令、变量赋值及条件控制语句结合起来,用户就获得了一个强大的编程工具。需要说明的是,在Linux系统本身就存在大量的shell程序,用于各种管理和应用。
9.3 shell启动及其命令 9.3.1 shell的启动 1.系统在用户登录时启动shell 在Linux系统引导过程中,首先启动init进程以查询终端的各个端口及其特性,当发现活动的终端时,调用getty进程。接着getty进程在接受用户名和口令后,调用login进程。login进程负责验证用户身份,验证后把控制权交给shell程序。shell根据环境文件建立系统范围内的工作环境和该用户自己的工作环境,最后显示命令提示符(#、$、%)。 在/etc/passwd文件中指定要启动的shell,如下所示:
root:x:0:0:root:/root:/bin/bash john:x:201:Certer′starf:/home:/bin/sh 2. 命令行状态下的交互shell启动 在系统中可能有多种版本的shell存在,可通过相应的命令来启动,如: $bsh $ksh $sh 3. 执行用户命令时启动shell(子shell) 由系统生成新的子shell来执行该命令。
9.3.2 命令的查询 环境变量PATH的构成决定了寻找shell命令和其他程序的途径,是影响shell程序效率的主要因素之一。PATH变量设置要考虑以下几点:在PATH变量中,命令使用得多的程序目录应放在前面,极少用目录不设在PATH变量中。在shell程序内用得较多的程序目录应加在变量PATH中。并且PATH应尽可能地短,不要出现重复的目录。还要尽量避免查询大目录,如需设置,将其路径放在PATH路径的最后位置。 例: PATH=/bin:/usr/bin:/etc:.
9.3.3 shell常用的命令 Linux系统通常提供了许多命令以方便用户与系统的交互对话。这些命令的选项较多,并且可利用命令的集成(如输入输出改道、管道机制)及程序设计功能组合成许多命令。 Linux有丰富的shell命令,大致划分为以下几类:目录操作与管理、文件操作与管理命令、系统管理与维护、用户管理与维护、系统状态、进程管理、通讯命令、其他命令。例如, awk cat chmod chown cp cron date df diff du echo expr file find grep init kill ln login ls mail make man mkdir mv nohup pg ps pwd read rm wc
通常情况下,在用户输入一个命令(非内部命令)时,注册shell首先通过查询路径查找该命令,然后生成出一个副本(称为该shell的子shell),由子shell来负责解释执行这个命令。在命令执行过程中,注册shell(父shell)等待子shell的执行而进入睡眠态,一旦子shell执行完毕,子shell将唤醒注册shell(父shell),而自己的生命周期到此结束。通常情况下,在用户输入一个命令(非内部命令)时,注册shell首先通过查询路径查找该命令,然后生成出一个副本(称为该shell的子shell),由子shell来负责解释执行这个命令。在命令执行过程中,注册shell(父shell)等待子shell的执行而进入睡眠态,一旦子shell执行完毕,子shell将唤醒注册shell(父shell),而自己的生命周期到此结束。
9.3.4 shell的内部命令 内部命令构造在shell的内部。内部命令比非内部命令的执行速度要快。因此,编写shell程序时应尽量地使用shell的内部命令。shell常用的内部命令有: # 注释命令,#后面的内容作为注释信息。 : 空命令,通常放在一行的最左边,实际不做任何命令,只返回出口代码0。 其他命令有:cd,eval,set,unset,exec,exit,if,else,for,case,while,until,continue,break等。
9.4 shell命令的集成 9.4.1 元字符和文件名生成 1. UNIX元字符(通配符)的定义 * 匹配任何字符串,包括空字符串; ? 匹配任何单个字符; [.,-,!] 按照范围、列表或不匹配等形式匹配指定的字符; \ 转意符,使元字符失去其特殊的含义。 例: [a-d,x,y] 匹配字符a、b、c、d、x、y; z* 匹配以字符z开始的任何字符串;
x?y 匹配以x开始、以y结束、中间为任何单个字符的字符串; [!Z] 匹配不为Z的单个字符。 2. 元字符作为文件扩展名的使用 例: [a-f]* 匹配字符a到字符f开头的文件名,如abc,d2,e3.c,f.dat; *z 匹配以字符z结尾的任何字符串,如win.z,core.zz,a-c-5z; rc?.d 匹配以rc开始、以.d结束、中间为任何单个字符的文件名,如rc0.d,rc2.d,rcS.d; *[!o] 匹配不以o结尾的文件名。
9.4.2 管道和命令表 在shell中有两种结构类型:管道线和命令表。当shell检测到一个管道操作符时,就建立一个系统管道文件,这是一个先进先出的数据结构,它允许在同一时刻对管道线上的命令或程序进行读和写,即允许两个无关的命令通过管道连接交换信息。 1. 管道的概念 管道:是一个命令的标准输出与另一个命令的标准输入之间的连接,不经过任何中间文件; 管道线:是由管道操作符分隔的一个命令序列,最简单的管道线是一个简单命令; 管道操作符:用符号“|”表示。
例: w|wc -l ps aux|grep ftp 2. 命令表的概念 命令表:一串管道线构成了一个命令表,最简单的命令表是一个管道线,一个命令表送回的值是该命令表中最后一个管道线的出口状态。 管道线分隔符:分隔命令表元素,确定管道线执行的条件。各分隔符含义如下: ; 表示按顺序执行管道线; && 表示根据条件(true),执行其后面的管道线; ‖ 表示根据条件(false),执行其后面的管道线;
& 表示前面的管道线在后台(异步)执行。 例1: 四个管道线构成一个命令表 ls -l /tmp /root w|wc -l ps 例2: 与例1等价 ls -l /tmp /root ;w|wc -l ;ps 例3: sys-account &
9.4.3 命令组合 命令组合有两种形式:{命令表}和(命令表),前者只在本shell中执行,不产生新的子进程;后者要产生新的子进程来执行命令表。 例1: { cd mydoc; rm junk;} 该命令表只能在当前shell下执行,先进入目录mydoc,然后执行rm命令,执行完毕后,当前目录已改变为mydoc。 例2: (cd mydoc; rm junk;) 当前shell要生成一个子shell进程,由该子shell来执行命令表。子shell完成操作后,自然消亡,而其父shell进程的当前路径并没有变化。
9.4.4 命令替换 当一个字符串被括在反撇号“ ` ”中时,该字符串将作为命令被shell解释执行,即用命令的执行结果替换这个字符串本身。要注意反撇号与单引号的区别。 例1:$ now=′date′ $ echo $now date $ now=`date` $ echo $now 1998年 10月 28日 星期三 17时 51分 56秒 CST 例2:$ count=10 $ count=`expr $count+1` $ echo $count 11
9.4.5 输入、输出重定向 1. 使用标准改向符进行重定向(改向) < 输入改向 << 追加输入改向 > 输出改向 >> 追加输出改向 2. 使用标准文件描述字进行重定向(改向) 在Linux系统中,定义了用于输入、输出的标准文件,其文件描述字0为进程的标准输入、文件描述字1为标准输出、文件描述字2为标准错误输出。 3. 标准错误输出的改向(>、>>) 格式为:
command 2 >file command 2 >>file 例1: 将myfile1作为sort的输入。 sort <myfile1 例2: 将date的输出转向到myfile2文件中。 date >myfile2 例3: 将ls \|l的输出追加到myfile3文件中。 ls -l>>myfile3 例4:将错误输出改向到err-file文件。 $myprog 2>err-file 例5:将标准输出和错误输出改向out文件。 $myprog >out 2>>out $myprog >out 2>>&1
9.5 shell变量 9.5.1 shell变量描述 shell实际上是基于字符串的程序设计语言,但也有变量。shell变量能够而且只能存储正文字符串,即它只有一种类型的变量即串变量。但从赋值的形式上看,则可以分成四种类型的变量或变量形式。变量的名字必须以字母或下划线开头,可以包括字母、数字和下划线。 9.5.2 用户自定义变量 用户自定义变量语法格式:name=string,赋值号“=”两边不允许有空白符。 例:
nodehost=beijing.UUCP path=/bin:/usr/bin:/etc/bin count=10 允许多个赋值操作,按从右到左的顺序进行。 例: $A=$BB=abcC=″OK″ $ echo $A $B $C abc abc OK 当引用一个未设置的变量时,其隐含值为空。 例: $ echo ″$mail is path of mailbox″ is path of mailbox
如果用双引号“”将值括起来,则括起来的字符串允许出现空格、制表符和换行符等特殊字符,而且允许有变量替换。如果用双引号“”将值括起来,则括起来的字符串允许出现空格、制表符和换行符等特殊字符,而且允许有变量替换。 例1:$ MAIL=/var/mail/fk $ var=″$MAIL is path of mailbox″ $ echo $var /var/mail/fk is path of mailbox 例2: $ str=″This is \n a book″ $ echo $str This is a book
如果用单引号‘’将值括起来,则括起来的字符串允许出现空格、制表符和换行符的特殊字符,但不允许有变量替换。如果用单引号‘’将值括起来,则括起来的字符串允许出现空格、制表符和换行符的特殊字符,但不允许有变量替换。 例3: $ BOOK=″English book″ $ MSG=′$BOOK′ $ echo $MSG $BOOK 例4: $ msg=′ Today is Sunday′ $ echo $msg Today is Sunday
引用变量的值时,可以用花括号{}将变量名称括起来,使变量名称与它的后续字符分隔开,如果紧跟在变量名称后面的字符是字母、数字或下划线时,必须要使用花括号。引用变量的值时,可以用花括号{}将变量名称括起来,使变量名称与它的后续字符分隔开,如果紧跟在变量名称后面的字符是字母、数字或下划线时,必须要使用花括号。 例5: $ str=′This is a string′ $ echo ″${str}ent test of variables″ This is a stringent test of variables $ echo ″$strent test of variables″ test of variables
可将变量设置为只读形式,格式为,readonly 变量名1 变量名2 例6: $ ux=UNIX.SUN $ readonly ux $ ux=UNIX.SCO ux: is read only 查看只读形式的变量,格式为readonly 例7: $ readonly readonly ux
9.5.3 位置变量 位置变量顾名思义是与变量所在位置有关的变量,这是一种特殊的变量。当一个shell过程被调用时,shell隐含地为它建立一系列的位置变量。这种位置变量是系统预定义好的,可以直接引用。如命令行的shell过程名本身被指定为位置变量$0,第一个命令参数为$1,……,第九个命令参数为$9。 例: ls / /bin /etc /usr/bin /dev $0 $1 $2 $3 $4 $5
1. 内部命令shift 的作用 当位置变量个数超出9时,就不能直接引用位置大于9的位置变量了,必须用shift命令存取。每执行一次shift命令,删除$1位置变量,并使其他的所有位置变量向左移动一个位置。 例: $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 myprog a b c d e f g h i j shift b c d e f g h i j shift c d e f g h i j shift d e f g h i j
2. 用set命令进行强制性赋值 位置变量可以使用set命令进行强制性赋值。 例: set Sun Mon Tue Wed Thu Fri Sat 结果$1 $2 $3 $4 $5 $6 $7 注意: $0是不能用这种方法赋值的。
9.5.4 环境变量 shell执行环境由一系列环境变量组成,这些变量是由shell维护和管理的。所有这些变量都可被用户重新设置,变量名由大写字母或数字组成。 CDPATH 执行cd命令时使用的搜索路径; HOME 用户的home目录; PATH 寻找命令或可执行文件的搜索路径; PS1 主命令提示符,默认为“$”; PS2 从命令提示符,默认为“>”; TERM 使用的终端类型。
9.5.5 预定义的特殊变量 在shell中有一组特殊的变量,其变量名和变量值只有shell本身才可以设置。 “$#” 记录传递给shell的自变量个数。 例1: myprog a b c 则 $#的值为3 例2: if test $# -lt 2 then echo ″ two or more args required ″ exit fi
“$?”取最近一次命令执行后的退出状态:执行成功返回码为0,执行失败返回码为1。“$?”取最近一次命令执行后的退出状态:执行成功返回码为0,执行失败返回码为1。 例: $test -r my-file(假设my-file文件不可读) $echo$? 1 “$$”记录当前shell的进程号。
9.5.6 变量替换 shell在遇到未设置的变量时,将其值作为空串处理。而在实际应用中,对于未设置的变量,用户可以根据需要采用不同的处理方式,这可通过变量替换来实现。变量替换提供了三种功能:允许替换未设置变量的隐含值;允许对未设置变量赋值;在访问未设置变量时,提示出错信息。格式为, ${var:-word} 例1: 假设$PARM未设置 $echo ″ The value of PARM is ${PARM:-undefined}″ The value of PARM is undefined $echo $PARM -
例2:假设$PARM未设置 $arg=${PARM:- ″ not defined ″} (注意: 双引号) $echo ′ $arg : ′$arg $arg : not defined 例3:对未设置变量赋值 $cat use.d sel=$1 :${sel∶=main}(注意: 此处的“:” 为空命令) echo ″ Your selection is $sel ″ $use.d programming Your selection is programming
注意: 变量替换的这种形式不允许使用位置变量,若要使用位置变量,则必须先将位置变量赋值给一中间变量,然后再对中间变量进行这种形式的替换。 例4:将例3中的空命令用中间变量替换。 $cat use.d sel=$1 my-sel=${sel∶=main} echo ″ Your selection is $my-sel ″ $use.d programming Your selection is programming
例5: 测试环境变量TERM是否设置。 $cat check.env : ${TERM:? ″ the TERM varible should be set ″ } 注意: 出错信息以一行长为限,且输出到标准错误输出上。
9.5.7 特殊字符的引用 在shell中所使用的许多特殊符号也可为其他目的所使用,因此当不需要引用这些字符的特殊含义时,就必须进行删除。消除特殊字符的含义有3种常用的方法,分别是转义符、单引号和双引号。 1. 转义符(\)的引用 使用转义符消除单个字符的特殊含义,即将紧跟在转义符后面的单个字符按字符本身的实际含义解释。 例:转义符具有续行功能 $ cat back.sh echo ″Enter your name: \\c″ read name echo Hello $name
$ back.sh Enter your name: john Hello john 2. 单引号 (‘’)的引用 使用单引号消除被括在单引号中的所有特殊字符的含义,即单引号表示内容照原样不动。 例1: $echo ′type a $* please′ type a $* please 例2: 在指定的目录中,查找名字为 *.zh 或 $.sh文件。 $cat test.sh grep ′*.zh | $.sh′ $1
3. 双引号 (“”)的引用 使用双引号能消除被括在双引号中的大部分特殊字符的含义,不能消除的字符有:$、 ` 、″、 \。 例1:$echo ″ Type a \$*, please ″ Type a $*, please 例2: vdate= ″`date` is the system maintenance time ! ″ 例3: $cat share-file mkdir /tmp/fk chmod 755 /tmp/fk cp ″$@″ /tmp/fk chmod 777 /tmp/fk/*