【Linux系统编程】—— 从零开始实现一个简单的自定义Shell

文章目录

  • 什么是自主shell命令行解释器?
  • 实现shell的基础认识
  • 全局变量的配置
  • 初始化环境变量
  • 实现内置命令(如 cd 和 echo)
    • cd命令:
    • echo命令:
  • 构建命令行提示符
  • 获取并解析用户输入的命令
  • 执行内置命令与外部命令
  • Shell的主循环
  • 最后头文件献上

前言:在前文当中,我们学习了进程的概念以及进程等待、终止、替换等等。 本篇博客的主题是关于写一个⾃主Shell命令⾏解释器

什么是自主shell命令行解释器?

自主Shell命令行解释器(通常称为“shell”)是一个允许用户与操作系统进行交互的命令行界面。用户可以通过Shell输入命令,Shell负责将这些命令传递给操作系统内核,然后执行相应的任务。

在云服务器(虚拟机)上,我们可以使用系统所提供的Bash,即Linux下常见的shell
Shell解释器的功能包括:

  1. 命令执行:它会解析用户输入的命令,并将其传递给操作系统内核以执行。
  2. 脚本执行:Shell能够执行一系列命令(称为脚本),通常用于自动化任务。
  3. 输入输出重定向:Shell允许用户将命令的输入输出重定向到文件或其他命令,以实现更灵活的任务处理。
  4. 环境管理:它管理用户的环境变量和配置,允许用户定制Shell的行为。
  5. 交互式与批处理模式:Shell不仅可以作为交互式的命令行工具使用,也可以用来批量执行命令和脚本。

实现shell的基础认识

我们可以通过参考Linux当中的shell做出我们自己的shell
命令行的外表
用户名, 主机名,当前路径都保存在环境变量中。
在这里插入图片描述
所以在我们的要实现的代码当中,我们可以将这三个值通过封装3个函数得到:

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);  // 更新PWD环境变量}return pwd == NULL ? "None" : pwd;
}

通过getenv(), getcwd(),这些内置函数,我们可以知道需要的一些shell的值
当然也可以不用这么复杂的得到

//获取用户名
const char*getusername()
{return getenv("USER");
}
//获取主机名
const char*gethostname()
{return getenv("HOSTNAME");
}
//获取路径名
const char*getpwd()
{return getenv("PWD");
}

这样也是可以简单的得到的,为了完整性,我们选择较复杂的那种,当然,自己想要简单实现可以使用下面这一种.

全局变量的配置

#define MAXARGC 128  // 最大命令行参数数量
char *g_argv[MAXARGC];  // 存储命令行参数
int g_argc = 0;  // 命令行参数数量#define MAX_ENVS 100  // 最大环境变量数量
char *g_env[MAX_ENVS];  // 存储环境变量
int g_envs = 0;  // 环境变量数量std::unordered_map<std::string, std::string> alias_list;  // 存储命令别名char cwd[1024];  // 当前工作目录
char cwdenv[1024];  // 用于存储更新后的PWD环境变量int lastcode = 0;  // 上一个命令的退出状态

初始化环境变量

你需要初始化Shell的环境变量,并从系统中获取它们。通过 environ 可以访问到当前系统的环境变量。

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

实现内置命令(如 cd 和 echo)

在Shell中,通常有一些内置命令,如 cd 和 echo。你需要编写函数来实现这些命令。

cd命令:

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];chdir(where.c_str());  // 切换到指定目录}return true;
}

echo命令:

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

构建命令行提示符

在每次等待用户输入时,你需要显示一个命令行提示符。这个提示符通常包括用户名、主机名和当前工作目录。

void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}

获取并解析用户输入的命令

Shell需要读取用户的命令并对其进行解析,将命令行分解成不同的参数。

