shell学习笔记一--总括
原创
©著作权归作者所有:来自51CTO博客作者licong_jay的原创作品,请联系作者获取转载授权,否则将追究法律责任
Shell脚本编程是Unix/Linux系统管理员应当具备的一项非常重要的技能,优秀管理员用shell脚本完成绝大多数工作,所以他们有足够的时间喝咖啡泡论坛。然而,要掌握这一技能并不十分容易,这需要了解相当数量的知识,并进行大量的练习和实践。笔者丝毫不敢说自己已经掌握了这一技能,我写这些文章只是为了把学习的历程记录下来,如果恰好也对你有所帮助我很荣幸。
首先,我们来解决必须回答的问题:shell是什么?要回答这个问题必须先清楚Unix/Linux系统的结构。我们把这个结构简单分为两部分:系统内核——实用程序。系统内核是系统的心脏,从打开计算机自检时就驻留在计算机内存,直至计算机关闭;而实用程序驻留在计算机磁盘上,仅当需要时才调入内存。
那么shell是什么呢?shell是一种实用程序,实际上所有Unix/Linux命令都是一个实用程序!每当系统允许用户登陆时,系统(准确地讲是init程序)为每个终端启动getty,getty做一些事情然后在分配给他的终端上显示”login:”等待用户输入信息。一旦用户键入信息并以回车结束,getty程序就会消失,同时启动login程序完成登陆处理。用户成功登陆后,将会启动一个重量级的程序,那就是shell!
Shell为什么那么重要呢?因为他有很多很强大的功能:
一、执行程序:shell负责解释并执行终端请求的程序。
二、变量和文件名的替换:shell会解释一些特殊的符号来进行替换。
三、I/O重定向:将输入和输出重定向到别的地方(不再是标准的键盘和屏幕)。
四、管道线连接:shell可以把多个命令连接在一起,就像管道一样。
五、环境控制:shell控制着用户的环境,你可以根据需要通过shell来改变环境。
六、解释型程序设计语言:可以类似于C(尽管没那么强大)那样编写复杂的程序。
下面我们的详细内容都是围绕上面六大功能展开的,在这之前先说明一下shell脚本又是什么?把shell能够解释的命令一条接一条写到文件里,加入上面提到的一些功能,并给该文件可以执行的权限,那么一个shell脚本就诞生了。换句话说,当你掌握了这些功能也就基本上掌握了编写shell脚本的技能了!
——程序执行
shell负责解释并执行终端请求的程序,这里的程序大多数情况都是指Unix/Linux命令。Shell所涉及到的每条命令都遵循相同的基本格式:
程序名 参数表
Shell扫描命令行,并判断要执行的程序名字以及要传给程序什么参数(这里我们认为选项是参数的一部分)。
Shell用特定的字符去判断程序名的起止位置。这些字符被称为空白字符,有空格、TAB和换行符(回车),shell会忽略掉多出的空白字符。键入命令:mv file temp时shell扫描命令行,并提取从该行开始到第一个空白字符为止的字符串作为将执行的程序名:mv;接下去,到下一个空白字符之前的字符序列作为传给mv的第一个参数:file;再接着到下一个空白字符(这里是换行符之前的字符序列是传给命令mv的第二个参数:temp。分析完命令之后,shell就会执行mv命令,同时传给它两个参数file和temp。后面的任务就交给mv去完成了。注意,Unix/Linux系统绝大多数情况是严格区分大小写的!
前面提过,多个空白字符将被shell忽略。这就是说,当shell处理命令行:
echo when do
we eat?
时,该命令行给echo程序4个参数:when、do、we和eat?
因为echo得到参数后,只是将他们显示在终端上,并用一个空格字符分隔各个参数,因此下面的输出就很容易理解了:
$echo when
do we eat?
When do we eat?
$
事实上,echo命令根本看不见那些空白字符:他们已被shell“吞”掉了!
——变量和文件名的替换
与其他程序设计语言一样,shell允许你给变量赋值。任何时候,在命令行中指定一个变量,并且在其前面冠以美元符号,shell都会在这个位置用已赋给该变量的值来替换。我们后面还会详细讨论这个主题。
Shell也在命令行执行文件名替换。实际上,在判定要执行的程序名及其参数之前,它先扫描命令行,寻找文件名替换字符*,?,或[…]。假如当前目录包含如下文件:
$ls
file1
file2
file3
$
现在对echo命令做文件名替换:
$echo *
file1 file2
file3
$
有多少个参数传给了echo程序,一个还是4个?因为我们知道shell会执行文件名替换,所以答案是4个。当shell分析命令行:
echo *
时,它识别出特殊字符 *,并在命令行中将*替换为当前工作目录下的所有文件:
echo file1
file2 file3
然后shell确定传给命令的参数。因此echo是看不见*的,当轮到它来处理时,看到的是命令行中键入4个参数。
——I/O重定向
在命令行中执行输入输出重定向也是shell的职责。它扫描命令行,看是否有特殊字符<、>和>>出现(还有字符<<,将在后面讨论)。
键入命令:
echo Remember
to order Law >reminder
时,shell识别出特殊的重定向字符>,并提取命令行中的下一个词作为重定向输出结果的文件名,在上例中是reminder。如果reminder已经存在,而你也有对它的写入权限,原来的内容就丢失了(如果没有写入权限,shell会提示一条错误信息)。
在shell开始程序的执行之前,它就将程序的标准输出重定向到指定文件。就程序而言,它根本不知道自己的输出被重定向了,它只是按照自己的方式将输出写入标准输出(通常是屏幕),并没有意识到shell已经将输出重定向到一个文件。
让我们来看两条几乎一样的命令:
$wc –l users
5 users
$wc –l <
users
5
第一种情况下,shell分析命令行,并判断要执行的程序名是wc以及要传给它的两个参数:-l和users。当wc开始执行时,它被传给了两个参数。第一个参数是-l,表示它将对行进行计数。第二个参数指明了要进行计数的文件名。所以,wc打开文件users,统计行数,并在终端上打印出统计后的行数和相应文件名。
第二种情况下,wc的操作稍微有一点不同。扫描命令行时,shell识别出输入重定向字符<,紧跟着的那个词表示重定向数据院文件的名字。Shell从命令行“吞”掉<users,启动wc程序的执行,将他的标准输入重定向到文件users,并传递给它一个参数-l。这时wc开始执行,它发现只传给了自己一个参数-l。因为没有指定文件名,wc将此作为统计标准输入上出现的行数的一种指示。因此,wc在计数标准输入上的行数时,并未意识到,它实际上是在为文件users中的行进行计数。最终的结果显示在终端上——没有文件名,因为没有给过wc文件名。
——管道线连接
就像在命令行中搜索重定向字符一样,shell也在搜索管道字符 | 。对每个它发现的管道字符,它将位于 | 前面的命令的标准输出连接到位于 | 后面的命令的标准输入。然后为这两个程序执行初始化(判断程序及其参数)。
因此在键入
$who | wc –l
时,shell发现,管道字符隔开了命令who和wc。它将前面命令的标准输出连接到后面命令的标准输入,然后为执行这两个程序做初始化。当执行who时,它产生一个登录人员的列表,并将结果写到标准输出:但它没有意识到,结果将不会送到终端,而是送到另一个命令。
当wc命令执行时,它识别出没有指定文件名,并在标准输入上统计行数。没有意识到标准输入不是来自于终端,而是来自于who命令的输出。
——环境控制
Shell提供某些命令用于定制环境。环境包括用户的宿主目录、shell用于提醒用户键入命令的提示符号、每当请求运行某个程序而进行搜索的目录列表等等。详细的内容留到后续专门的章节继续讨论。
——解释型程序设计语言
Shell有着自己的内部程序语言。它和C等其他高级程序设计语言最大的区别在于,这种语言是解释型的,他只是分析每条语句并执行,而不像其他语言那样需要经过编译才能执行。解释型的shell比编译型的程序更易于调试和修改,但他们的运行比等价的编译程序更耗时。
与大部分程序设计语言类似,shell也具有if、case、for、while、until等判断、分支、循环结构,以及变量、数组和函数。这部分内容是脚本编程的重点和难点之一,后续也会有专门的章节来进行讨论。
好了,shell总括就到这里了。总结一下,就是我们上面提到的六大功能。每一个功能都值得我们深入挖掘!事实上,你把这些功能挖掘得越透彻,你也就越容易写出更高质量的脚本。考虑不把篇幅写得过大,我将分几个主题来分别深入这些功能,有兴趣的朋友可以继续关注,链接地址也会后续提供。