首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

2.8  综合应用

至此,我们已经学习完作为程序设计语言的shell的主要功能。是时候将我们所学的知识结合起来以编写一个实际的示范程序了。

贯穿全书,我们将编写一个CD数据库应用程序以展示所学知识。首先从shell脚本开始,但很快我们就会用C语言重写该程序,并给它加上数据库等新功能。下面就开始吧。

2.8.1  需求

假设我们收集了大量的CD唱片。为了方便管理,我们将设计和实现一个管理CD唱片的程序。在学习Linux程序设计的过程中,实现这样一个电子CD唱片目录看起来是个比较理想的项目。

开始阶段,我们至少应该能够做到把每张CD唱片的基本资料保存起来,如唱片的名称、音乐类型、艺术家或作曲家的名字等。我们可能还想再保存一些简单的曲目资料。

我们希望能够以每张CD唱片为单位进行搜索,而不是以曲目资料为单位。

为了让这个小小的应用程序比较完整,我们还希望能够在这个应用程序中对唱片资料进行输入、更新和删除。

2.8.2  设计

对数据进行更新、检索和显示的这三项需求表明采用一个简单的菜单应该就足够了。由于所有需要存储的数据全部都是文本,而且假设收集的CD唱片不是很多,所以我们就没有必要使用复杂的数据库,使用一些简单的文本文件即可。将资料保存在文本文件中将使我们的应用程序比较简单,而且如果需求发生了变化,操纵文本文件总是要比操纵其他类型的文件更加容易。至少我们可以使用一个编辑器来手工输入和删除数据,而不必非要通过编写程序来完成。

我们需要为数据存储做出一个重要的设计决策:一个文件够用吗?如果够用,它应该采用什么样的格式?除曲目信息以外,我们想要保存的大部分资料在每张CD唱片上只出现一次(我们暂不考虑某些CD唱片上会有多名作曲家或艺术家作品的情况),而几乎所有CD唱片都有一个以上的曲目。

我们需要对可以存储的CD唱片上的曲目数量加一个限制吗?这看起来是一个非常随意和没有必要的限制,所以还是立刻放弃这个想法吧!

如果对曲目数量不做限制,就有以下三种选择:

l 只使用一个文件,用一行来保存“标题”信息,再用n行保存该CD唱片上的曲目信息。

l 将每张CD唱片的所有信息都放置在一行上,一直延续这一行的内容到没有可以保存的曲目信息为止。

l 把标题信息和曲目信息分开,用不同的文件来分别保存它们。

只有第三种做法能够让我们灵活地修改文件的格式,如果今后我们想把数据库转换为关系数据库格式的话(在第7章详细介绍),就需要对文件格式进行修改,因此我们选择第三种方法。

下一个决策是要在文件里放入哪些信息。

我们决定对每张CD唱片保存以下信息:

l CD唱片的目录编号。

l 标题。

l 曲目类型(古典、摇滚、流行、爵士等)。

l 作曲家或艺术家。

对曲目,我们简单的保存以下信息:

l 曲目编号。

l 曲名。

为了把这两个文件结合起来,我们必须把曲目信息和CD唱片上的其他信息关联起来。为了完成这一任务,我们使用CD唱片的目录编号。因为它对每张CD唱片都是唯一的,所以它将在标题文件中只出现一次,在曲目文件中也只出现一次。

让我们来看一个示例标题文件,如表2-24所示。

表  2-24

目录编号

标    题

曲目类型

作曲家

CD123

Cool sax

Jazz

Bix

CD234

Classic violin

Classical

Bach

CD345

Hits99

Pop

Various

它所对应的曲目文件如表2-25所示。

表  2-25

目录编号

曲目编号

曲    名

CD123

1

Some jazz

CD123

2

More jazz

CD345

1

Dizzy

CD234

1

Sonata in D minor

这两个文件通过目录编号结合在一起。请记住,标题文件中的一个数据项一般都对应曲目文件中的多行数据。

最后需要决定的是如何分隔数据项。在关系数据库里,长度固定的数据字段比较常见,但它并非总是最方便的。另一种常见方法是使用一个逗号,我们在这里就选择这个方法(即用逗号分隔变量,CSV)。

在接下来的实验部分,为了不至于让你迷失方向,我们把将要用到的函数列在下面:

实验:CD唱片应用程序

(1) 这个示例脚本程序的第一行,就和前面一样,确保自己可以作为一个shell脚本程序来执行,接下来是一些版权信息:

