Linux:简单自定义shell

1.实现原理

考虑下⾯这个与shell典型的互动:

[root@localhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[root@localhost epoll]# ps
PID TTY TIME CMD
3451 pts/0 00:00:00 bash
3514 pts/0 00:00:00 ps
⽤下图的时间轴来表⽰事件的发⽣次序。其中时间从左向右。shell由标识为sh的⽅块代表,它随着时间的流逝从左向右移动。shell从⽤⼾读⼊字符串"ls"。shell建⽴⼀个新的进程,然后在那个进程中运⾏ls程序并等待那个进程结束。

 

然后shell读取新的⼀⾏输⼊,建⽴⼀个新的进程,在这个进程中运⾏程序 并等待这个进程结束。
所以要写⼀个shell,需要循环以下过程:
  1. 获取命令⾏
  2. 解析命令⾏
  3. 建⽴⼀个⼦进程(fork)
  4. 替换⼦进程(execvp)
  5. ⽗进程等待⼦进程退出(wait)

2.实现代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]"//shell定义的全局数据//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;//2.环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;char cwd[1024];
char cwdenv[2048];//last exit code
int lastcode = 0;const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;//从配置文件中获取环境变量for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc((strlen(environ[i]) + 1));strcpy(g_env[i], environ[i]);g_envs++;}//测试g_env[g_envs++] = (char*)"HAHA=for_test";g_env[g_envs] = NULL;//导成环境变量for (int i = 0; g_env[i]; i++){putenv(g_env[i]);}environ = g_env;
}bool Cd()
{if (g_argc == 1){std::string home = GetHome();if (home.empty())return true;chdir(home.c_str());}else{std::string where = g_argv[1];// cd - / cd ~if (where == "-"){// Todu}else if (where == "~"){// Todu}else{chdir(where.c_str());}}return true;
}void Echo()
{if (g_argc == 2){std::string opt = g_argv[1];if (opt == "$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (opt[0] == '$'){std::string env_name = opt.substr(1);const char* env_value = getenv(env_name.c_str());if (env_value)std::cout << env_value << std::endl;}else{std::cout << opt << std::endl;}}
}std::string DirName(const char* pwd)
{
#define SLASH "/"std::string dir = pwd;if (dir == SLASH)return SLASH;auto pos = dir.rfind(SLASH);if (pos == std::string::npos)return "BUG?";return dir.substr(pos + 1);
}void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}void PrintCommandPrompt()
{char prompt[COMMAND_SIZE];MakeCommandLine(prompt, sizeof(prompt));printf("%s", prompt);fflush(stdout);
}bool GetCommandLine(char* out, int size)
{//ls -a -l ->"ls -a -l"字符串char* c = fgets(out, size, stdin);if (c == NULL)return false;//fgets失败out[strlen(out) - 1] = 0;//清理\nif (strlen(out) == 0)return false;//如只输入\nreturn true;
}bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;//命令行分析"ls -a -l"->"ls" "-a" "-l"  g_argv[g_argc++] = strtok(commandline, SEP);while ((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));g_argc--;return g_argc > 0 ? true : false;}bool CheckAndExecBuildin()
{std::string cmd = g_argv[0];if (cmd == "cd"){Cd();return true;}else if (cmd == "echo"){Echo();return true;}//else if(cmd == "export")//else if(cmd == "alias")//...return false;
}int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}int main()
{// shell 启动的时候,从系统中获取环境变量// 我们的环境变量信息应该从父shell统一来InitEnv();while (true){//1.输出命令行提示符PrintCommandPrompt();//2.输入用户输入的命令char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline)))continue;//3.命令行分析"ls -a -l"->"ls" "-a" "-l"if (!CommandParse(commandline))continue;//4.检测并处理内建命令if (CheckAndExecBuildin())continue;//5.执行命令Execute();}return 0;
}

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

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

相关文章

PLSQL语法入门--PL/SQL 基础详解

PL/SQL 基础详解 PL/SQL&#xff08;Procedural Language for SQL&#xff09;是 Oracle 数据库中的一种过程式语言&#xff0c;它扩展了 SQL 的功能&#xff0c;允许开发者编写复杂的程序逻辑。 一、匿名块 解释 匿名块是 PL/SQL 的基本执行单位&#xff0c;它是一段独立的…

Oracle--用户管理

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 用户管理在 Oracle 数据库中至关重要。一个服务器通常只运行一个 Oracle 实例&#xff0c;而一个 Oracle 用户代表一个用户群&#xff0c;他们通过该用…

UOS+N 卡 + CUDA 环境下 X86 架构 DeepSeek 基于 vLLM 部署与 Dify 平台搭建指南

一、文档说明 本文档是一份关于 DeepSeek 在X86架构下通vLLM工具部署的操作指南&#xff0c;主要面向需要在UOSN卡CUDA环境中部署DeepSeek的技术人员&#xff0c;旨在指导文档使用者完成从 Python 环境升级、vLLM 库安装、模型部署到 Dify 平台搭建的全流程操作。 二、安装Pyt…

操作系统之shell实现(下)

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C语言的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

Spark,流量统计案例

提前创好一个文件夹分为四个类 FlowBean中的代码内容为&#xff1a;package org.example.flow; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; //hadoop 序列化 //三个属性&#xff1a;手机…

下载油管视频 - yt-dlp

文章目录 1. yt-dlp与you-get介绍1.1 主要功能对比1.2 使用场景1.3 安装 2. 基本命令介绍2.1 默认下载视频2.2 指定画质和格式规则2.3 下载播放列表2.4 备注 3. 参考资料 之前只使用you-get下载b站视频&#xff0c;当时了解you-get也可下载油管视频&#xff0c;但之前无此需求&…

基于javaweb的SSM+Maven教材管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

VS2022+QT环境配置及基本操作

参考文章 2025最新&#xff01;Visual Studio 2022 QT6.7 环境配置全攻略&#xff1a;一键搞定安装与乱码问题&#xff0c;开发效率翻倍&#xff01;&#xff08;全网最详细教程&#xff0c;手把手教你搭建完美开发环境&#xff01;&#xff09;_vs2022 qt-CSDN博客 下载QT …

使用percona-toolkit同步mysql表数据

背景 做了主备mysql的配置以后&#xff0c;可能因为切换过程造成不一致的情况&#xff0c;这个时候可以处理的方式是全量导入再导出&#xff0c;这个有个问题就是操作的数据太多了 我们只需要数据补全同步即可 mysql的同步是基于binlog的&#xff0c;如果没有记录的部分的数据…

MDG 实现后端主数据变更后快照自动刷新的相关设置

文章目录 前言实现过程BGRFC期初配置&#xff08;可选&#xff09;设置 MDG快照 BGRFC维护BP出站功能模块 监控 前言 众所周知&#xff0c;在MDG变更请求创建的同时&#xff0c;所有reuse模型实体对应的快照snapshot数据都会记录下来。随后在CR中&#xff0c;用户可以修改这些…

重装系统 之 Dell戴尔服务器 PowerEdge R750xs + window server2012r2 || 2016

因要求需要给新服务器装个 win server2012或者2016系统 XXX使用U盘制作PE系统U盘安装系统不行&#xff0c;适合普通win8&#xff0c;win10&#xff0c;win11U盘制作PE系统U盘安装win10系统教程U盘制作PE系统U盘安装win10系统教程https://mp.weixin.qq.com/s/t0W8aNJaHPAU8T78nh…

基于Spring Security 6的OAuth2 系列之二十六 - 终章

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

一键配置多用户VNC远程桌面:自动化脚本详解

在当今远程工作盛行的时代,高效且安全地管理多用户远程桌面访问变得至关重要。本文将介绍一个强大的自动化脚本,该脚本能够快速创建用户并配置VNC远程桌面环境,大大简化了系统管理员的工作。 一、背景介绍 在Linux系统中,手动配置VNC服务器通常需要执行多个步骤,包括创建…

IOT项目——双轴追光系统

双轴太阳能追光系统 - ESP32实现 系统概述 这个系统使用&#xff1a; ESP32开发板2个舵机&#xff08;水平方向和垂直方向&#xff09;4个光敏电阻&#xff08;用于检测光照方向&#xff09;适当的电阻&#xff08;用于光敏电阻分压&#xff09; 接线示意图 --------------…

Maven集成模块打包使用

文章目录 1.问题思考&#xff08;如何对集成模块进行打包&#xff09;2.问题解决 &#xff08;如何对集成模块进行打包&#xff09;3.使用者使用该jar包(jar包安装本地仓库和使用) 1.问题思考&#xff08;如何对集成模块进行打包&#xff09; 思考&#xff1a;假设有这么一个场…

OpenVINO教程(二):图片目标检测推理应用

YOLO模型物体检测 下面是一个简单的python程序,他的功能是使用yolo11n模型对coco_bike.jpg照片进行检测,并显示检测结果 代码步骤如下: coco_bike.jpg照片加载yolo模型使用模型进行detect推理显示推理结果 下面是完整的代码 from pathlib import Pathimport urllib.request…

聚类算法(K-means、DBSCAN)

聚类算法 K-means 算法 算法原理 K-means 是一种基于类内距离最小化的划分式聚类算法&#xff0c;其核心思想是通过迭代优化将数据划分为 K 个簇。目标函数为最小化平方误差&#xff08;SSE&#xff09;&#xff1a; S S E ∑ i 1 K ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2…

Oracle在ERP市场击败SAP

2024年&#xff0c;甲骨文&#xff08;Oracle&#xff09;以87亿美元的ERP收入和6.63%的市场份额&#xff0c;首次超越SAP&#xff0c;成为全球最大的ERP应用软件供应商&#xff0c;结束了SAP自上世纪80年代以来在该领域的长期霸主地位。据APPS RUN THE WORLD的市场调研&#x…

嵌入式面试高频笔试题目解析

一、基础概念与 C 语言核心题 1. 指针与内存操作 典型题目: char str[] = "hello"; char *ptr = "world"; str[0] = H; // 合法吗? ptr[0] = W; // 合法吗?为什么?解析: str 是栈上数组,可修改内容,str[0]=H 合法。ptr 指向常量字符串区,修改会…

【Python】Selenium切换网页的标签页的写法(全!!!)

在使用selenium做网站爬取测试的时候&#xff0c;我们经常会遇到一些需要点击的元素&#xff0c;才能点击到我们想要进入的页面&#xff0c; 于是我们就要模拟 不断地 点点点击 鼠标的样子。 这个时候网页上就会有很多的标签页&#xff0c;你的浏览器网页标签栏 be like: 那…