目录
前言
本实验为课设内容,博客内容为部分报告内容,仅为大家提供参考,请勿直接抄袭,另外,本次实验所用平台是Ubuntu 22.04 LTS,代码均是在终端进行编译的,不会的可以先了解怎么用终端编程,或者利用其他较为智能的开发环境进行编程
1 实验题目
实验十 实现一个简单的shell命令行解释器
2 实验目的
根据实验五中所熟悉的 P、V 原语对应的实际 Windows API 函数,并参考教材中读者-写者问题的算法原理,尝试利用 Windows API 函数实现第一类读者-写者问题(读者优先)。
3 实验内容
3.1 步骤
要设计的 shell 类似于 sh,bash,csh 等,必须支持以下内部命令:
cd <目录>更改当前的工作目录到另一个<目录>。如果<目录>未指定,输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示。这个命令应该也能改变 PWD 的环境变量。
environ 列出所有环境变量字符串的设置(类似于 Linux 系统下的 env 命令)。
echo <内容 > 显示 echo 后的内容且换行
help 简短概要的输出你的 shell 的使用方法和基本功能。
jobs 输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号。
quit,exit,bye 退出 shell。
提示:shell 的主体就是反复下面的循环过程
while(1){
接收用户输入的命令行;
解析命令行;
if(用户命令为内部命令)
直接处理;
else if(用户命令为外部命令)
创建子进程执行命令;
else
提示错误的命令;
}
3.2 关键代码
string CMD;//指令
string path="/实验10/";//保存路径
while(1){
cout<<"请输入指令:";
getline(cin,CMD);
if(CMD=="exit"||CMD=="quit"||CMD=="bye")break;
else if(CMD=="help"){
path.clear();//清除路径
path="/实验10/";
cout<<"cd <目录>更改当前的工作目录到另一个<目录>。";
cout<<"如果<目录>未指定,输出当前工作目录。如果<目录>不存在,会提示错误信息。"<<endl;
cout<<"environ 列出所有环境变量字符串的设置。"<<endl;
cout<<"echo <内容 > 显示 echo 后的内容且换行。"<<endl;
cout<<"help shell的使用方法和基本功能。"<<endl;
cout<<"jobs 输出 shell 当前的一系列子进程。"<<endl;
cout<<"quit 退出 shell。"<<endl;
cout<<"exit 退出 shell。"<<endl;
cout<<"bye 退出 shell。"<<endl;
}
else if(CMD.find("echo")!=string::npos){
path.clear();//清除路径
path="/实验10/";
cout << CMD.substr(5) << endl;
}
else if(CMD=="environ"||CMD=="jobs"||CMD.find("cd")!=string::npos){
if(CMD.find("cd")!=string::npos){
if(CMD.size()>3){//将路径放在这里处理是因为在下面处理会把路径清除无法保存之前输入的路径
path=path+CMD.substr(3)+"/";
}else{
cout<<"当前工作目录:"<<path<<endl;
}
}
pid_t pid;
/* fork a child process */
pid=fork();
if (pid < 0)
{ /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}else if(pid==0){
/* 子进程 */
if(CMD=="environ"){
path.clear();//清除路径
path="/实验10/";
execlp("env","",NULL);
}else if(CMD=="jobs"){
path.clear();//清除路径
path="/实验10/";
execlp("pstree","-p",NULL);
}else if(CMD.find("cd")!=string::npos){
if(CMD.size()>3){
execlp("/bin/ls","ls",path.substr(10).c_str(),NULL);
}
}
}else{
/* 父进程将一直等待,直到子进程运行完毕*/
wait(NULL);
}
}else {
cout<<"指令错误,请重新输入,可以输入help查看本shell的所支持的指令!"<<endl;
}
}
4 实验结果与分析
(1)cd命令,当未指定目录时,输出当前工作目录,当指定目录时,如果指定的目录存在则直接进入并打印当前目录下的文件夹和文件信息,如果指定的目录不存在,则提示错误信息,结果如下面的图片所示:
未指定目录,输出当前工作目录。
图1.1 未指定目录
指定目录存在,直接进入并打印文件夹和文件信息。
图1.2 指定目录存在
指定目录不存在,提示错误。
图1.3 指定目录不存在
(2)environ命令,输入environ命令后会列出所有环境变量字符串的设置,部分结果如下图所示:
图1.4 environ命令
(3)echo命令,输入echo <内容>后,会打印内容的信息并换行,结果如下图所示:
图1.5 ehco命令
(4)help命令,输入help命令后,会输出提示信息,结果如下图所示:
图1.6 help命令
(5)quit、exit和bye命令,输出这3个命令之后,会退出当前shell命令行解释器,注意,当进入目录级多的时候,这三个命令可能需要多次,应该是因为父进程fork子进程之后,一直在等待子进程结束,但是子进程结束后,父进程还处在while(1)里面,所以还需要再输入这三个指令以结束父进程,我当时时间紧就没解决了,大家可以自己想想怎么解决,下面的图片只展示quit命令的结果,因为其他两个是一样的:
图1.7 quit、exit和bye命令
5 代码
//#include<bits/stdc++.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<string>
using namespace std;
typedef long long LL;
const int maxn = 10010;
int main(){
string CMD;//指令
string path="/实验10/";//保存路径
while(1){
cout<<"请输入指令:";
getline(cin,CMD);
if(CMD=="exit"||CMD=="quit"||CMD=="bye")break;
else if(CMD=="help"){
path.clear();//清除路径
path="/实验10/";
cout<<"cd <目录>更改当前的工作目录到另一个<目录>。";
cout<<"如果<目录>未指定,输出当前工作目录。如果<目录>不存在,会提示错误信息。"<<endl;
cout<<"environ 列出所有环境变量字符串的设置。"<<endl;
cout<<"echo <内容 > 显示 echo 后的内容且换行。"<<endl;
cout<<"help shell的使用方法和基本功能。"<<endl;
cout<<"jobs 输出 shell 当前的一系列子进程。"<<endl;
cout<<"quit 退出 shell。"<<endl;
cout<<"exit 退出 shell。"<<endl;
cout<<"bye 退出 shell。"<<endl;
}
else if(CMD.find("echo")!=string::npos){
path.clear();//清除路径
path="/实验10/";
cout << CMD.substr(5) << endl;
}
else if(CMD=="environ"||CMD=="jobs"||CMD.find("cd")!=string::npos){
if(CMD.find("cd")!=string::npos){
if(CMD.size()>3){//将路径放在这里处理是因为在下面处理会把路径清除无法保存之前输入的路径
path=path+CMD.substr(3)+"/";
}else{
cout<<"当前工作目录:"<<path<<endl;
}
}
pid_t pid;
/* fork a child process */
pid=fork();
if (pid < 0)
{ /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}else if(pid==0){
/* 子进程 */
if(CMD=="environ"){
path.clear();//清除路径
path="/实验10/";
execlp("env","",NULL);
}else if(CMD=="jobs"){
path.clear();//清除路径
path="/实验10/";
execlp("pstree","-p",NULL);
}else if(CMD.find("cd")!=string::npos){
if(CMD.size()>3){
execlp("/bin/ls","ls",path.substr(10).c_str(),NULL);
}
}
}else{
/* 父进程将一直等待,直到子进程运行完毕*/
wait(NULL);
}
}else {
cout<<"指令错误,请重新输入,可以输入help查看本shell的所支持的指令!"<<endl;
}
}
return 0;
}