当前位置: 首页 > article >正文

linux中myshell的实现

目录

引言

全局信息

核心代码

核心函数


引言

随着计算机技术的飞速发展,操作系统在人们日常工作和生活中扮演着越来越重要的角色。Linux作为一款开源、自由且功能强大的操作系统,已经成为了众多服务器和开发者的首选平台。Shell作为Linux系统的用户界面,为用户提供了执行命令、管理文件、自动化任务等功能。

在本项目中,我们将实现一个简单的Shell程序——myshell。在myshell中,将模拟shell如何进程执行内建指令、普通指令与进程切换。

全局信息

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

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44

int lastcode = 0;
int quit = 0;
extern char **environ;
char commandline[LINE_SIZE];
char *argv[ARGC_SIZE];
char pwd[LINE_SIZE];

// 自定义环境变量表
char myenv[LINE_SIZE];

我们将特定的元素定义为宏,方便代码的理解与阅读。

核心代码

主体分为:指令的交互(打印对话栏,获取指令字符串)

                  指令字符串的拆解

                  判断是否为内建指令

                  不是内建指令,将进行frok执行

                                                在循环中,不断进行交互

int main()
{
    while(!quit){
        // 2. 交互问题,获取命令行, ls -a -l > myfile / ls -a -l >> myfile / cat < file.txt
        interact(commandline, sizeof(commandline));

        // commandline -> "ls -a -l -n\0" -> "ls" "-a" "-l" "-n"
        // 3. 子串分割的问题,解析命令行
        int argc = splitstring(commandline, argv);
        if(argc == 0) continue;

        // 4. 指令的判断 
        //内键命令,本质就是一个shell内部的一个函数
        int n = buildCommand(argv, argc);

        // 5. 普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

核心函数

1.交互


const char *getusername()
{
    return getenv("USER");
}

const char *gethostname()
{
    return getenv("HOSTNAME");
}

void getpwd()
{
    getcwd(pwd, sizeof(pwd));   //#include <unistd.h>    char* getcwd(char* buf, size_t size);  //buf是一个输出型参数,size是buf的大小,返回值是buf指向的字符串的地址。
}

void interact(char *cline, int size)
{
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);
    char *s = fgets(cline, size, stdin);
    assert(s);
    (void)s;
    // "abcd\n\0"
    cline[strlen(cline)-1] = '\0';
}

2.拆解


// ls -a -l | wc -l | head 
int splitstring(char cline[], char *_argv[])
{
    int i = 0;
    argv[i++] = strtok(cline, DELIM);
    while(_argv[i++] = strtok(NULL, DELIM)); // 故意写的=
    return i - 1;
}

3.普通指令

子进程执行,父进程等待


void NormalExcute(char *_argv[])
{
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return;
    }
    else if(id == 0){
        //让子进程执行命令
        //execvpe(_argv[0], _argv, environ);
        execvp(_argv[0], _argv);
        exit(EXIT_CODE);
    }
    else{
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid == id) 
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}

4.内建指令


int buildCommand(char *_argv[], int _argc)
{
    if(_argc == 2 && strcmp(_argv[0], "cd") == 0){
        chdir(argv[1]);
        getpwd();
        sprintf(getenv("PWD"), "%s", pwd);
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "export") == 0){
        strcpy(myenv, _argv[1]);
        putenv(myenv);
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){
        if(strcmp(_argv[1], "$?") == 0)
        {
            printf("%d\n", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$'){
            char *val = getenv(_argv[1]+1);
            if(val) printf("%s\n", val);
        }
        else{
            printf("%s\n", _argv[1]);
        }

        return 1;
    }

    // 特殊处理一下ls
    if(strcmp(_argv[0], "ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
    return 0;
}

列举了export建立环境变量、echo打印、cd改变路径等信息        

建立环境变量只需要

 else if(_argc == 2 && strcmp(_argv[0], "export") == 0){
     strcpy(myenv, _argv[1]);
     putenv(myenv);
     return 1;

在bash中putenv即可。

注意:

putenv只是修改了环境变量表的一个指针,让他指向了这个区域。但是这个区域改变之后,就无法获得对应的环境变量。

因此我们需要将区域固定住,额外开辟了一个myenv的空间(cline是变化的)。


http://www.kler.cn/news/366259.html

相关文章:

  • 【HarmonyOS Next】原生沉浸式界面
  • 新手直播方案
  • 【AscendC算子开发】笔记1 算子开发哲学
  • PostgreSQL两节点用keepalived实现主备的高可用架构
  • vue写个表格,让它滚动起来,没有用datav,有的时候结合会出错,一种简单的方法,直接用animation
  • 实现可扩展人工智能的便捷之路:英特尔 Tiber 开发者云 + MinIO 对象存储
  • 基于Springboot+Vue的食品商城系统 (含源码数据库)
  • 解决电脑更改IP地址后无法连接网络的实用指南
  • Linux中级(DNS域名解析服务器)
  • 将项目从 Webpack 到 Vite 迁移的步骤
  • java-JVM面试问题-2024
  • 代码随想录算法训练营第46期Day43
  • OJ (在线判题) Java 提交避坑总结,持续补充
  • 8.three.js相机详解
  • NVR录像机汇聚管理EasyNVR多品牌NVR管理工具/设备视频报警功能详解
  • linux网络编程5——Posix API和网络协议栈,使用TCP实现P2P通信
  • R语言编程
  • Deepin V23 / 统信UOS 下安装与配置 tftp
  • 小米商城全栈项目
  • 自学Python不知道看什么书?10本Python经典好书(附pdf),看完少走一半弯路
  • Nginx处理跨域请求(CORS)
  • 从 Web2 到 Web3:区块链技术的演进与未来趋势
  • PostgreSQL两节点用keepalived实现主备的高可用架构
  • 最新版本jdbcutils集成log4j做详细sql日志、自动释放连接...等
  • PyTorch model.train()和model.eval()介绍
  • 如何使用的是github提供的Azure OpenAI服务