(2) 首先要做的事情就是,确保设置好脚本程序将要用到的一些全局变量。我们设置标题文件、曲目文件和一个临时文件。我们还设置Ctrl+C组合键的中断处理,以确保在用户中断脚本程序时删除临时文件。

(3) 现在开始定义函数。因为脚本程序是从文件的第一行开始执行,所以这样做可以确保在调用任何一个函数之前都能够找到所有函数的定义。为了避免在几个地方反复书写同样的代码,最开始的两个函数是简单的工具型函数。

(4) 下面是主菜单函数set_menu_choice。菜单的内容是动态变化的,当用户选择了某张CD唱片后,主菜单中会多出几个选项。

注意,echo –e命令可能不能被移植到某些shell中。

(5) 接下来是两个很短的函数insert_title和insert_track,它们的作用是向数据库文件里添加数据。虽然有的人不喜欢这种长度只有一行的函数,但它们被用在其他函数中时会因为意义明确而便于理解。

接下来的是比较大的函数add_record_reack,它会用到上述两个小函数。这个函数使用模式匹配来确保用户未输入逗号(因为我们把逗号用做数据字段之间的分隔符),用算术操作在用户输入曲目时递增当前曲目的编号。

(6) add_records函数用来输入新CD唱片的标题信息。

(7) find_cd函数的作用是使用grep命令在CD唱片标题文件中查找CD唱片的有关资料。我们需要知道字符串在标题文件里出现的次数,但grep命令的返回值只能告诉我们没有找到该字符串还是找到了多次。为了解决这一问题,我们把grep命令的输出保存到一个临时文件中,文件中的每行对应一次匹配,然后再统计该文件的行数。

单词统计命令wc的输出中使用空格符分隔被统计文件中的行数,单词数和字符个数。我们使用$(wc –l $temp_file)标记从输出结果中提取出第一个参数并赋值给变量linesfound。如果我们要用到wc命令输出中其他靠后的值,我们可以利用set命令把shell参数变量设置为wc命令的输出结果。

我们把IFS(内部数据字段分隔符)设置为一个逗号,这样我们就可以读取以逗号分隔的数据字段了。另一个可选择的命令是cut。

(8) update_cd函数允许我们重新输入CD唱片的有关资料。注意,我们想要搜索(使用grep)的行是以$cdcatnum开头(通过标志^)并且其后跟着一个逗号,因此我们需要把$cdcatnum变量的扩展放在一对花括号({})里,这样就可以搜索紧跟在CD目录编号之后的逗号了。这个函数还用花括号将多个语句组成一个语句块,这用在当get_confirm返回true的情况。

(9) count_cds函数的作用是快速统计数据库中CD唱片个数和曲目总数。

(10) remove_records函数的作用是从数据库文件中删除数据项,它通过grep –v命令删除所有匹配的字符串。注意,我们必须用一个临时文件来完成这一工作。

如果我们使用下面这样的命令:

$title_file文件就会在grep命令开始执行之前被>输出重定向操作设置为空文件,从而使grep命令实际上将从一个空文件里读取数据。

(11) list_tracks字段函数还是使用grep命令来找出我们想要的行,它通过cut命令去处理有关的数据字段,然后通过more命令提供按页输出。如果考虑一下如果用C语言重新实现这段大约20行左右的代码需要多少条语句的话,你就不得不佩服shell是一个功能多么强大的工具了。

(12) 现在已经定义好了所有的函数,可以进入主程序部分了。开头的几行先确保需要的文件处于一个已知状态,然后调用主菜单函数set_menu_choice,再根据它的输出进行相应的操作。

如果用户选择了退出,我们先删除临时文件,再显示结束信息,最后以成功完成条件(退出码为0)退出。

2.8.3  应用程序的说明

脚本程序开始处的trap命令是用来设置在用户按下Ctrl+C组合键时的中断处理,根据终端设置的不同,它可能引发EXIT或INT信号。

实现菜单选择还有其他的办法,特别值得一提的是bash或ksh提供的select结构(它未被列在X/Open规范中)。它是一个专门用来处理菜单选择的结构。如果你并不介意自己的脚本程序的移植性稍差的话,可以考虑使用它。还可以通过here文档来实现为用户提供多行信息。

读者可能已经注意到,开始一个新的CD唱片记录时,并没有对它的主键字进行检查,新代码只是忽略使用同样代码的后续唱片标题,但把它们上面的曲目添加到第一个CD唱片的曲目清单中。如下所示:

我们将把这个问题及其他改进留给读者,请充分发挥你们的想象力和创造力,因为你可以在GPL条款之下对这些代码进行任意的修改。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论