hnust操作系统课程设计,其他题目较为简单,可参考其他优秀博主的文章,这里仅介绍两道题
本篇为简单 shell 命令行解释器的设计与实现
白嫖容易,创作不易,若大家认为有用,学到东西,不妨点个赞
博客原创,转载请注明出处
附上另一篇博客链接:银行家算法改进实现
仅用于学习,如有侵权,请联系删除
简单 shell 命令行解释器的设计与实现
本实验主要目的在于进一步学会如何在 Linux 系统下使用进程相关的系统调 用,了解 shell 工作的基本原理,自己动手为 Linux 操作系统设计一个简单的命 令接口
要设计的 shell 类似于 sh,bash,csh 等,必须支持以下内部命令:
(1)cd <目录>更改当前的工作目录到另一个<目录>。如果<目录>未指定, 输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示。这个命 令应该也能改变 PWD 的环境变量;
(2)environ 列出所有环境变量字符串的设置(类似于 Linux 系统下的 env 命令);
(3)echo < 内容 > 显示 echo 后的内容且换行;
(4)help 简短概要的输出你的 shell 的使用方法和基本功能;
(5)jobs 输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号;
(6)quit,exit,bye 退出 shell。
提示:shell 的主体就是反复下面的循环过程
while(1)
{ 58 接收用户输入的命令行;
解析命令行;
if(用户命令为内部命令) 直接处理;
else if(用户命令为外部命令)
创建子进程执行命令;
else 提示错误的命令; }
在CLUI(Command Line User Interface,与GUI对应)模式下,用户是通过shell与系统交互的。shell也是一个程序,它与其他程序最大的不同是:它的目的是执行其他程序,而不是从事计算、绘图、存储等等具体事务。
●shell不断询问是否有键盘输入,用户以enter结束键盘输入
●shell执行后,会用户输入的字符串,判断用户输入的命令是否合理,若合理则调用相关函数
●shell 判断命令合理后,会判断命令为内部命令或外部命令,若为外部命令,则提示用户输入非法命令,并需要重新输入
●shell在得到内部命令后,会判断其是否需要使用fork()函数,若不需要fork子进程,则直接根据功能输出即可
●shell判断需要fork子进程后,会在相关函数内fork子进程,并且调用相关库函数,实现相关功能。
流程图:
算法步骤:
命令解释器首先是一个无限循环。
首先在main函数中,我们需要设置一个无限循环,不断让用户输入命令
使用getline()函数进行输入,这里需要注意,必须使用getline,因为输入中包含了空格,用cin会出错
cout << "请输入要执行的命令:(查看帮助请输入help)" << endl;
getline(cin,CMD);//这里必须使用getline,因为输入需要包含空格!
通过if-else结构进行解析命令字符串
判断若输入的是quit、bye、exit命令,则直接break跳出循环,表示退出shell
若命令为help,调用HELP函数输出帮助文档
若判断为echo,则采用字符串切片输出
else if(CMD.find("echo")!=string::npos) cout << CMD.substr(5) << endl;
若判断为environ、jobs、cd这三个命令,此时需要创建子进程,分别调用其函数
ENVIRON函数中,fork创建子进程,使用execlp函数控制执行linux命令env达到该功能,此时需要注意,父进程需要使用waitpid等待子进程结束
核心代码:
execlp("env","",NULL);
pid_t pid;
pid = fork();
if(pid==0)
{
int a = execlp("env","",NULL);
if(a==-1)
{
cout << "执行命令environ失败!" << endl;
exit(0);
}
}
else if(pid<0)
{
cout << "创建子进程失败!" << endl;
exit(0);
}
else if(pid>0) waitpid(pid,NULL,0);//父进程阻塞等待指定子进程结束
JOBS函数中同上,采用execlp函数调用pstree -p命令,以树形结构显示程序和进程之间的关系
execlp("pstree","-p",NULL);
最为困难的是CD函数的实现,该函数需要实现切换到另一个目录,这里采用了chdir()函数进行更改目录,其中利用C++字符串操作,控制其参数Path,更改目录后,再调用execlp,利用ls -a命令输出当前工作目录。这里纠结许久,因为全部使用execlp函数会在第一个execlp函数结束后子进程就直接结束了。
注意事项:其实此处并未完善,没有完全判断输入的情况,由于我懒所以不想改了,有兴趣得同学可以自行修改
其中得 path 目录也需要根据个人得Linux目录更改
if(CMD=="cd "||CMD=="cd")
{
cout << "输出当前工作目录" << endl;
execlp("ls","-a",NULL);
}
else if(CMD.size()>3)
{
string path="/root/"+CMD.substr(3);
cout << chdir(path.c_str()) << endl;
cout << "输出当前工作目录" << endl;
execlp("ls","-a",NULL);
}
cd命令:
env命令:
help命令:
jobs命令:
echo命令:
此次设计一个简单的Shell命令行解释器,进一步理解了Linux系统下使用进程的相关系统调用,进一步理解shell命令解释器的工作原理和工作机制,在此次实验的设计与实践操作过程中,遇到些许问题,例如使用cin输入会出错,必须使用getline输入空格,以及execlp()函数的使用,其中各种参数的意义,以及函数执行后子进程直接退出。
此次实验总体并不难,利用C++字符串基础即可轻松完成大部分功能,但是其中最为困难的是cd命令,需要考虑如何正确转换到指定目录,以及如何在转换后还能输出当前目录。经过查阅资料,解决以上问题,深入理解了shell 命令行解释器工作原理。
源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
string CMD;//传入的命令
void HELP();
void ENVIRON();
void JOBS();
void CD();
void HELP()
{
cout << "-----------------------" << endl;
cout << endl;
cout << "支持的命令\t" << "功能" << endl;
cout << endl;
cout << "cd [目录]\t" << "更改当前目录到另一个<目录>" << endl;
cout << endl;
cout << "environ\t\t" << "列出所有环境变量字符串的设置" << endl;
cout << endl;
cout << "help\t\t" << "列出shell的使用方法和基本功能" << endl;
cout << endl;
cout << "echo [内容]\t" << "显示出echo后的内容并换行" << endl;
cout << endl;
cout << "jobs\t\t" << "输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号。" << endl;
cout << endl;
cout << "quit,exit,bye\t" << "退出 shell。" << endl;
cout << endl;
cout << "-----------------------" << endl;
}
void ENVIRON()
{
cout << "-----------------------" << endl;
pid_t pid;
pid = fork();
if(pid==0)
{
int a = execlp("env","",NULL);
if(a==-1)
{
cout << "执行命令environ失败!" << endl;
exit(0);
}
}
else if(pid<0)
{
cout << "创建子进程失败!" << endl;
exit(0);
}
else if(pid>0) waitpid(pid,NULL,0);//父进程阻塞等待指定子进程结束
cout << "-----------------------" << endl;
}
void JOBS()
{
cout << "-----------------------" << endl;
pid_t pid;
pid = fork();
if(pid>0) waitpid(pid,NULL,0);//父进程阻塞等待指定子进程结束
else if(pid==0)
{
int a = execlp("pstree","-p",NULL);
if(a==-1)
{
cout << "执行命令jobs失败!" << endl;
exit(0);
}
}
else
{
cout << "创建子进程失败!" << endl;
exit(0);
}
cout << "-----------------------" << endl;
}
void CD()
{
cout << "-----------------------" << endl;
pid_t pid;
pid = fork();
if(pid>0) waitpid(pid,NULL,0);//父进程阻塞等待指定子进程结束
else if(pid<0)
{
cout << "创建子进程失败!" << endl;
exit(0);
}
else
{
if(CMD=="cd "||CMD=="cd")
{
cout << "输出当前工作目录" << endl;
execlp("ls","-a",NULL);
}
else if(CMD.size()>3)
{
string path="/root/"+CMD.substr(3);
cout << chdir(path.c_str()) << endl;
cout << "输出当前工作目录" << endl;
execlp("ls","-a",NULL);
}
}
cout << "-----------------------" << endl;
}
int main()
{
while(true)
{
cout << "请输入要执行的命令:(查看帮助请输入help)" << endl;
getline(cin,CMD);//这里必须使用getline,因为输入需要包含空格!
if(CMD=="quit"||CMD=="exit"||CMD=="bye") break;
else if(CMD=="help") HELP();
else if(CMD.find("echo")!=string::npos) cout << CMD.substr(5) << endl;
else if(CMD=="environ") ENVIRON();
else if(CMD=="jobs") JOBS();
else if(CMD.find("cd")!=string::npos) CD();
else cout << "非法命令,请重新输入" << endl;
}
cout << "程序退出!" << endl;
return 0;
}