Linux 模拟实现shell【简单实现】

shell的模拟实现

我们知道shell是一个永不退出的程序,所以他应该是一个死循环,并且shell为了防止影响到自己,我们在命令行上输入的所有命令都是由shell的子进程来执行的,所以它应该要有创建子进程的相关函数,当然也会有进程替换的相关函数,因为我们直接创建子进程,父子进程是共享代码的,如果没有进程替换,shell根本无法让子进程执行特定的命令。

总的思路:
1.打印提示符 && 获取用户命令字符串
2.分割字符串
//“ls -a -l” —> “ls” “-a” “-l”
3.check buildin command 检查内键命令
4.执行命令

1.打印提示符 && 获取用户命令字符串

在这里插入图片描述

对于getUserCommand函数:

先介绍fgets函数:在这里插入图片描述

#define NUM 1024
#define SIZE 64

int getUserCommand(char *command,int num)
{
    printf("[%s@%s %s]",getUsername(),getHostname(),getCwd());

    //不用scanf,遇到空格会停,不好用
    char *r = fgets(command,num,stdin);//最终还是会输入\n
    if(r == NULL) return -1;
    //abcd\n
    command[strlen(command) - 1] = '\0';//给最后加上\0
    return strlen(command);
}

getUsername(),getHostname(),getCwd() 是为了读取用户名@主机名 当前所在文件夹

在这里插入图片描述

2.分割字符串

比如我要执行ls命令,ls命令后还可以携带选项,ls -a -l,shell需要获取指令与选项,并将它们分割(shell接收到的是一串字符串"ls -a -l",shell需要将字符串分割为更小的字符串"ls" “-a” “-l”)。

定义char usercommand[NUM];接收一整行的指令,该数组中的元素保存的是字符,char *argv[SIZE];//存储命令拆分后结果,该数组中的元素要保存的是分割后的字符串。
在这里插入图片描述

介绍strtok:
在这里插入图片描述

void commandSplit(char *in,char *out[])
{
    int argc = 0;
    out[argc++] = strtok(in," ");//分隔并放入out指针数组
    while(out[argc++] = strtok(NULL," "));
}

3.check buildin command 检查内键命令

在这里插入图片描述

内建命令:

开头说到,shell的命令分为内部命令和其他命令,何为内建命令? 内建命令是一个需要shell自己执行的命令,即shell不创建子进程,自己亲自执行的命令。

shell对于一些命令是必须要亲自执行的:比如cd更改工作路径,将shell的工作路径修改后,由于子进程的工作路径与父进程相同,更改分进程的工作路径后,父进程创建出的子进程的工作路径也是被修改过的,体现给用户的感觉就是当前的工作路径改变了。

对于内建命令,shell是怎么实现的? shell中有许多内建命令,当shell接收到指令并解析后,需要判断用户输入的命令是否为内建命令,如果是就执行拦截操作,使子进程不再被创建,自己执行该指令。如果不是内建命令,则创建子进程执行该命令。

内建命令太多,代码里面只写了cd和export内建命令。

char cwd[1024];//全局变量
char enval[1024];//for test 

char *homepath()
{
    char *home = getenv("HOME");
    if(home) return home;
    else return (char*)".";
}

void cd(const char *path)
{
    chdir(path);//chdir(),用户将当前的工作目录改变成以参数路径所指的目录
    char tmp[1024];
    getcwd(tmp,sizeof(tmp));//getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数size为buf的空间大小。
    sprintf(cwd,"PWD=%s",tmp); //打印到字符串中
    putenv(cwd);//putenv 函数会将cwd 直接填写到环境表中
}

// 什么叫做内键命令: 内建命令就是bash自己执行的,类似于自己内部的一个函数!
// 1->yes, 0->no, -1->err
int doBuildin(char* argv[])
{
    if(strcmp(argv[0],"cd") == 0) //cd ...
    {
        char *path =  NULL;
        if(argv[1] == NULL) path = homepath();//纯cd,回到HOME
        else path = argv[1];
        cd(path);//进入要进入的路径
        return 1;
    }
    else if(strcmp(argv[0],"export") == 0)
    {
        if(argv[1] == NULL) return 1;
        strcpy(enval,argv[1]);//拷贝
        putenv(enval);//直接填写到环境表中
        return 1;
    }
    return 0;
}

对于void cd(const char *path),用 chdir() 函数来将 用户当前的工作目录改变成以参数路径所指的目录,
用 getcwd() 函数将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,
用 sprintf() 函数将 “PWD=工作目录” 字符串打印给cwd,
用putenv 将cwd 直接填写到环境表中。

4.执行命令

在这里插入图片描述

