操作系统课程设计——简单shell命令行解释器实现

前言

hnust操作系统课程设计,其他题目较为简单,可参考其他优秀博主的文章,这里仅介绍两道题

  • 简单 shell 命令行解释器的设计与实现
  • 银行家算法改进实现

本篇为简单 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子进程,并且调用相关库函数,实现相关功能。

流程图:

详细设计

算法步骤:

命令解释器首先是一个无限循环。

  1. 首先在main函数中,我们需要设置一个无限循环,不断让用户输入命令

  2. 使用getline()函数进行输入,这里需要注意,必须使用getline,因为输入中包含了空格,用cin会出错

    cout << "请输入要执行的命令:(查看帮助请输入help)" << endl;
    getline(cin,CMD);//这里必须使用getline,因为输入需要包含空格!
    
  3. 通过if-else结构进行解析命令字符串

  4. 判断若输入的是quit、bye、exit命令,则直接break跳出循环,表示退出shell

  5. 若命令为help,调用HELP函数输出帮助文档

  6. 若判断为echo,则采用字符串切片输出

    else if(CMD.find("echo")!=string::npos) cout << CMD.substr(5) << endl;
    
  7. 若判断为environ、jobs、cd这三个命令,此时需要创建子进程,分别调用其函数

  8. 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);//父进程阻塞等待指定子进程结束
    
  9. JOBS函数中同上,采用execlp函数调用pstree -p命令,以树形结构显示程序和进程之间的关系

    execlp("pstree","-p",NULL);
    
  10. 最为困难的是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 <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <unistd.h>
#include <iostream>
#include <string>
#include <string.h>
#include <cstdlib>

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;
}


  • 25
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值