操作系统课设之简单 shell 命令行解释器的设计与实现

前言

课程设计开始了,实验很有意思,写博客总结学到的知识
白嫖容易,创作不易,学到东西才是真
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/archives/185
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/118310091

为了美观,实验源代码在结尾处,整合版见下
链接:https://pan.baidu.com/s/1rXj1QJGuw-BVc5sQWret9w
提取码:Lin2
操作系统课程设计源代码
本次操作系统课程设计合集
操作系统课设之Windows 进程管理
操作系统课设之Linux 进程管理
操作系统课设之Linux 进程间通信
操作系统课设之Windows 的互斥与同步
操作系统课设之内存管理
操作系统课设之虚拟内存页面置换算法的模拟与实现
操作系统课设之基于信号量机制的并发程序设计
操作系统课设之简单 shell 命令行解释器的设计与实现
仅用于学习,如有侵权,请联系我删除

实验题目

简单 shell 命令行解释器的设计与实现

实验目的

本实验主要目的在于进一步学会如何在 Linux 系统下使用进程相关的系统调用,了解 shell 工作的基本原理,自己动手为 Linux 操作系统设计一个简单的命令接口。

总体设计(含背景知识或基本原理与算法、或模块介绍、设计步骤等)

背景知识:

本实验要使用创建子进程的 fork()函数,执行新的命令的 exec()系列函数,通常 shell 是等待子进程结束后再接受用户新的输入,这可以使用 waitpid()函数。以上相关的系统函数调用的说明请参见实验二的背景知识。

需求分析:

题目要求设计的 shell 是类似于 sh,bash,csh 等,必须支持以下内部命令:
cd <目录> 能输出PWD,更改工作目录
environ 列出所有环境变量字符串的设置,类似env 命令。
echo <内容> 显示 echo 后的内容且换行
help 提供对于本shell的帮助
jobs 输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号。
quit,exit,bye 退出 shell。

总体设计:

我将输入的指令分成三类,第一类是错误指令,表示本shell没添加的shell;第二类是内部指令,就是需求中提到的指令,第三类是shell中自定义的指令,我设计的是如果输入的指令中含有”lin”就输出”linxx,希望你开心每一天”.
同时对于内部指令还分为两类,一类是需要子进程调用exec()的命令,有:“cd”,“environ”,“jobs”,其他都是不需要子进程的,直接按照输入处理就行了。

详细设计(含主要的数据结构、程序流程图、关键代码等)

因为涉及到大量的字符串操作,本次在Linux下用c++实现,主要是用到string类型的方法。

第一部分 输入

因为输入需要将空格也输入进去,所以这里特意提一下

string cmd;
cout<<"请输入指令: ";
getline(cin,cmd);

第二部分 识别指令

这部分用的是string自带的方法find(),与设定好的指令一一比较

const string sysCmds[CMD_NUM]={"cd","environ","jobs","help","echo","quit","exit","bye"};
//0表示失败 1表示内部命令 2表示姓名
        int flag = 0;
        //内部命令的序号
        int index=-1;

        for(int i=0;i<CMD_NUM;i++)
        {
            if(cmd.find(sysCmds[i]) != string::npos)
            {
                flag = 1;
                index = i;
                if(index != 0)
                {
                    f_path.clear();
                }
                else
                {
                    if(cmd.size()>2)
                    {
                        string path(cmd,3,cmd.size()-3);
                        f_path = f_path+"/"+path;
                    }
                }

                break;
            }
        }

		if(flag==0 && cmd.find("lin") != string::npos)
        {
            flag = 2;
        }

第三部分 指令逻辑设计

这一部分也是最难的一部分,分两类说
第一类 需要子进程
cd:
cd指令的实现是最麻烦的,因为要实现PWD的改变,我想的是用系统自带的ls命令,然后我的程序本身维护一个PWD路径,每次给ls子进程传入的参数都是基于我程序维护的PWD的绝对路径,然后还需要注意的是用户输入的都会用cd前缀,需要处理一下,提取关键路径,然后再合并到程序维护的PWD中,然后将新的PWD作为参数传入ls子进程中,实现cd命令效果。然后如果输入了其他指令,就把cd维护的PWD串清空。
核心代码如下:

if(cmd.find(sysCmds[i]) != string::npos)
            {
                flag = 1;
                index = i;
                if(index != 0)
                {
                    f_path.clear();
                }
                else
                {
                    if(cmd.size()>2)
                    {
                        string path(cmd,3,cmd.size()-3);
                        f_path = f_path+"/"+path;
                    }
                }

                break;
            }

