OS课程设计
- 修改md命令
- 修改命令行预处理程序
- 新增fc命令,实现两个文件的比较
- 新增replace命令,实现文件取代
- 新增move命令,实现文件“搬家”及子目录改名
- 新增batch命令,实现批处理
- 修改close、type等命令,允许不带文件名参数
- 修改del、copy等命令,使其可以使用统配符 *(选做内容)
- 完善copy命令,改正某些缺陷(选做内容)
- 完善copy命令,允许覆盖同名文件(选做内容)
- 增加输出重定向功能(选做内容)
- 修改copy命令,增加合并复制功能(选做内容)
- 修改程序,实现多磁盘文件系统(选做内容)
- 修改磁盘块容量(选做内容)
- 修改undel命令(选做内容)
- 修改read与write命令,增加读写位置、长度参数(选做内容)
- 修改read与write命令,允许不带“文件名”参数(选做内容)
- 修改write命令,增加“删除”方式(选做内容)
修改md命令: 当前的md命令在创建目录时只检查和是否存在同名目录,未检查是否存在同名文件。例如,在参考程序中执行:
md bin
由于根目录已经存在名为bin的目录,会提示“错误:目录重名”,但执行:
md test
尽管根目录存在一个名为test的文件,创建仍能继续。正确的做法应该是功能完整的演示程序中,显示“错误:目录与现有目录或文件重名!”
请尝试修复以上错误。
修改命令行预处理函数ParseCommand( )
,使以下命令行(命令与参数间无空格符)可以正确执行:
cd/
cd..
dir/usr
copy/usr/boy mail
……
即命令名称与后边的“/”或“..”之间不需要空格也能正确执行。
【说明】参考程序已经可以处理类似于“cd/”的形式,但还不能处理“cd..”等形式。另外,若有输出重定向功能,“>”、“>>”也允许不是用空格分隔符(即允许与其它参数连用)。例如:type/usr/boy>>/test
命令形式:
fc <文件名1> <文件名2>
命令功能:
逐个字节比较指定的两个文件,若相同,显示“文件内容相同”字样;若不同,显示第一个不同字节的位置和各自的内容。若文件不存在则报错。
命令形式:
replace <文件名> <目录名>
命令功能:
以“文件名”指定的文件,取代“目录名”指定目录中的同名文件。
请按以下要求设计replace命令的函数:
-
若指定文件或被取代文件不存在,则报错;
-
若被取代的是只读属性的文件时,应询问用户;
-
具有隐藏和系统属性的文件不能被取代;
-
若目录名参数缺省,则取代当前目录的同名文件;
-
文件名指定的文件和被取代的文件不应该是同一个文件,即不能自己取代自己。
命令形式:
move <文件名> <目录名>
命令功能:
将“文件名”指定的文件或目录,移动到“目标名”指定目录中(名字保持不变)。此命令也可改变子目录名。“文件名”指定被操作的文件或目录,可以是目录名;“目录名”指定“文件名”的新位置或子目录的新名字,即必须是目录名,不能是文件名。文件或子目录转移时不能改名。
-
若转移的是一个文件,且目录名指定的目录原来已经存在,则将该文件转移到指定目录中;若指定目录中有同名文件,则询问是否覆盖,若同意则覆盖之。
-
若转移的是一个子目录,而“目录名”指定的子目录不存在,则move命令执行子目录改名操作(必须是同一目录内);若指定目录存在,则将“文件名”指定的目录转移到该目录中,但若指定目录中存在与“文件名”指定的目录同名的子目录,则报错。
move命令的要点归纳如下:
(1) 文件或目录,无重名时,可转移到目标目录中(只能同名转移,转移必定是不同目录的);
(2) move命令可对子目录改名,但不能用于文件改名(改名操作必定是同目录进行的);
(3) 转移时,文件可覆盖文件,目录也可覆盖文件;但文件或目录都不能覆盖目录。
应用举例:
① 将当前目录中的子目录lin,改名为chen,可输入如下命令:
move lin chen
【注】前提是lin是当前目录中的一个子目录,而当前目录中无chen子目录和chen文件。
② 将当前目录中的子目录lin,转移到子目录/bin中,可输入如下命令:
move lin /bin
【注】前提是lin是当前目录中的一个子目录,而根目录中已存在子目录bin,且/bin中无名字为lin的子目录。此命令可覆盖子目录/bin中的名字为lin的文件(要询问用户)。
③ 将目录/usr中的文件boy,转移到当前目录下的bin子目录中,可以输入如下命令:
move /usr/boy bin
【注】前提是boy是目录/usr中的一个文件,而当前目录中已存在子目录bin,且bin中无名字为boy子目录。若bin目录中已存在文件boy,则询问用户是否覆盖。
【说明】新增move命令和新增batch命令两项工作可任选其一。
(注:设计此命令与设计move命令命令两项工作可任选其一)
命令形式:batch batchfile
命令功能:从磁盘文件batchfile逐行读入命令行执行,直到batchfile中所有命令行都执行完毕为止。
batchfile是一个预先编好的文本文件,其内容是多条可执行命令。例如,预先编好文本文件test.txt,其内容为:
cd /usr
dir
type boy
copy boy user/box
则执行命令batch test.txt,系统将依次自动执行cd /usr、dir、type boy和copy boy user/box四条命令,跟你用键盘依次输入上述四条命令的执行过程和执行效果完全相同。
注:
文本文件可以在“记事本”或MSDEV或其他任何文本编辑环境中编写。利用此命令,可以改写(简化)目录初始化程序,建立如图1所示的初始目录结构。
功能较完整的演示程序“OS实验.exe”,除了从真正的磁盘文件读入并执行批处理命令外,batch命令还提供了如下形式(带一个参数“s”):
batch filename s
该命令从模拟盘Disk[][]中读入批处理文件,并逐条执行命令,例如,模拟盘根目录中有文件test,其内容如下:
dir
type map
cd usr
dir
open /boy
cd user/ma
type qil_nanj
cd /
uof
close boy
则在当前目录为根目录时,键入命令:
C:/>batch test s (下划线部分为键入内容)
系统执行过程如下:
C:/>dir
The Directory of C:/
bin
usr
auto 0
dev
boy 170
abc_map 203
map 76
test 77
5 file(s) 526 bytes
3 dir(s) 313856 free
C:/>type map
A A A A A A A A A A A A A A
A A
C:/>cd usr
C:/usr>dir
The Directory of C:/usr
..
user
lib
bin
boy 270
cat 171
dirfile 279
dir0 281
4 file(s) 1001 bytes
4 dir(s) 313856 free
C:/usr>open /boy
文件/boy打开成功。
C:/usr>cd user/ma
C:/usr/user/ma>type qil_nanj
七律 人民解放军占领南京
1949.04
钟山风雨起苍黄,百万雄师过大江。
虎踞龙盘今胜昔,天翻地覆慨而慷。
宜将胜勇追穷寇,不可沽名学霸王。
天若有情天亦老,人间正道是沧桑。
C:/usr/user/ma>cd /
C:/>uof
打开文件表UOF的内容如下:
文件名 文件属性 首块号 文件长度 状态 读指针 写指针
/boy 普通 40 170 打开 1 171
C:/>close boy
关闭文件/boy成功。
从上可见,其执行过程与直接键入命令完全相同。
修改close、type等命令,使其可以使用如下命令形式:
(1) close命令改成可用如下形式:
close <文件名>——关闭指定文件
close——关闭当前操作文件(新增形式)
(2) type命令改成可用如下形式:
type <文件名>——显示指定文件内容
type——显示当前操作文件内容(新增形式)
所谓“当前操作文件”,是指上一次使用create、open、write、read、rewind或fseek等命令操作过的文件。
另外,fseek、rewind、block等非正规操作系统命令也可增加没有文件名的命令形式。如修改fseek命令后,使它可用如下2种命令形式:
fseek <文件名> |p——将指定文件的读、写指针移到文件第n字节处。此处“|p”表示位置,“”表示某个整数(此形式与参考程序对应命令功能相同)。
fseek |p——将当前操作文件的读、写指针移到文件第n字节处(新增形式)
为了将文件名与位置参数区别开来,位置参数前加字符“|”以便与文件名区别。
修改某些命令,使其可以使用统配符“*”。例如:
del * ——删除当前目录中的所有文件
attrib * ——显示当前目录中所有文件和子目录的属性
copy * <目标目录> ——将当前目录中的所有文件复制到目标目录中(同名复制)
目前参考程序中的copy命令不能处理如下情况:假设当前目录是/usr,执行如下命令之一
copy boy /
copy boy ..
上述2条命令应该是将当前目录/usr中的文件boy复制到其父目录(根目录)中,文件名仍为boy。但系统不能正确执行,而显示“目标文件名错误”的信息。正确的功能应该是:
-
若当前目录中不存在文件boy,则报错;
-
第2个命令若当前目录是根目录,因根目录没有父目录,故应报错;
-
若当前目录的父目录中已经存在名为boy的文件,也报错(或询问用户是否覆盖)。
-
其他情况上述命令都应能正确执行。
请按此要求修改copy命令处理程序,使之在上述命令形式也能正确执行。
参考程序中的copy命令,为了程序设计方便,当目标文件存在同名文件或同名子目录时,就停止执行复制操作。可模仿DOS操作系统,修改copy命令,使当目标文件存在同名文件时,询问用户是否覆盖,得到肯定回答时,即使该同名文件是只读文件,也仍然复制(覆盖原先文件,即与目标文件同名的文件被删除)。例如,当执行命令:copy boy /usr/test时,若子目录/usr中已存在文件test,则询问是否覆盖;若test是子目录名,则将文件boy复制到子目录/usr/test下,文件名与源文件相同,即boy。但是如果/usr/test/boy仍为子目录,则显示错误信息,停止执行copy命令。
某些命令执行结果在屏幕显示,可以使其输出结果保存到某个文件中(屏幕不再显示),这就是所谓的“输出重定向”功能。例如,可以修改如下命令,使之具有输出重定向功能(输出重定向用符号“>”或“>>”表示):
命令形式1:type <文件名1> > <文件名2>
命令功能:将原先应该显示的“文件名1”指定的文件内容,保存到“文件名2”指定的文件中。文件名2指定文件的原先内容被删除。这相当于复制文件。
命令形式2:type <文件名1> >> <文件名2>
命令功能:将原先应该显示的“文件名1”指定的文件内容,保存到“文件名2”指定的文件中。文件名2指定文件的原先内容不删除,新内容接到原先内容尾部。这相当于合并复制文件。
type命令增加了输出重定向功能后,共有3种命令形式(不考虑缺省文件名1的情况时)。
命令形式1:dir [<目录名>] [|<属性符>] > <文件名>
命令功能:将原先应该显示的指定目录中指定属性的文件名和子目录名等内容,保存到文件名指定的文件中。文件名指定文件的原先内容被删除。
命令形式2:dir [<目录名>] [|<属性符>] >> <文件名>
命令功能:将原先应该显示的指定目录中指定属性的文件名和子目录名等内容,保存到文件名指定的文件中。文件名指定文件的原先内容不删除,新内容接到原先内容尾部。
具体地讲,dir命令在原有的4种形式的基础上,增加了输出重定向功能后,增加了8种新的命令形式(这样,dir共有12种命令形式),以下列出8种新的命令形式:
① dir > <文件名> ——将原先应该显示的当前目录中普通属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则原内容删除。
② dir <目录名> > <文件名>——将原先应该显示的指定的目录中普通属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则原内容删除。
③ dir |<属性> > <文件名> ——将原先应该显示的当前目录中指定属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则原内容删除。
④ dir <目录名> |<属性> > <文件名> ——将原先应该显示的“目录名”指定的目录中指定属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则原内容删除。
⑤ dir >> <文件名> ——将原先应该显示的当前目录中普通属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则新内容接到原内容的尾部。
⑥ dir <目录名> >> <文件名> ——将原先应该显示的“目录名”指定的目录中普通属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则新内容接到原内容的尾部。
⑦ dir |<属性> >> <文件名> ——将原先应该显示的当前目录中指定属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则新内容接到原内容的尾部。
⑧ dir <目录名> |<属性> >> <文件名> ——将原先应该显示的“目录名”指定的目录中指定属性的文件及第一级子目录名保存到“文件名”指定的文件中。若指定文件已存在,则新内容接到原内容的尾部。
【建议】为方便编成,可以利用“字符串流”的方法,先将要显示的内容,保存到字符数组中,然后再根据要求决定将该字符数组显示还是存盘。
命令形式:copy <文件名1>+<文件名2> [<文件名3>]
命令功能:文件1和文件2合并,复制到文件3中,其中文件2接到文件1的尾部(文件1、文件2不变)。若文件名3缺省,则目标文件与文件1同名。若文件1、文件2不存在,则报错。若文件3有重名冲突,则询问用户决定是否覆盖。应用举例(假设当前目录是/usr):
(1) copy user/boy+test bin
上述命令将当前目录中的文件test,连接到当前目录中的user子目录中文件boy尾部,复制到当前目录的文件bin中,文件boy和test内容不变。
l 若bin是目录名,则目标文件复制到bin子目录中,文件名为boy。若bin中已经存在文件boy,则询问是否覆盖它;若bin中的boy仍是子目录,则报错;
l 若当前目录中已经存在文件bin,则询问是否覆盖它;若/usr/user/boy或/usr/test不 存在,则报错。
(2) copy user/boy+test
上述命令将当前目录中的文件test,连接到当前目录中的user子目录中文件boy尾部,复制到当前目录中,文件名为boy。
l 若当前目录中已存在文件boy,则询问是否覆盖它;
l 若当前目录中存在boy子目录,则目标文件复制到/usr/boy中,文件名为boy;
(3) copy boy+bin/test 该命令将当前目录中bin子目录中的文件test,连接到当前目录中的文件boy尾部,连接后的内容复制到当前目录的文件boy中(因要改变boy的内容,故在复制时要询问用户是否覆盖boy)。
参考程序中只有一个磁盘(设定为C盘),修改程序,实现多磁盘系统(例如有C、D等磁盘)。多磁盘时,每个磁盘有一个FAT表,每个磁盘有一个当前目录,每个磁盘有一个根目录,整个系统有一个当前磁盘。这种系统应增加选择当前磁盘的命令,例如命令:
D:
该命令的功能是:选择D盘作为当前盘。
另外,这种系统的路径名开头可有盘符。例如:D:/doc/test,代表D:盘根目录下doc子目录中的文件test。
【修改提示】假设磁盘个数为2。文件分配表FAT可改为二维数组FAT[2][K]
(磁盘块数为K);模拟磁盘可用三维数组Disk[2][K][SIZE]
(K为磁盘块数,SIZE为磁盘块容量,例如,K=1000,SIZE=256)。当前目录用结构类数组curpath[2]记录。增设全局变量d_no,用于记录当前磁盘号(其值为0,表示当前磁盘为C:盘,其值为1,表示表示当前磁盘为D:盘)。其他修改不再赘述,例如必须修改路径分析处理函数FindPath()
和ProcessPath()
等等。
【注】功能较全的程序“OS实验.exe”中有三个磁盘(C:、D:和E:)。
将磁盘块容量SIZE由64改为256字节(这需要对程序中跟此有关的部分作相应的改动),磁盘块容量增大为256后,可将删除文件恢复表udtab存储到磁盘中。为此,需修改UnDel的结构,例如,UnDel的结构可修改如下:
struct UnDel //恢复被删除文件的数据结构(共128字节)
{
char gpath[112]; //被删除文件的全路径名(不含文件名)
char ufname[FILENAME_LEN]; //被删除文件名
char ufattr; //被删除文件属性
short ufaddr; //被删除文件的首块号
short fb; //存储被删除文件长度及块号的指针(首块号)
};
每个盘块可以存放2个udtab表的表项。为方便计,udtab表的长度仍可限定为40,可在磁盘中开辟一个大小为20个磁盘块的连续存储空间存放udtab表。udtab表存储在连续的磁盘空间的优点是:可用数组的方法直接处理Disk中的udtab表。退出系统时,无需将udtab表存盘,启动系统时,无需读入udtab表。udtab的块号链的首块号记录在Disk[0]中。
可考虑3种修改方案,每个同学可任选其中一种修改方案。
此方案应修改UnDel的数据结构,以便udtab在磁盘中存储。例如可用如下结构:
struct UnDel //恢复被删除文件表的数据结构
{
char ufname[FILENAME_LEN]; //被删除文件名
char fattr; //被删除文件的属性
short gpath;//它所指的盘块链存储被删除文件的全路径名(不含文件名)
short fb; //它所指的盘块链存储被删除文件的块号(含文件的首块号)
};
这样处理,可使每个盘块存储4个表项。这种修改方式处理思路与参考程序相同,但因结构类型UnDel改变了,故del和undel命令的处理函数都要做相应修改。另外,系统启动时,不再需要从文件UdTab2008.dat读入udtab表的数据,退出系统时,也不再需要将udtab表存入文件UdTab2008.dat。
上述UnDel结构中gpath所指的盘块链最多2个盘块(本系统中,去掉文件名后,路径名不超过115字节)。fb所指的盘块链的盘块数视文件占用的盘块数而定,因盘块容量为64B,每个盘块可存放32个块号,故文件占用块数不超过32时,fb所指盘块链只有1个盘块,其余类推。
(1) 参考程序中的undel命令,只恢复指定目录中被删除的文件,并不处理指定目录以外被删除文件的恢复问题,使用不太方便。修改程序,使undel处理所有被删除文件,命令格式改为:undel,即命令中不带“目录名”参数,命令的处理过程是:逐个显示udtab表中记录的被删除文件的信息,询问用户是否恢复它,肯定回答时恢复之(恢复操作过程与原先相似),否则不恢复。
(2) 参考程序中,当被删除文件原先的目录项被它用后,即使被删除文件原先占用的盘块没有被占用,也不能再恢复该文件,这是因为UnDel结构中没有包括文件长度等信息,若UnDel结构中包含FCB的全部信息,就可以做到即使被删除文件原先的目录项被占用,仍可以恢复该文件,只要该文件的磁盘块没有被占用。请按此思路修改程序,提高对被删除文件的恢复能力。
为了实现(2)的功能,建议可采用如下措施:
-
修改磁盘块容量为256字节;
-
UnDel结构为:
struct UnDel //恢复被删除文件的数据结构(共128字节)
{
char gpath[112]; //被删除文件的全路径名(不含文件名)
char ufname[FILENAME_LEN]; //被删除文件名
char ufattr; //被删除文件属性
short ufaddr; //被删除文件的首块号
short fb; //存储被删除文件长度及块号的指针(首块号)
};
其中fb所指盘块中第一个值为文件长度值。每个盘块可存储2个表项。
- udtab的表项数仍假定为40,在磁盘中开辟一个有20个盘块的连续空间,用来存储udtab表。设这20个盘块的连续空间的首块号为b1,则udatb表可定义如下:
UnDel *udtab=(UnDel*) Disk[b0];
这样,udtab实际上仍然是一个数组,对udtab表的操作,与参考程序一样(只是其表项的结构有所不同)。退出系统时,不再需要将udtab单独存盘,只需将模拟磁盘Disk[K][SIZE]
存盘即可。
- 修改undel等有关程序。
参考程序实现undel命令的思路是:删除文件将释放磁盘空间,但将它的有关目录项信息以及它占用的盘块号存储到udtab表中,恢复文件时,根据udtab表中文件原先占用的盘块号信息,恢复该文件。这是DOS等操作系统采用的方法。请修改undel的实施方案,采用类似于Windows的回收站方案,实现被删除文件恢复功能。回收站方案的基本思路是:被删除文件占用的磁盘空间并不释放,不过将它从它所在目录,“搬到”一个特殊文件夹——回收站中,当需要恢复它时,再将它从回收站“搬回”它原先所在目录。真不需要该文件时,将它从回收站清除(这时才真正释放磁盘空间)。解决方案之一是:在根目录中建立一个特殊的文件夹recycled (其属性为:只读、隐藏、系统),称recycled为回收站,其表项的数据结构仍采用前面介绍的FCB结构,但其中的成员变量Fsize,不用来存储文件长度,而用来存储一个盘块号,该盘块中存储被删除文件长度和文件的全路径名(不含文件名),这里的“全路径名”就是文件的原位置,还原文件时该信息是不可或缺的。
dir等命令处理recycled文件夹时,与普通文件夹略有不同(因其文件长度等信息要从Fsize号盘块中取出,不能直接获得)。rd命令应修改成不能删除文件夹recycled,copy, move, replace等命令也改成不能对文件夹recycled操作。功能较完整的程序“OS实验.exe”中可以演示udtab表恢复被删除文件,也可演示利用“回收站”还原被删除文件。
【注】采用回收站方案的系统,磁盘块容量应≥128字节,否则一个盘块可能存放不下文件长度和文件的全路径名。程序“OS实验.exe”中的盘块容量是256字节,该程序实现了“回收站”功能。
当前的参考程序无法正确处理read和write命令的参数,修改read命令处理函数ReadComd()以及write命令处理函数WriteComd(),使其在使用如下基础命令形式:
read <文件名>——读指定文件,从读指针位置开始读到文件尾部(现有形式)
write <文件名> ——在写指针当前所指位置写,写入内容代替原内容(现有形式)
以及以下新增形式:
read <文件名> |pm——读指定文件,从指定位置m开始读到文件尾部(新增形式)
read <文件名> |ln——读指定文件,从读指针位置开始读n个字节(新增形式)
read <文件名> |pm |ln——读指定文件,从指定位置m开始读n个字节(新增形式)
write <文件名> |ins——在写指针所指位置写,写入处开始的原内容后移(新增形式)
write <文件名> |pn——在文件开头第n个字节处写,改写方式(新增形式)
write <文件名> |pn |ins——在文件开头第n个字节处写,插入方式(新增形式)
write <文件名> |app——在文件尾部以添加方式写入新内容(新增形式)
上述命令中,“|p”表示“位置”,“m”表示某个整数。“|L”表示“长度”,“n”也代表某个整数。
【思考】如何修改程序,使参数“insert”、“append”只要前3个字符对就可以,但多于3个字符也行。例如:对于“insert”,输入ins、inse、inser、insert(不区分大小写)都可以,输入其它不行。
修改read命令处理函数ReadComd()以及write命令处理函数WriteComd(),允许其命令中不带“文件名”参数。这样,新增4种新的read命令形式:
read——读当前操作文件,从读指针位置开始读到文件尾部(新增形式)
read |pm |ln——读当前操作文件,从指定位置m开始读n个字节(新增形式)
read |pm——读当前操作文件,从指定位置m开始读到文件尾部(新增形式)
read |ln——读当前操作文件,从读指针位置开始读n个字节(新增形式)
同时新增加如下5种不带文件名的write命令形式:
write——在写指针当前所指位置写,写入内容代替原内容(代替方式或改写方式)
write |pn——在文件开头第n个字节处写,改写方式
write |ins——在写指针所指位置写,写入处开始的原内容后移(插入方式)
write |pn |ins——在文件开头第n个字节处写,插入方式
write |app——在文件尾部写(添加方式)
除了插入、改写方式外,在3的基础上,还可以考虑增加“删除”方式,这样,又可增加如下命令形式:
write <文件名> |del——对指定文件,从写指针位置删除到文件末尾
write |del——对“当前操作文件”,从写指针位置删除到文件末尾
write <文件名> |lm |del——对指定文件,从写指针位置开始,删除m个字节
write |lm |del——对“当前操作文件”,从写指针位置开始,删除m个字节
write <文件名> |pn |del——对指定文件,从指定位置n处开始删除到文件末尾
write |pn |del——对“当前操作文件”,从指定位置n处开始删除到文件末尾
write <文件名> |pn |lm |del——对指定文件,从指定位置n处开始删除m个字节
write |pn |lm |del——对“当前操作文件”,从指定位置n处开始删除m个字节
【注】没有完成4.17的工作,也可以在write命令中增加“删除”功能,不过上述8条有关write删除功能的命令,只有4条带文件名参数的命令可以使用。