bool GetCommandLine(char *out, int size)
{char *c = fgets(out, size, stdin);if (c == NULL) return false;out[strlen(out) - 1] = 0;  // 清除换行符if (strlen(out) == 0) return false;return true;
}bool CommandParse(char *commandline)
{
#define SEP " "g_argc = 0;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 CheckAndExecBuiltin()
{std::string cmd = g_argv[0];if (cmd == "cd"){Cd();  // 执行cd命令return true;}else if (cmd == "echo"){Echo();  // 执行echo命令return true;}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;
}

Shell的主循环

在Shell的主循环中,你需要不断显示命令提示符,获取用户输入,解析命令,并根据命令类型执行相应操作。

int main()
{InitEnv();  // 初始化环境变量while (true){PrintCommandPrompt();  // 打印提示符// 获取用户输入的命令行char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline)))continue;// 解析命令行if (!CommandParse(commandline))continue;// 检查并执行内置命令if (CheckAndExecBuiltin())continue;// 执行外部命令Execute();}return 0;
}

最后头文件献上

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unordered_map>#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

通过上面的每一步就可以在Linux当中做出自己的简单shell。

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

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

相关文章

html,css,js的粒子效果

这段代码实现了一个基于HTML5 Canvas的高级粒子效果&#xff0c;用户可以通过鼠标与粒子进行交互。下面是对代码的详细解析&#xff1a; HTML部分 使用<!DOCTYPE html>声明文档类型。<html>标签内包含了整个网页的内容。<head>部分定义了网页的标题&#x…

.Net Core微服务入门系列(一)——项目搭建

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…

【JavaSE】(8) String 类

一、String 类常用方法 1、构造方法 常用的这4种构造方法&#xff1a;直接法&#xff0c;或者传参字符串字面量、字符数组、字节数组。 在 JDK1.8 中&#xff0c;String 类的字符串实际存储在 char 数组中&#xff1a; String 类也重写了 toString 方法&#xff0c;所以可以直…

Linux-C/C++--深入探究文件 I/O (下)(文件共享、原子操作与竞争冒险、系统调用、截断文件)

经过上一章内容的学习&#xff0c;了解了 Linux 下空洞文件的概念&#xff1b;open 函数的 O_APPEND 和 O_TRUNC 标志&#xff1b;多次打开同一文件&#xff1b;复制文件描述符&#xff1b;等内容 本章将会接着探究文件IO&#xff0c;讨论如下主题内容。  文件共享介绍&…

npm run dev 时直接打开Chrome浏览器

package.json 修改下配置 "scripts": {"dev": "vite --open chrome.exe",......}, "dev": "vite" 修改为 "dev": "vite --open chrome.exe" 这样方便一点&#xff0c;省得每次去点调试窗口的链接

微软预测 AI 2025,AI Agents 重塑工作形式

1月初&#xff0c;微软在官网发布了2025年6大AI预测&#xff0c;分别是&#xff1a;AI模型将变得更加强大和有用、AI Agents将彻底改变工作方式、AI伴侣将支持日常生活、AI资源的利用将更高效、测试与定制是开发AI的关键以及AI将加速科学研究突破。 值得一提的是&#xff0c;微…

《从入门到精通:蓝桥杯编程大赛知识点全攻略》(五)-数的三次方根、机器人跳跃问题、四平方和

本博客将详细探讨如何通过二分查找算法来解决这几个经典问题。通过几个实际的例子&#xff0c;我们将展示如何在这些问题中灵活应用二分查找&#xff0c;优化计算过程&#xff0c;并在面对大数据量时保持高效性。 目录 前言 数的三次方根 算法思路 代码如下 机器人跳跃问题…

微服务知识——4大主流微服务架构方案

文章目录 1、微服务聚合模式2、微服务共享模式3、微服务代理模式4、微服务异步消息模式 微服务是大型架构的必经之路&#xff0c;也是大厂重点考察对象&#xff0c;下面我就重点详解4大主流微服务架构方案。 1、微服务聚合模式 微服务聚合设计模式&#xff0c;解决了如何从多个…

麒麟操作系统服务架构保姆级教程(十三)tomcat环境安装以及LNMT架构

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 之前咱们学习了LNMP架构&#xff0c;但是PHP对于技术来说确实是老掉牙了&#xff0c;PHP的市场占有量越来越少了&#xff0c;我认识一个10年的PHP开发工程师&#xff0c;十年工资从15k到今天的6k&am…

游戏AI,让AI 玩游戏有什么作用?

让 AI 玩游戏这件事远比我们想象的要早得多。追溯到 1948 年&#xff0c;图灵和同事钱伯恩共同设计了国际象棋程序 Turochamp。之所以设计这么个程序&#xff0c;图灵是想说明&#xff0c;机器理论上能模拟人脑能做的任何事情&#xff0c;包括下棋这样复杂的智力活动。 可惜的是…

Golang的文件处理优化策略

Golang的文件处理优化策略 一、Golang的文件处理优化策略概述 是一门效率高、易于编程的编程语言&#xff0c;它的文件处理能力也非常强大。 在实际开发中&#xff0c;需要注意一些优化策略&#xff0c;以提高文件处理的效率和性能。 本文将介绍Golang中的文件处理优化策略&…

数据结构学习记录-队列

队列的基本概念 1、队列是操作受限的线性表 2、队头&#xff1a;允许删除的一端 3、队尾&#xff1a;允许插入的一端 4、空队列&#xff1a;不含任何元素的空表 5、特点&#xff1a;先进先出、FIFO 6、应用场景&#xff1a; 栈&#xff1a;解决括号匹配&#xff1b;逆波…

java知识框架

面试1 基础篇 如何理解OOP面向对象编程&#xff1f; 对现有事物进行抽象&#xff0c;具有继承、封装、多态的特征。 继承&#xff1a;从已有的类也就是父类进行继承信息。 封装&#xff1a;对数据和数据操作的方法绑定起来&#xff0c;通过方法进行访问或者操作数据。 多态…

JDBC实验测试

一、语言和环境 实现语言&#xff1a;Java。 环境要求&#xff1a;IDEA2023.3、JDK 17 、MySQL8.0、Navicat 16 for MySQL。 二、技术要求 该系统采用 SWING 技术配合 JDBC 使用 JAVA 编程语言完成桌面应用开发。 三、功能要求 某电商公司为了方便客服查看用户的订单信…

小程序获取微信运动步数

1、用户点击按钮&#xff0c;在小程序中触发getuserinfo方法&#xff0c;获取用户信息 <scroll-view class"scrollarea" scroll-y type"list"><view class"container"><button bind:tap"getLogin">获取</button&…

macOS 安装JDK17

文章目录 前言介绍新特性下载安装1.下载完成后打开downloads 双击进行安装2.配置环境变量3.测试快速切换JDK 小结 前言 近期找开源软件&#xff0c;发现很多都已经使用JDK17springboot3 了&#xff0c;之前的JDK8已经被替换下场&#xff0c;所以今天就在本机安装了JDK17&#…

Windows电脑桌面记录日程安排的提醒软件

在快节奏的现代生活中&#xff0c;工作效率成为了衡量个人能力的重要标准之一。然而&#xff0c;日常办公中常常会遇到各种琐事和任务&#xff0c;如果没有合理安排日程&#xff0c;很容易陷入混乱&#xff0c;导致效率低下。因此&#xff0c;做好日程安排对于日常工作至关重要…

MFC 使用 32位带Alpha通道的位图

最近需要做一个MFC界面上的图片,众所周知,MFC 好像只支持 bmp 格式的! 先看我的原始24位图片,RGB 三个颜色各占8位 (256色), 所以是24位。 如果放到MFC界面上,是这个很丑的效果 它是一个正方形图片,周围的白色可以看见。 解下来,进入今天的主题: 32位带 Alpha 通…

Ubuntu22部署MySQL5.7详细教程

Ubuntu22部署MySQL5.7详细教程 一、下载MySQL安装包二、安装MySQL三、启动MySQL 检查状态登录MySQL 四、开启远程访问功能 1、允许其他主机通过root访问数据库2、修改配置文件&#xff0c;允许其他IP通过自定义端口访问 五、使用Navicat连接数据库 默认情况下&#xff0c;Ubun…

大模型 | AI驱动的数据分析:利用自然语言实现数据查询到可视化呈现

【本文作者&#xff1a;擎创科技资深产品专家 布博士】 在当今AI驱动的时代&#xff0c;数据分析已成为各行各业不可或缺的能力。然而&#xff0c;传统的数据分析流程通常需要掌握SQL、数据处理和可视化等多项专业技能&#xff0c;这对非技术背景的业务人员来说是一个不小的挑…