核心代码2部分


if(index == 0) //cd
            {
                 if(cmd.size()>2)
                 {
                     cout<<"当前路径:"<<f_path<<endl;
                     execlp("/bin/ls","ls",f_path.c_str(),NULL);
                  }
                  else
                  {
                     execlp("/bin/ls","ls",NULL);
                  }

             }

environ:
调用系统的env指令,execlp("env","",NULL);
jobs:
调用系统的pstree -p指令,execlp("pstree","-p",NULL);
echo:

string text(cmd,5,cmd.size()-5);
cout<<text<<endl;

echo单独说一下就是C++关于string用法,这是调用了string的构造函数,参数是cmd的[5, cmd.size())子串。
其余指令都是类似于echo,根据输入简单的处理一下,然后输出。

实验结果与分析

cd命令
在这里插入图片描述
environ命令
在这里插入图片描述
jobs命令
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果符合逻辑设计

小结与心得体会

本实验是一个综合性实验,涉及到了Linux下的C/C++编程,Linux进程创建,不过因为对Linux系统下C/C++编程较为熟悉了,所以本实验完成并未花费太长时间,主要是卡在cd指令如何实现比较好,同时对fork()函数的理解更加深入。同时对于Linux系统中shell命令行的执行过程有了更深入的了解。=w=

源代码

#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <unistd.h>
#include <iostream>
#include <string>

#define CMD_NUM 8
using namespace std;


const string sysCmds[CMD_NUM]={"cd","environ","jobs","help","echo","quit","exit","bye"};
//string.find("cd")!=string::npos

int main()
{
    string f_path;
    while(true)
    {
        string cmd;
        cout<<"请输入指令: ";
        getline(cin,cmd);

        //0表示失败 1表示内部命令 2表示姓名
        int flag = 0;
        //内部命令的序号
        int index=-1;

        for(int i=0;i<CMD_NUM;i++)
        {
            if(cmd.find(sysCmds[i]) != string::npos)
            {
                flag = 1;
                index = i;
                if(index != 0)
                {
                    f_path.clear();
                }
                else
                {
                    if(cmd.size()>2)
                    {
                        string path(cmd,3,cmd.size()-3);
                        f_path = f_path+"/"+path;
                    }
                }

                break;
            }
        }

        if(flag==0 && cmd.find("lin") != string::npos)
        {
            flag = 2;
        }


        switch(flag)
        {
        case 0:
            printf("指令错误,请重新输入,帮助请输入 help\n");
            break;
        case 1:
            if(index < 0)
            {
                printf("指令错误,请重新输入,帮助请输入 help\n");
            }
            else if(index < 3) //需要调用子进程
            {
                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(index == 0) //cd
                    {
                        if(cmd.size()>2)
                        {
                            cout<<"当前路径:"<<f_path<<endl;
                            execlp("/bin/ls","ls",f_path.c_str(),NULL);
                        }
                        else
                        {
                            execlp("/bin/ls","ls",NULL);
                        }

                    }
                    else if(index == 1) //environ
                    {
                        execlp("env","",NULL);
                    }
                    else if(index == 2) //jobs
                    {
                        execlp("pstree","-p",NULL);
                    }

                }
                else   /* 父进程 */
                {
                    /* 父进程将一直等待,直到子进程运行完毕*/
                    waitpid(pid,NULL,0);
                }

            }
            else    //不需要子进程
            {
                if(index == 3) //help
                {
                    cout<<"RongLin's Shell:"<<endl;
                    cout<<"cd [路径] -列出该路径下的文件"<<endl;
                    cout<<"environ -列出系统的环境变量"<<endl;
                    cout<<"jobs -查看当前进程树"<<endl;
                    cout<<"help -帮助文档"<<endl;
                    cout<<"echo [内容] -显示 echo 后的内容且换行"<<endl;
                    cout<<"quit -退出本shell"<<endl;
                    cout<<"exit -退出本shell"<<endl;
                    cout<<"bye -退出本shell"<<endl;
                }
                else if(index == 4) //echo
                {
                    string text(cmd,5,cmd.size()-5);
                    cout<<text<<endl;
                }
                else    //quit exit bye
                {
                    return 0;
                }
            }
            break;
        case 2:
            cout<<cmd<<",希望你开心每一天"<<endl;
            break;
        }
    }
    return 0;

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值