int execute(char *argv[])
{
    pid_t id = fork();
    if(id < 0) return -1;
    else if(id == 0) //child
    {
        //exec command
        execvp(argv[0],argv);//execvp()会从环境变量所指的目录中查找符合参数 file 的文件名, 找到后执行该文件, 然后将第二个参数argv 传给该执行的文件。
        exit(1);
    }
    else //father
    {
        int status = 0;
        pid_t res = waitpid(-1, &status, 0);//阻塞式等待
        if(res > 0)//等待成功
        {
            printf("exit code: %d \n", WEXITSTATUS(status));
        }
    }

    return 0;
}

用子进程进行进程替换,执行命令:

在进行进程替换时,使用ececvp()函数:
在这里插入图片描述

效果动态图

在这里插入图片描述

代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

#define NUM 1024
#define SIZE 64


char cwd[1024];
char enval[1024];//for test 

char *homepath()
{
    char *home = getenv("HOME");
    if(home) return home;
    else return (char*)".";
}

const char *getUsername()
{
    const char *name = getenv("USER");
    if(name) return name;
    else return "none";
}

const char *getHostname()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname) return hostname;
    else return "none";
}

const char *getCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd) return cwd;
    else return "none";
}

int getUserCommand(char *command,int num)
{
    printf("[%s@%s %s]",getUsername(),getHostname(),getCwd());

    //不用scanf,遇到空格会停,不好用
    char *r = fgets(command,num,stdin);//最终还是会输入\n
    if(r == NULL) return -1;
    //abcd\n
    command[strlen(command) - 1] = '\0';
    return strlen(command);
}

void commandSplit(char *in,char *out[])
{
    int argc = 0;
    out[argc++] = strtok(in," ");//分隔并放入out指针数组
    while(out[argc++] = strtok(NULL," "));
}

void cd(const char *path)
{
    chdir(path);//chdir(),用户将当前的工作目录改变成以参数路径所指的目录
    char tmp[1024];
    getcwd(tmp,sizeof(tmp));//getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数size为buf的空间大小。
    sprintf(cwd,"PWD=%s",tmp); //打印到字符串中
    putenv(cwd);//putenv 函数会将c wd 直接填写到环境表中
}

// 什么叫做内键命令: 内建命令就是bash自己执行的,类似于自己内部的一个函数!
// 1->yes, 0->no, -1->err
int doBuildin(char* argv[])
{
    if(strcmp(argv[0],"cd") == 0) //cd ...
    {
        char *path =  NULL;
        if(argv[1] == NULL) path = homepath();
        else path = argv[1];
        cd(path);
        return 1;
    }
    else if(strcmp(argv[0],"export") == 0)
    {
        if(argv[1] == NULL) return 1;
        strcpy(enval,argv[1]);
        putenv(enval);
        return 1;
    }

    return 0;
}

int execute(char *argv[])
{
    pid_t id = fork();
    if(id < 0) return -1;
    else if(id == 0) //child
    {
        //exec command
        execvp(argv[0],argv);//execvp()会从环境变量所指的目录中查找符合参数 file 的文件名, 找到后执行该文件, 然后将第二个参数argv 传给该执行的文件。
        exit(1);
    }
    else //father
    {
        int status = 0;
        pid_t res = waitpid(-1, &status, 0);//阻塞式等待
        if(res > 0)//等待成功
        {
            printf("exit code: %d \n", WEXITSTATUS(status));
        }
    }

    return 0;
}

