《UNIX编程环境》——第5章 shell程序设计 5.1 定制cal命令

本节书摘来自异步社区《UNIX编程环境》一书中的第5章,第5.1节,作者:【美】Brian W. Kernighan , Rob Pike著,更多章节内容可以访问云栖社区“异步社区”公众号查看

第5章 shell程序设计

尽管大多数用户认为shell是一个交互式的命令解释器,但实际上它是一种程序设计语言,它的每一条语句运行一条可执行的命令。shell要同时满足交互执行命令和编程运行命令两种使用方式的要求,它是一种特殊的语言,造成这种局面,既有历史的原因,也有设计方面的影响。它的应用程序范围广泛,而且从语言的观点看目前还有许多争议,但不了解这些争议的细节并不影响用户高效地使用shell进行程序设计。本章的目的是通过逐步开发一些有用的shell程序,说明shell编程的基本原理。本章不是一部shell的参考手册,阅读本章时应该在手边准备好《UNIX程序员手册》中关于sh(1)的部分,以便随时参考。

和其他很多命令一样,shell程序的使用细节通常可以通过实践很快地掌握。shell手册常常不易于理解,而一个好的例子有时是将问题解释清楚的最好方法。鉴于这一原因,本章围绕着程序实例而不是围绕着shell的特性来组织,是shell编程指南,而不是shell所有功能的罗列。这里不仅讨论shell能做什么,而且结合强调交互式程序测试的思想,讨论shell程序的开发和编写。

当用shell或者其他语言编写程序时,如果恰有其他的人也需要使用这个程序,将有助于程序的进一步改进和完善。因为他人对使用程序的要求往往要比程序编写者本人更苛刻,因此,shell编程的一个重要原则在于提高程序的健壮性,要能处理不正确的输入,即使程序运行错误,也要能给出有用的提示信息。

5.1 定制cal命令

UNIX编程环境
shell程序的一个通常用途是增强或改善应用程序的用户界面。作为一个增强程序界面的例子,考虑cal(1)命令:

screenshot

这里的缺点是月份必须使用数字输入。而且,当输入cal 10时,将打印10年的年历,而不是打印当年10月份的月历。因此,要打印一个月的月历,必须同时输入相应的年份。

重要的是,不论cal命令提供的是什么接口,都可以只改变用户接口而不需要改动cal程序本身。可以把命令放在自己的bin目录里,将一个更易于使用的参数语法转化为实际的cal命令要求的参数格式;甚至还可以调用自己的cal版本,这样就更加直接一些。

在编制新的cal命令时,首先要考虑的问题是:cal应该做些什么?我们的基本设想是要求cal能够更合理地工作。cal应该能通过名字识别月份,当有两个参数时,除了将月份名转换为相应数字以外,其他功能和原来的cal命令完全一样。当仅给定一个参数时,cal应打印当年相应月份的月历,而当不提供参数时,它只打印当月的月历。这些正是cal命令的最常用的情况。综上所述,要解决的主要问题是:确定有多少个参数,如何将这些参数转换为标准cal要求的格式。

shell提供的case语句正好适用于进行上述判断:

screenshot

case语句将单词(word)和模式(pattern)从头至尾进行比较,当遇到第一个匹配的模式时,执行与该模式相应的命令(command),而且仅仅执行这一命令。模式均按照shell模式匹配规则书写,对文件名匹配规则稍微作了一些推广。每项匹配所对应的命令均以双分号(;;)结尾。(最后一个匹配项的命令之后可以不用;;,但为了编辑的方便,通常还是保留它。)

我们的cal版本能确定出现在命令行中参数的个数,处理以字母表示的月份名,然后再调用前述真正的cal。shell变量$#保存了调用shell文件时的参数的个数,其他特殊shell变量列在表5-1中。

screenshot

表5-1 shell内部变量
screenshot

第一个case条件检查参数个数$#,并选择对应的操作。第一个case最后的模式*表示匹配所有的情况,即当参数个数即非0又非1时,执行最后一种情况。(因为各个模式是顺序扫描,所以与所有情形匹配的模式一定是最后扫描的。)m和y变量分别作为月份和年份-当给定两个参数时,此时,我们的这个cal命令与原始的cal命令执行相同的操作。

第一个case语句有两行都包括了这样一条语句:

screenshot

虽然字面上这一语句的功能不太明显,但是通过下面几条命令很容易看出这一语句的作用。

screenshot

set是shell的一个内部命令,它能够处理相当多的事情。当没有参数时,set给出环境变量值,正如第3章中所提到的。set还能实现重置基本参数如$1、$2等的功能。set date把$1重置为星期几,$2重置为月份,等等。因此,在cal程序的第一个条件语句case中,当没有参数时,按照当前日期设置月份和年份。只有一个参数时,将该参数作为月份,而年份从当前日期中取得。

