1、基于 Linux Shell 的 CD 管理系统需求假如我们有一个扩展的 CD 集合.要使得我们的生活更为简单,我们设计和实现在一个管理 CD 的程序.用我们所学的 Linux 编程的知识来实现一个电子目录似乎是一个很不错的主意.我们希望,至少最初是这样的,我们的程序可以存储基本的 CD 信息,例如标签,音乐种类或是艺术家.我们还希望可以保存一些跟踪信息.我们希望可以在每一个 CD 项目上进行搜索,但是却不会在跟踪细节上进行搜索.为了使得我们的程序完整,我们希望可以输入,更新,删除任何信息.设计我们在上面所说的三个需求-更新,搜索,显示数据-指明了我们要实现一个简单的菜单程序.所有我们要存储的
2、内容都是文本的,而且我们在这里假设我们的 CD 集合并不是很大.我们并不需要一个完整的数据库,我们只需要一些简单的文本就可以了.将信息存放在一个文本文件中可以使得我们的程序保持简单,而且如果我们的需求发生变化,处理文本文件总是要比其他类型的文件简单得多.作为最后的一个手段,我们可以手工使用文本编辑器来输入和删除数据,而并需要编写一个程序来完成这样的工作.我们必须为我们的数据存储做出一个重要的设计决定:是不是单一的一个文件就足够了呢?如果是这样,那么他应是什么格式的文件呢?我们所希望存储的大多数的数据信息对于每一个 CD 来说只有一次,当然这样排除跟踪信息.所有的CD 将会有多于一个跟踪信息.我
3、们是否应在我们要存储的每一个 CD 上设置一个数字的极限呢?这看起来是任意的和不必需的限制,所以我们立刻否决了这样的想法.如果我们允许灵活的跟踪数目,我们有下面的三个选项:1 使用单一的文件,使用一行来存放 CD 的标题类信息并使用 N 行来存放 CD的跟踪信息.2 将 CD 的所有信息放在一行,允许行继续直到没有跟踪信息需要存入为止.3 将标题信息与跟踪信息相分离并使用另一个不同的文件进行存储.只有第三个选项可以允许我们灵活的来修正文件的格式,如果曾经希望将我们的数据库转换为相关的格式我们可以做出这样的选择,所以在这里我们会选择第三个选项.下一个决定就是我们要在我们的文件中存放什么内容:最初
4、,对于每一个 CD 标题,我们会选择存放:1 CD 目录标号2 标题3 类型4 作曲家或艺术家对于轨迹内容,我们会简单的存放:1 轨迹标号2 轨迹名字为了能够合并这两个文件,我们必须将轨迹信息与 CD 的其他信息相关联.为了这样做,我们会使用 CD 目录编号.因为这对于每一个 CD 来说是唯一的,这样他就会在标题文件和轨迹文件中只出现一次.下面让我们来看一下一个简单的标题文件的例子:目录 标题 类型 作曲家CD123 Cool sax Jazz BixCD234 Classic violin Classical Bach CD345 Hits99 Pop Various他们所对应的轨迹信息如下
5、:目录 轨迹标号 标题CD123 1 Some jazzCD123 2 More jazzCD345 1 DizzyCD234 1 Sonata in D minor这两个文件使用目录域进行联合.在这里我们要记住,对于标题文件中的每一个实体在轨迹文件中会有多行与之对应.我们要做的最后一件事就是如何来区分这些实体.在关系数据库中通常采用确定宽度的域,但是并不总是合适的.另一个常用的办法就是逗号,而这也是我们这里所采用的方法.在我们下面的部分中,为了使得我们不至于迷惑,我们会使用下面的一些函数:get_return()get_confirm()set_menu_choice()insert_tit
6、le()insert_track()add_record_tracks()add_records()find_cd()update_cd()count_cds()remove_records()list_tracks()一个 CD 程序1 在我们的例子程序中,第一行总是要保证这个程序要作为一个 Shell 脚本来执行,接下来的是一些版权信息:#!/bin/sh# Very simple example shell script for managing a CD collection.# Copyright (C) 1996-2003 Wrox Press.# This program is
7、free software; you can redistribute it and/or modify it# under the terms of the GNU General Public License as published by the# Free Software Foundation; either version 2 of the License, or (at your# option) any later version.# This program is distributed in the hopes that it will be useful, but# WI
8、THOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General# Public License for more details.# You should have received a copy of the GNU General Public License along# with this program; if not, write to the Free Software Foundat
9、ion, Inc.# 675 Mass Ave, Cambridge, MA 02139, USA.2 我们要做的第一件事就是保证我们在整个脚本中使用的全局变量已经进行设置.我们要设置标题文件,一个轨迹文件以及一个临时文件.我们同时要跟踪Ctrl+C 操作,这样如果用户中断了脚本,我们可以保证删除临时文件.menu_choice=”current_cd=”title_file=”title.cdb”tracks_file=”tracks.cdb”temp_file=/tmp/cdb.$trap rm -f $temp_file EXIT3 现在我们要定义我们需要的一些函数,这样脚本从顶行开始执
10、行,在我们试着第一次调用这些函数时可以找到这些函数的定义.为了避免在一些重写一些相同的代码,最初的两个函数是简单的实用程序.get_return() echo -e “Press return c”read xreturn 0get_confirm() echo -e “Are you sure? c”while truedoread xcase “$x” iny | yes | Y | Yes | YES )return 0;n | no | N | No | NO )echoecho “Cancelled”return 1;*) echo “Please enter yes or no”
11、;esacdone4 现在我们来定义主要的菜单函数,set_menu_choise.菜单的内容可以动态的变化,如果一个 CD 实体被选择可以增加一些其他的选项.set_menu_choice() clearecho “Options :-”echoecho “ a) Add new CD”echo “ f) Find CD”echo “ c) Count the CDs and tracks in the catalog”if “$cdcatnum” != “” ; thenecho “ l) List tracks on $cdtitle”echo “ r) Remove $cdtitle”
12、echo “ u) Update track information for $cdtitle”fiecho “ q) Quit”echoecho -e “Please enter choice then press return c”read menu_choicereturn5 现在是两个非常短小的函数,insert_title 和 insert_track,这样可以增加数据库文件.虽然一些讨厌这样的内容,但是他们却可以使得其他的函数看起来更为简洁.紧随着他们的是一个较大的函数定义,add_record_track,在这其中使用前面的两个函数.这个函数使用模式匹配从而保证用户没有输入逗号(
13、因为我们要使用逗号作为区域分隔符),并且当输入了轨迹信息后使用算村运算来增加轨迹数.insert_title() echo $* $title_filereturninsert_track() echo $* $tracks_filereturnadd_record_tracks() echo “Enter track information for this CD”echo “When no more tracks enter q”cdtrack=1cdttitle=”while “$cdttitle” != “q” doecho -e “Track $cdtrack, track titl
14、e? c”read tmpcdttitle=$tmp%,*if “$tmp” != “$cdttitle” ; thenecho “Sorry, no commas allowed”continuefiif -n “$cdttitle” ; thenif “$cdttitle” != “q” ; theninsert_track $cdcatnum,$cdtrack,$cdttitlefielsecdtrack=$(cdtrack-1)ficdtrack=$(cdtrack+1)done6 add_records 函数允许将一个主 CD 的信息实体作为一个新的 CDadd_records()
15、# Prompt for the initial informationecho -e “Enter catalog name c”read tmpcdcatnum=$tmp%,*echo -e “Enter title c”read tmpcdtitle=$tmp%,*echo -e “Enter type c”read tmpcdtype=$tmp%,*echo -e “Enter artist/composer c”read tmpcdac=$tmp%,*# Check that they want to enter the informationecho About to add ne
16、w entryecho “$cdcatnum $cdtitle $cdtype $cdac”# If confirmed then append it to the titles fileif get_confirm ; theninsert_title $cdcatnum,$cdtitle,$cdtype,$cdacadd_record_trackselseremove_recordsfireturn7 find_cd 函数使用 grep 命令在 CD 标题文件中查找指定的目录名字的文本.我们需要知道查找到多少次字符串,但是 grep 命令只会在他匹配了零次或是多次时才返回一个值.因为这样的
17、情况,我们在一个文件中存储这些输出,其中每一个行匹配一个,然后我们可以计算这个文件中的行数.字数统计命令 wc,在他的输出中使用空格来分隔行数,字数以及文件中的字符数.我们使用$(wc -l $temp_file)命令来从这个命令的输出中得到第一个参数来设置 linesfound 变量.如果我们希望得到其他的后面的参数,我们可以使用set 命令来设置命令输出的 Shell 参数变量.我们将 IFS 设置为逗号,这样我们就可以使用逗号来进行分隔了.另一个办法就是使用 cut 命令.find_cd() if “$1” = “n” ; thenasklist=nelseasklist=yficdca
18、tnum=”echo -e “Enter a string to search for in the CD titles c”read searchstrif “$searchstr” = “” ; thenreturn 0figrep “$searchstr” $title_file $temp_fileset $(wc -l $temp_file)linesfound=$lcase “$linesfound” in0) echo “Sorry, nothing found”get_returnreturn 0;1) ;2) echo “Sorry, not unique.”echo “Fo
19、und the following”cat $temp_fileget_returnreturn 0esacIFS=”,”read cdcatnum cdtitle cdtype cdac $temp_filemv $temp_file $tracks_fileechoadd_record_tracksfireturn9 count_cds 将会返回我们数据库内容的一个快速计数.count_cds() set $(wc -l $title_file)num_titles=$lset $(wc -l $tracks_file)num_tracks=$lecho found $num_titles
20、 CDs, with a total of $num_tracks tracksget_returnreturn10 remove_records 将断开数据库文件与实体之间的联系,使用 grep -v 命令来移除所有匹配的字符串.在这里我们要注意的是我们必须使用临时文件.如果我们试着使用下面的命令:grep -v “$cdcatnum” $title_file在 grep 命令有机会执行之前$title_file 已经使用输出重定向设置为一个空文件,所以 grep 命令将会读取一个空文件.remove_records() if -z “$cdcatnum” ; thenecho You mu
21、st select a CD firstfind_cd nfiif -n “$cdcatnum” ; thenecho “You are about to delete $cdtitle”get_confirm thenecho no CD selected yetreturnelsegrep “$cdcatnum,” $tracks_file $temp_filenum_tracks=$(wc -l $temp_file)if “$num_tracks” = “0” ; thenecho no tracks found for $cdtitleelse echoecho “$cdtitle
22、:-”echocut -f 2- -d , $temp_fileecho | $PAGER:-morefifiget_returnreturn12 现在所有的函数都已经进行定义,我们可以进入我们的主要部分了.最初的几行可以简单的将文件读入到一个已知的状态,然后我们调用菜单函数,set_menu_choice 并且反应在输出.当选择了 quit,我们删除这个临时文件,输出信息,并且已成功条件返回.rm -f $temp_fileif ! -f $title_file ; thentouch $title_filefiif ! -f $tracks_file ; thentouch $tracks
23、_filefi# Now the application properclearechoechoecho “Mini CD manager”sleep 1quit=nwhile “$quit” != “y” ;doset_menu_choicecase “$menu_choice” ina) add_records;r) remove_records;f) find_cd y;u) update_cd;c) count_cds;l) list_tracks;b)echomore $title_fileechoget_return;q | Q ) quit=y;*) echo “Sorry, choice not recognized”;esacdone#Tidy up and leaverm -f $temp_fileecho “Finished”exit 0