操作系统大作业:在Linux环境下模拟实现简单命令解释器(文档部分)
一、 课程设计(大作业)具体内容
3.1 程序概述
已完成一下命令
pwd //显示当前所在目录的路径名
list <目录名> //列出指定目录名中的所有目录及文件
modifydir <目录名或路径> //改变当前工作目录
makedir <目录名> //新建目录
deldir <目录名> //删除目录
exit //退出命令解释程序
rename <旧文件名> <新文件名> //重命名一个文件或目录
copy <已存在的文件名> <副本文件名或路径> //复制一个已存在的文件
date //显示当前日期
3.2 概念原理
1. 命令行解析:首先,需要从用户输入中解析出命令和其对应的参数。这可以通过使用Linux的`read`函数读取用户输入,然后使用正则表达式或其他方法解析出命令和参数。
2. 命令处理:根据解析出的命令,调用相应的系统函数来实现对应的功能。例如,`pwd`命令可以通过调用`getcwd`函数来实现,`list`命令可以通过调用`listdir`函数来实现。
3. 目录操作:在处理目录相关命令(如`modifydir`、`makedir`、`deldir`)时,需要掌握Linux目录结构的原理,了解`chdir`、`mkdir`、`rmdir`等系统调用。
4. 文件操作:处理文件相关命令(如`rename`、`copy`)时,需要了解文件操作的基本原理,如文件权限、文件描述符等,并使用相应的系统调用(如`rename`、`cp`)来实现。
5. 文件查找:实现`find`命令时,需要使用`find`命令的语法规则,结合Linux的文件系统实现查找功能。
6. 错误处理:在程序运行过程中,可能会遇到各种错误,如输入解析错误、目录操作错误等。需要使用错误处理机制,如`perror`函数,来输出错误信息,提示用户正确操作。
7. 程序循环:使用`while`循环不断接收用户输入,直到用户输入`exit`命令为止。
8. 输出格式:按照题目要求,输出提示符为“姓名拼音@”,需要在程序中设置好输出格式。
9. 测试与注释:在编写程序的过程中,要注重测试每一个功能,确保其正常运行。同时,为代码添加详细的注释,方便他人理解和维护。
总体来说,实现这个简单命令解释器需要掌握Linux系统调用、文件操作、正则表达式解析、错误处理等基本概念和原理。在编写过程中,要注重代码的结构和可读性,确保程序稳定可靠。
3.3 完成情况
已完成以下命令
1.pwd //显示当前所在目录的路径名
2.list <目录名> //列出指定目录名中的所有目录及文件
3.modifydir <目录名或路径> //改变当前工作目录
4.makedir <目录名> //新建目录
5.deldir <目录名> //删除目录
6.exit //退出命令解释程序
7.rename <旧文件名> <新文件名> //重命名一个文件或目录
8.copy <已存在的文件名> <副本文件名或路径> //复制一个已存在的文件
9.date //显示当前日期
其中find功能未实现
图3-1
3.4 详细设计
3.4.1. pwd
//显示当前所在目录的路径名 mypwd
void mypwd() {
char path[100];
getcwd(path,100);
cout<<"当前目录: "<<path<<endl;
}
使用getcwd函数获取当前目录的路径,将其存储在path数组中。getcwd函数默认返回的是当前工作目录的路径,而不是当前运行目录的路径。
3.4.2. list
bool mylist(string dir) {
DIR* d = opendir(dir.c_str());
if(d==NULL) {
return false;
} else {
struct dirent *dirent;
while(dirent=readdir(d)) {
cout<<endl;
cout<<" "<<dirent->d_name<<" "<<dirent->d_type<<" "<<dirent->d_reclen<<endl;
cout<<endl;
}
closedir(d);
return true;
}
}
1. 调用`opendir(dir.c_str())`打开指定的目录。`DIR* d`是一个指向打开目录的指针。
2. 如果打开目录失败(`d==NULL`),函数返回false,表示目录不存在或无法访问。
3. 否则,循环遍历目录下的所有文件和子目录。`dirent`是一个结构体指针,用于存储目录项的信息。
4. 使用`readdir(d)`从目录中读取下一个目录项。
5. 输出目录项的名称(`dirent->d_name`)、类型(`dirent->d_type`)和大小(`dirent->d_reclen`)。
6. 使用`closedir(d)`关闭目录。
7. 循环结束后,函数返回true,表示目录遍历成功。
3.4.3. modifydir
// 改变当前目录-mycd
bool mycd(string path) {
if(chdir(path.c_str())==0) {
return true;
} else {
return false;
}
}
函数内部使用系统调用函数 chdir() 来实现改变当前目录的功能。chdir() 函数接收一个字符串参数,表示要切换到的目录路径。
如果 chdir() 函数执行成功(返回值为 0),说明目录切换成功,函数返回 true。否则,返回 false。
3.4.4.makedir
//新建目录-mymkdir
bool mymkdir(string dir) {
if(mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)==0) {
return true;
} else {
return false;
}
}
1. 使用`mkdir`函数尝试创建目录。`mkdir`函数的原型为:`int mkdir(const char *pathname, mode_t mode),`其中`pathname`为要创建的目录路径,`mode`为权限模式。
2. 为`mkdir`函数传入的`dir.c_str()`,即将目录名称转换为字符串的C风格字符指针。
3. 设置权限模式`S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH`,表示允许用户(U)、组(G)、其他(O)和所有(A)具有读、写、执行权限。
4. 检查`mkdir`函数的返回值。如果返回值为0,表示目录创建成功,函数返回true;否则,返回false。
总之,这段代码的功能是尝试创建一个名为`dir`的目录,如果创建成功,返回true,否则返回false。
3.4.5. deldir
//删除目录-myrmdir
bool myrmdir(string dir) {
if(rmdir(dir.c_str())==0) {
return true;
} else {
return false;
}
}
1. 使用`if`语句检查`rmdir`函数的返回值。如果返回值为0,表示目录删除成功,函数返回true。
2. 如果目录删除失败,函数返回false。
3.4.6. exit
Return 0;直接退出
3.4.7. rename
//重命名文件或目录-myrename
bool myrename(string lastname,string newname) {
if(rename(lastname.c_str(),newname.c_str())==0) {
return true;
} else {
return false;
}
}
该函数接受两个参数,一个是旧文件名(lastname),另一个是新文件名(newname)。函数使用系统调用rename()来执行重命名操作。
3.4.8. copy
//复制文件-mycopy
//先判断文件是否存在,存在则在原来要复制的文件名后加(1),否则直接复制
bool mycopy(string existname,string newname) {
int fo1,fo2;
char buf[1024];
fo1=open(existname.c_str(),O_RDONLY);
if(fo1==-1) {
return false;
} else {
fo2=open(newname.c_str(),O_RDONLY);
if(fo2!=-1) {
int i;
cout<<"确定替换文件?"<<endl;
cout<<"输入1为确定,非1为否定.";
cin>>i;
if(i!=1) {
newname+="(1)";
}
close(fo2);
}
fo2=open(newname.c_str(),O_WRONLY|O_CREAT,S_IRWXU);
int size = read(fo1,buf,sizeof(buf));
write(fo2,buf,size);
close(fo1);
close(fo2);
return true;
}
}
1. 首先,检查源文件是否存在。如果存在,则在原文件名后加(1),否则直接复制。
2. 打开源文件(existname)以只读模式,如果打开失败,返回false。
3. 尝试以只读模式打开目标文件(newname),如果打开成功,但不替换文件,关闭文件并提示用户确定是否替换。
4. 如果用户输入1,则在目标文件名后加(1),否则不加。
5. 以写入和创建模式打开目标文件(newname),读取源文件的内容,并将数据写入目标文件。
6. 关闭源文件和目标文件,返回true表示复制成功。
3.4.9. date
//显示当前日期-mydate
void date()
{
time_t timeval;
(void)time(&timeval);
string timestr;
// invoke the ctime fuction and return the string
timestr = ctime(&timeval);
cout<<"当前时间: "<<timestr<<endl;
}
1. 定义一个时间类型(time_t)的变量timeval,用于存储时间戳。
2. 使用time函数获取当前时间,并将时间戳存储在timeval变量中。注意,这里的time函数调用不需要传入参数,因为它默认使用本地时间。
3. 定义一个空字符串timestr,用于存储转换后的时间字符串。
4. 使用ctime函数将时间戳转换为字符串,并将结果存储在timestr中。ctime函数将时间戳转换为UTC时间,并使用默认的格式字符串("%Y-%m-%d %H:%M:%S")输出。
5. 使用cout输出转换后的时间字符串,并在末尾添加换行符。
3.5 系统测试
图3-2️ 编辑.c文件
图3-3 编译并运行
图3-4 输入tanghan@pwd
图3-5 输入tanghan@list
图3-6 tanghan@list 输出结果
图3-7 输入tanghan@modifydir
图3-8 输入tanghan@makedir
图3-9 新建目录成功
图3-10 输入tanghan@deldir
图3-11 成功删除
图3-12 删除成功
图3-13 输入tanghan@exit
图3-14 输入tanghan@rename
图3-15 改名成功
图3-16 输入tanghan@date显示日期成功
二、 总结
4.1 遇到的困难及解决的问题
4.2 总结与感想
三、 参考文献
[1] 鲍勃·泰勒(Bob Taylor),《Linux命令行与Shell脚本编程大全》(Linux Command Line and Shell Scripting),人民邮电出版社,2016年。
[2] 埃里克·瑞姆(Eric Raymond),《Linux编程指南》(The Linux Programming Interface),机械工业出版社,2014年。
[3] 尼尔·哈丁(Neil Haddad),《Linux命令行与Shell脚本编程从入门到精通》(Linux Command Line and Shell Scripting Boot Camp),电子工业出版社,2019年。
[4] 迈克尔·瓦尔(Michael Wale),《Linux命令行与Shell脚本编程实战》(Linux Command Line and Shell Scripting in Action),人民邮电出版社,2017年。