shell的编写

news2024/11/17 7:51:11

文章目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(科研实践篇)大模型相关知识

1.embedding 1.介绍&#xff1a; embedding就是用一个低纬的向量表示一个物品。而这个embedding向量的实质就是使距离相似的向量所对应的物品具有相似的含义&#xff08;参考皮尔逊算法和cos余弦式子&#xff1a;计算相似度&#xff09;简单来说&#xff0c;就是用空间去表示…

1.Docker简介和安装

1 Docker 简介 1.1 Docker 是什么&#xff1f; docker是一个开源的应用容器引擎。 1.2 容器是什么&#xff1f; 容器是一种轻量级的虚拟化技术 &#xff0c;它是一个由应用运行环境、容器基础镜像组成的集合。 以 Web 服务 Nginx 为例&#xff0c;如下图所示&#xff1a;Ngin…

【并发编程】CountDownLatch

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳中求进&#xff0c;晒太阳 CountDownLatch 概念 CountDownLatch可以使一个获多个线程等待其他线程各自执行完毕后再执行。 CountDownLatch 定义了一个计数器&#xff0c;…

贝锐蒲公英企业路由器双机热备,保障异地组网可靠、不中断

对于关键业务&#xff0c;比如&#xff1a;在线支付系统、远程医疗监控系统、重要数据中心等&#xff0c;一旦网络发生故障&#xff0c;可能导致巨大的损失或影响&#xff0c;因此需确保网络拥有极高的可靠性、稳定性和容错能力。 面对此类场景和需求&#xff0c;贝锐蒲公英异…

优秀网站收藏——持续更新

1、Uiverse.io 官网&#xff1a;Open-Source UI elements for any project Uiverse.io是一个开源免费的UI组件库&#xff0c;直接使用HTML和CSS组成&#xff0c;可以方便的使用在任何前端框架上。它包含了丰富的UI组件类型&#xff0c;如按钮、复选框、开关、卡片、加载动画、…

在s390x架构机器上构建frps/frpc镜像 —— 筑梦之路

源码&#xff1a;GitHub - fatedier/frp: A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. # 克隆代码git clone https://github.com/fatedier/frp.git# 切换目录cd frp# 构建frps服务端docker build -t frps:s390x -f …

ALPHA开发板上的PHY芯片驱动:LAN8720驱动

一. 简介 前面文章了解到&#xff0c;Linux内核是有提供 PHY通用驱动的。 本文来简单了解一下ALPHA开发板上的 PHY网络芯片LAN8720的驱动。是 LAN8720芯片的公司提供的 PHY驱动。 二. ALPHA开发板上的PHY芯片驱动&#xff1a;LAN8720驱动 我 们 来 看 一 下 LAN8720A 的 …

【算法每日一练]-数论(保姆级教程 篇1 埃氏筛,欧拉筛)

目录 保证给你讲透讲懂 第一种&#xff1a;埃氏筛法 第二种&#xff1a;欧拉筛法 题目&#xff1a;质数率 题目&#xff1a;不喜欢的数 思路&#xff1a; 问题&#xff1a;1~n 中筛选出所有素数&#xff08;质数&#xff09; 有两种经典的时间复杂度较低的筛法&#xff0…

LeetCode-98. 验证二叉搜索树【树 深度优先搜索 二叉搜索树 二叉树】

LeetCode-98. 验证二叉搜索树【树 深度优先搜索 二叉搜索树 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;中序遍历解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树…

Exchanger 怎么用J.U.C

Exchanger简介 Exchanger通常用来解决以下类似场景的问题&#xff0c;如下&#xff1a;两个线程间需要交换数据的问题&#xff0c;在多线程编程中&#xff0c;经常会有这样的场景&#xff1a;两个线程各自持有一些数据&#xff0c;并且需要在某个点上交换这些数据&#xff0c;…

不借助三方工具,修改Windows的CapsLock键为其他功能键