int main()
{
    while(1)
    {
        char usercommand[NUM];//存储命令
        char *argv[SIZE];//存储命令拆分后结果
        //1.打印提示符 && 获取用户命令字符串
        int n = getUserCommand(usercommand,sizeof(usercommand));
        if(n <= 0) continue;//输入空也可以重新输入
        //2.分割字符串
        //"ls -a -l" ---> "ls" "-a" "-l"
        commandSplit(usercommand,argv);
        //3.check buildin command 检查内键命令
        n = doBuildin(argv);
        if(n) continue;
        //4.执行命令
        execute(argv);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/419051.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL Strict Mode is not set for database connection ‘default‘

在使用 DJango 框架执行迁移文件的命令时&#xff0c;可以看到出现如下警告&#xff1a; (ll_env) D:\workspace\workspace-mengll\learning-log>python manage.py migrate System check identified some issues: WARNINGS: ?: (mysql.W002) MySQL Strict Mode is not set …

springboot232青年公寓服务平台

青年公寓服务平台的设计与实现 摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;房屋信息因为其管理内容繁杂&#xff0c;管理数量繁…

android Service 与 activity 通信 并不断传数据

注&#xff1a;这只是个Demo 以下载为案例&#xff0c;实现开启下载&#xff0c;暂停下载&#xff0c;下载进度不断发送给activity class DownloadService : Service() {override fun onBind(intent: Intent?): IBinder? {return MyBinder()}inner class MyBinder : Binder…

IDEA中的Structure模块使用详解

IDEA中的Structure模块使用详解 类方法的展示 从左往右介绍&#xff1a; 1、最开头的 m 标识是表示为方法&#xff0c;如出现 f 标识则表示为属性&#xff1b; 2、m后面跟着的是方法或者属性的访问修饰符&#xff1a; #红色关闭的锁表示为private&#xff1b; #圆圈表示不带…

postman切换成黑色主题

postman安装以后默认是白色背景&#xff0c;如果想要切换成黑色的&#xff0c;大家可以按照下图箭头指示来操作。 1打开设置 2在Themes页面选择黑色主题

VR危险环境模拟介绍|VR虚拟现实设备

VR危险环境模拟是指利用虚拟现实技术来模拟和展现各种危险环境&#xff0c;以便训练人员应对紧急情况、提高安全意识和应急反应能力。这种模拟可以涉及到工业、医疗、紧急救援等多个领域&#xff0c;旨在帮助人们在真实环境中面对危险时能够做出正确的应对和决策。 VR危险环境…

LeetCode # 206. 反转链表

206. 反转链表 题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例…

深度学习 精选笔记(2)自动求导与概率

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

国际数字影像产业园迎来多家企业,数字产业再添“生力军”!

龙年开年&#xff0c;树莓集团总部国际数字影像产业园迎来12家企业&#xff0c;为成都数字产业再添强军。初春2月&#xff0c;也为园区冲刺首季度“开门红”按下“快进键”。 一、正式落地 期待企业更大规模发展 紫荆国际教育集团成立四川东方紫荆教育咨询有限公司&#xff0c…

FCU2601嵌入式控制单元获得开普「电磁兼容检验证书」

近日&#xff0c;飞凌嵌入式专为锂电池储能行业设计的FCU2601嵌入式控制单元获得了开普电磁兼容检验证书&#xff0c;此次性能检验项目包括高频干扰检验、静电放电干扰检验、辐射电磁场干扰检验、快速瞬变脉冲群干扰检验、浪涌干扰检验、工频磁场干扰检验、阻尼振荡磁场干扰检验…

1688以图搜图API接口|c#爬虫-1688官网自动以图搜图

1688item_search_img 拍立淘 背景 在1688有个功能&#xff0c;就是上传图片&#xff0c;就可以找到类似的商品。如下 网址 &#xff1a;https://www.1688.com/ 这时候&#xff0c;我们可以使用程序来代替&#xff0c;大批量的完成图片上传功能。 实现思路 1、找到图片上传…

VR虚拟现实技术应用到猪抗原体检测的好处

利用VR虚拟仿真技术开展猪瘟检测实验教学确保生猪产业健康发展 为了有效提高猪场猪瘟防控意识和检测技术&#xff0c;避免生猪养殖业遭受猪瘟危害&#xff0c;基于VR虚拟仿真技术开展猪瘟检测实验教学数据能大大推动基层畜牧养殖业持续稳步发展保驾护航。 一、提高实验效率 VR虚…

Git安装的一些步骤解说(小白好奇心严重版本)

Use bundled OpenSSH 安装 Git 时&#xff0c;您面临的选择是使用 Git 自带的 SSH 客户端&#xff08;bundled OpenSSH&#xff09;还是使用系统上已安装的外部 SSH 客户端&#xff08;external OpenSSH&#xff09;。以下是两个选项的一些考虑因素&#xff1a; 使用 Git 自带的…

回溯是怎么回事(算法村第十八关青铜挑战)

组合 77. 组合 - 力扣&#xff08;LeetCode&#xff09; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],…

ssm274办公自动化管理系统

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1课题背…

IDEA开发环境的安装与编写第一个程序

1.下载 IDEA&#xff08;全称IntelliJ IDEA&#xff09;是用于Java程序开发的集成环境&#xff08;也可用于其他语言&#xff09;&#xff0c;它在业界被公认是最好的Java开发工具之一&#xff0c;尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代…

回溯 Leetcode 332 重新安排行程

重新安排行程 Leetcode 332 学习记录自代码随想录 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&a…

算力简单介绍

"算力"这个词在不同的领域有不同的含义。下面是几个常见的解释&#xff1a; 在计算机科学中&#xff0c; "算力"通常指的是计算机系统或设备的处理能力。这包括 CPU、GPU、TPU 等处理器的性能。算力可以用来衡量一个设备能够在一定时间内完成的计算任务数量…

内网搭建mysql8.0并搭建主从复制详细教程!!!

一、安装mysql 1.1 mysql下载链接&#xff1a; https://downloads.mysql.com/archives/community/ 1.2 解压包并创建相应的数据目录 tar -xvf mysql-8.2.0-linux-glibc2.28-x86_64.tar.xz -C /usr/local cd /usr/local/ mv mysql-8.2.0-linux-glibc2.28-x86_64/ mysql mkdir…

Pytorch 复习总结 4

Pytorch 复习总结&#xff0c;仅供笔者使用&#xff0c;参考教材&#xff1a; 《动手学深度学习》Stanford University: Practical Machine Learning 本文主要内容为&#xff1a;Pytorch 深度学习计算。 本文先介绍了深度学习中自定义层和块的方法&#xff0c;然后介绍了一些…