set还可以识别多个选项,使用最多的是-v和-x,设置了这些选项后,每条命令在由shell处理执行时,会返回所运行的命令。这对于调试复杂的shell程序是必不可少的。

剩下的问题是月份的转换,即当月份是文字形式时,要将它转换成数字形式。第二个case语句正是完成这一工作的。无需多说,这段程序的功能是一目了然的。这里唯一不易理解的是case语句中的¦符号,它与egrep中的¦符号相同,表示选择,如big¦small表示或与big匹配,或与small匹配。当然这一条件也可以写成[jJ]an*等其他形式,程序能接受全部小写的月份名或者以大写字母开头的月份名,前者是因为UNIX系统主要接收小写,而后者是因为date命令打印的月份的第一个字母就是大写。shell模式匹配的规则列在表5-2内。
screenshot

在第二个case语句中的最后两种情形用于处理单个的参数,它可能是年份,但第一个case语句会将它当成月份;如果它是数字,可能用来表示月份,则作为月份处理,否则,作为年份处理。

最后一行使用转换后的参数,调用/usr/bin/cal(真正的cal命令)。新版本的cal程序可以接受以下输入:

screenshot

若键入cal 1984,将打印出1984年全年日历。

这个改进了的cal程序完成与原来的cal程序相同的工作,但是实际使用起来却更简单,更容易记忆,在名字选取上也尽量采用简单的方式,即用cal,而不用calendar(它已经是另一个命令)或任何其他不利于记忆的名称,如ncal。不改变命令的名字还有一个优点,即用户不会因为新名字而受影响。

在结束关于case语句的讨论之前,还需说明一点:为什么shell程序的模式匹配规则不同于ed程序及其衍生程序中的匹配规则。毕竟采用两种不同的模式意味着要学习两组规则,要求采用两种代码来处理这两组模式。有些区别源自最初的错误选择,后来也从未改正过—例如,匹配任意符号的模式在ed里使用“.”,在shell里使用“?”,仅仅是为了保持对过去的兼容性,除此之外没有任何其他原因;另一方面,有时不同的模式完成不同的工作。在ed中的正则表达式可以搜索行内任何位置出现的字符串,特殊符号^和$的作用是将搜索定位到行首和行尾。然而在默认情况下,对文件名,搜索定位一般为默认,这种情况下,如果用命令

screenshot

来代替命令

screenshot

将是非常麻烦的。

练习5-1 如果其他用户希望使用你的cal版本,如何使它被所有用户共享?把它放在/usr/bin目录中需要执行哪些操作?

练习5-2 有必要使cal程序在cal 83时打印1983年的年历吗?如果有必要的话,你怎样实现打印1983年的年历。

练习5-3 修改cal使之接受多个月份,例如:

screenshot

或者一个连续范围的月份

screenshot

假如现在是12月份,而运行cal Jan,应该得到的是今年的1月份还是明年的1月份?应如何考虑这个问题?

实验目的 Linux操作系统中shell是用户与系统内核沟通的中介,它为用户使用操作系统的服务提供了一个命令界面。用户在shell提示符($或#)下输入的每一个命令都由shell先解释,然后传给内核执行。本实验要求用C语言编写一个简单的shell程序,希望达到以下目的:  用C语言编写清晰易读、设计优良的程序,并附有详细的文档。  熟悉使用Linux下的软件开发工具,例如gcc、gdb和make。  在编写系统应用程序时熟练使用man帮助手册。  学习使用POSIX/UNIX系统调用、对进程进行管理和完成进程之间的通信,例如使用信号和管道进行进程间通信。  理解并发程序中的同步问题。  锻炼在团队成员之间的交流与合作能力。 2. 实验要求 1. ysh解释程序的重要特征 本实验要实现一个简单的命令解释器,也就是Linux中的shell程序。实验程序起名为ysh,要求其设计类似于目前流行的shell解释程序,如bash、csh、tcsh,但不需要具备那么复杂的功能。ysh程序应当具有如下一些重要的特征:  能够执行外部程序命令命令可以带参数。 . 。  能够执行fg、bg、cd、history、exit等内部命令。  使用管道和输入输出重定向。  支持前后台作业,提供作业控制功能,包括打印作业的清单,改变当前运行作业的前台/后台状态,以及控制作业的挂起、中止和继续运行。 除此之外,在这个实验中还须做到:  使用make工具建立工程。  使用调试器gdb来调试程序。  提供清晰、详细的设计文档和解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值