0. 背景交代 在我的Deepin上实现了CapsLock键切换输入法后&#xff0c;再用Windows会有点别扭&#xff0c;于是在一番查找资料和自行摸索后&#xff0c;找到了不借助第三方工具来实现修改CapsLock键的方法。 1. 修改CapsLock键为F15 1.1 Win R呼出运行窗口 1.2 输入Regedi…

Spring Boot 学习(2)——HelloWorld

HelloWorld&#xff01;全宇宙码农的第一个&#xff08;行&#xff09;程序&#xff08;代码&#xff09;。 1、创建项目 打开idea&#xff0c;新建一个maven项目。 1&#xff09;选择项目sdk&#xff08;本例是1.8&#xff09; 2&#xff09;输入GroupId&#xff08;co…

TCP的十个重要的机制

注&#xff1a;TCP不是只有十个机制 TCP 可靠传输是tcp最为重要的核心&#xff08;初心&#xff09; 可靠传输&#xff0c;并不是发送方把数据能够100%的传输给接收方 而是退而求其次 让发送方发送出去数据之后&#xff0c;能够知道接收方是否收到数据。 一但发现对方没有…

智慧公厕:提升城市公卫管理效率与环境舒适度的利器

公厕作为城市基础设施的重要组成部分&#xff0c;一直以来备受市民们的关注与诟病。然而&#xff0c;随着科技的发展和城市智慧化进程的推进&#xff0c;智慧公厕作为一种集成了物联网等技术的创新型公厕逐渐走入人们的视野。智慧公厕不仅实现了信息化、数字化和智慧化&#xf…

基于STC12C5A60S2系列1T 8051单片机的带字库液晶显示器LCD12864数据传输并行模式显示自定义字符应用

基于STC12C5A60S2系列1T 8051单片机的带字库液晶显示器LCD12864显示自定义字符应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍液晶显示器LCD12864简单介绍一、LCD…

WordPress 6.5 “里贾纳”已经发布

WordPress 6.5 “里贾纳”已经发布&#xff0c;其灵感来自著名爵士小提琴家Regina Carter的多才多艺。雷吉娜是一位屡获殊荣的艺术家和著名的爵士乐教育家&#xff0c;以超越流派而闻名&#xff0c;她在古典音乐方面的技术基础和对爵士乐的深刻理解为她赢得了大胆超越小提琴所能…

备战蓝桥杯--数论与搜索刷题2

话不多说&#xff0c;直接看题&#xff1a; 1.辗转相减法 我们不妨假设原等比数列a,a*(q/p),a*(q/p)^2.... 那么x1,,,,xn就是其中的n项&#xff0c;xi/x1(q/p)^b&#xff0c;假设最大比例为(q/p)^k&#xff0c;&#xff0c;那么一定有(q/p)^(k*s)(q/p)^b&#xff0c;即k是b的…

通用分布式锁组件

通用分布式锁组件 1 Redisson1.1介绍1.2 快速了解1.3 项目集成 2 定义通用分布式锁组件2.1 实现思路分析2.2 定义注解2.3 定义切面2.4 使用锁2.5.工厂模式切换锁类型2.5.1 锁类型枚举2.5.2 锁对象工厂2.5.3 改造切面代码 2.6 锁失败策略2.6.1 策略分析2.6.2 策略实现 2.7 基于S…

程序员们应注意的行业特有的法律问题

大家好&#xff0c;我是不会魔法的兔子&#xff0c;是一枚执业律师&#xff0c;持续分享技术类行业项目风险及预防的问题。 一直以来兔子都在以大家做项目时候会遇到的风险问题做分享&#xff0c;最近有个念头一直挥之不去&#xff0c;就是要不要给我们广大的程序员们也分享一…

从头开发一个RISC-V的操作系统(二)RISC-V 指令集架构介绍

文章目录 前提ISA的基本介绍ISA是什么CISC vs RISCISA的宽度 RISC-V指令集RISC-V ISA的命名规范模块化的ISA通用寄存器Hart特权级别内存管理与保护异常和中断 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提…