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

shell的编写

文章目录

  • 1.框架
  • 2.命令行
  • 3.获取用户命令字符串
  • 4.命令行字符串分割
  • 5.执行命令和内建命令
  • 6.完整代码:


1.框架

我们知道shell是一直存在的,所以首先我们第一步就是要搭建一个框架,使其一直存在。
那么也很简单,一个while循环就可以完成。
在这里插入图片描述

2.命令行

我们的shell最前面都是有一个命令行的在这里插入图片描述
。如下图:

那么命令行我们应该怎么获取呢?我们可以从环境变量中获取,env查看环境变量。
getenv函数就可以帮我们获取环境变量,可以获取用户名、地址等等!
getenv获取成功就会取到那个对象,如果获取失败就会返回空!!
在这里插入图片描述
然后我们需要把上面获取到的三个字符拼接在一起,这时候就需要用到snprintf函数了:
在这里插入图片描述

在这里插入图片描述

到这里,第一步就结束了!

3.获取用户命令字符串

这一步我们需要获取,命令字符串,因为有空格。我们无法使用scanf。这里使用fgets
在这里插入图片描述
在这里插入图片描述

4.命令行字符串分割

在这里插入图片描述
分割思路:
在这里插入图片描述
具体分割我们可以使用strtok函数
在这里插入图片描述
但是这里有个需要注意的地方,strtok的第二参数需要是一个字符串!!
在这里插入图片描述

5.执行命令和内建命令


内建命令:
在这里插入图片描述

6.完整代码:

以上就是编写shell的大致思路了!上面的shell为了和系统本身的shell做区分,所以前的路径都是绝对路径,一长串。在这里插入图片描述
myshell.c:

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

#define SIZE 512
#define ZERO '\0'
#define SEP " " 
#define NUM 32

// 为了方便,我就直接定义了
char cwd[SIZE*2];
char *gArgv[NUM];
int lastcode = 0;

void Die()
{
    exit(1);
}

//获取家目录
const char *GetHome()
{
    const char *home = getenv("HOME");
    if(home == NULL) return "/";
    return home;
}

//获取用户名
const char *GetUserName()
{
    const char *name = getenv("USER");
    if(name == NULL) return "None";
    return name;
}
//获取主机名
const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname == NULL) return "None";
    return hostname;
}
//获取路径
const char *GetCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd == NULL) return "None";
    return cwd;
}

int GetUserCommand(char command[], size_t n)
{
    char *s = fgets(command, n, stdin);
    if(s == NULL) return -1;
    command[strlen(command)-1] = ZERO;
    return strlen(command); 
}

void MakeCommandLineAndPrint()
{
    char line[SIZE];
    const char *username = GetUserName();
    const char *hostname = GetHostName();
    const char *cwd = GetCwd();

    snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
    printf("%s", line);
    fflush(stdout);
}

void SplitCommand(char command[], size_t n)
{
    (void)n;
    // "ls -a -l -n" -> "ls" "-a" "-l" "-n"
    gArgv[0] = strtok(command, SEP);
    int index = 1;
    while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值,在判断.因为分割之后,如果无法分割strtok会返回NULL,刚好让gArgv最后一个元素是NULL, 并且while判断结束
}


void ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0) Die();
    else if(id == 0)
    {
        // child
        execvp(gArgv[0], gArgv);
        exit(errno);
    }
    else
    {
        // fahter
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status);
            if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
        }
    }
}

void Cd()
{
    const char *path = gArgv[1];
    if(path == NULL) path = GetHome();
    // path 一定存在
    chdir(path);

    // 刷新环境变量
    char temp[SIZE*2];
    getcwd(temp, sizeof(temp));
    snprintf(cwd, sizeof(cwd), "PWD=%s", temp);
    putenv(cwd); // OK
}

int CheckBuildin()
{
    int yes = 0;
    const char *enter_cmd = gArgv[0];
    if(strcmp(enter_cmd, "cd") == 0)
    {
        yes = 1;
        Cd();
    }
    else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
    {
        yes = 1;
        printf("%d\n", lastcode);
        lastcode = 0;
    }
    return yes;
}


int main()
{

//首先,自己写的shell需要一直存在,所以设置一个while循环
int quite=0;
while(!quite)
{
 // 1. 我们需要自己输出一个命令行
   MakeCommandLineAndPrint();

// 2. 获取用户命令字符串
   char usercommand[SIZE];
   int n = GetUserCommand(usercommand, sizeof(usercommand));
   if(n <= 0) return 1;

// 3. 命令行字符串分割. 
   SplitCommand(usercommand, sizeof(usercommand));

// 4. 检测命令是否是内建命令
   n = CheckBuildin();
   if(n) continue;
// 5. 执行命令
   ExecuteCommand();

}
  return 0;
}

相关文章:

  • Maven 项目之快速选择环境配置文件
  • NSSCTF Round#20 Basic 真亦假,假亦真 CSDN_To_PDF V1.2 出题笔记 (附wp+源码)
  • ajax的优缺点有哪些?
  • 即刻体验 | 使用 Flutter 3.19 更高效地开发
  • Ubuntu服务器搭建 - 环境篇
  • java八股——linux常见命令
  • 智慧工地安全+绿色施工方案
  • 【子集回溯】Leetcode 78. 子集 90. 子集 II
  • 文心一言 vs GPT-4 -- 全面横向比较
  • SpringBoot(48)-使用 SkyWalking 进行分布式链路追踪
  • Spark-Scala语言实战(11)
  • 【智能算法】蜜獾算法(HBA)原理及实现
  • 笔记: JavaSE day15 笔记
  • 百卓Smart管理平台 importexport.php SQL注入漏洞复现(CVE-2024-27718)
  • OSPF中配置静态路由负载分担实验简述
  • SpringBoot -- Profiles
  • 手机无线投屏到windows11电脑
  • 基于深度学习的机场航拍小目标检测系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)
  • 49. QT中的HTTP通信与JSON数据格式解析
  • c#仿ppt案例
  • 上海环球马术冠军赛开赛,一场体育与假日消费联动的狂欢
  • 5月1日,多位省级党委书记调研旅游市场、假期安全等情况
  • 韩国代总统、国务总理韩德洙宣布辞职
  • 韩国检方结束对尹锡悦私宅的扣押搜查
  • 民营经济促进法出台,自今年5月20日起施行
  • 专访|首夺天元头衔创生涯历史,王星昊打算一步一步慢慢来