Linux自主实现shell

以下是在Linux操作系统 centos7版本下实现的shell ,该shell具备bash的基础功能,无上下键输入历史命令功能,删除字符或命令时按住Ctrl + Back

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<errno.h>
#define COMMAND_SIZE 512
#define ARGV_SIZE 32
#define SPACE " "
#define ZERO '\0'
#define NOMODE 0
#define WRITE_TRUNC 1
#define WRITE_APPEND 2
#define READ 3
#define ENDPATH(p) do{  p += strlen(p)-1; while(*p != '/')p--; }while(0)char cwd[COMMAND_SIZE * 2];
char oldcwd[COMMAND_SIZE * 2];
char* argv[ARGV_SIZE];
int errorcode = 0;
const char* filename;
const char* GetUser()  //获取相应的环境变量内容,下同
{const char* ret = getenv("USER");if(ret == NULL)return "None";return ret;
}const char* GetHostName()
{const char* ret = getenv("HOSTNAME");if(ret == NULL)return "None";return ret;
}const char* GetHome()
{const char* ret = getenv("HOME");if(ret == NULL)return "/";return ret;
}const char* GetPwd()
{const char* ret = getenv("PWD");if(ret == NULL)return "None";return ret;
}const char* GetOldPwd()
{const char* ret = getenv("OLDPWD");if(ret == NULL)return "None";return ret;
}
void ShowUserCommand()
{char commandline[COMMAND_SIZE];  //定义一个缓冲区,用来存放命令行提示符const char* lineuser = GetUser();//获取用户名const char* linehostname = GetHostName(); //获取家目录名称const char* linepwd = GetPwd();  //获取当前工作路径ENDPATH(linepwd);  //得到当前的工作路径后,取出最后一个目录,用宏函数实现linepwd++;linepwd = strlen(linepwd) == 0 ? "/" : (strcmp(linepwd, lineuser) == 0 ? "~" : linepwd)snprintf( commandline, COMMAND_SIZE, "[%s@%s:%s]>> ", lineuser, linehostname, line);// 如果长度为0表示当前工作路径为根目录,打印"/",如果当前工作路径是家目录,打印"~"printf("%s", commandline); //将缓冲区的内容打印到显示器fflush(stdout);
}int SplitCommand( char* tmp)
{//将tmp中的指令切分,读入到命令行参数表argv里int ret = NOMODE;argv[0] = strtok(tmp,SPACE);int i = 1;while((argv[i] = strtok(NULL, SPACE))){//如果指令中含有 ">>", ">","<"等与重定向相关的字符,则设置返回值ret,//对应后续相应的打开文件方式if(ret != NOMODE){i++;continue;}if(strcmp(argv[i], ">>") == 0){ret = WRITE_APPEND;filename = strtok(NULL, SPACE); //若有重定向则更新filename,为后面//的打开文件做准备,下同}else if(argv[i][0] == '>'){if(strlen(argv[i]) == 1){ret = WRITE_TRUNC;filename = strtok(NULL, SPACE);}else if(argv[i][1] == '>'){ret = WRITE_APPEND;filename = argv[i] + 2;}else {ret = WRITE_TRUNC;filename = argv[i] + 1;}}else if(argv[i][0] == '<'){ret = READ;if(strlen(argv[i]) == 1){filename = strtok(NULL, SPACE);}else {filename = argv[i] + 1;}}else {i++;}}argv[i] = NULL;return ret;
}int GetUserCommand(char* usercommand, size_t size)
{//将一整行输入读取到我们定义的缓冲区 usercommand中,进行后续的解析if(NULL == fgets(usercommand, size, stdin)){return 0;}usercommand[strlen(usercommand) - 1] = ZERO;return strlen(usercommand);//返回缓冲区长度,是0表示没有读到指令
}void Die()
{exit(1);
}
void ChangeFile(int mod)
{int fd = 0;if(mod == 0) return ;if(mod == 1 ){fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);  //打开重定向的文件,使用相应的模式打开dup2(fd,1);                                               //将文件描述符为1(标准输出文件)的文件重定向为fd,即我们打开的文件,下同}else if(mod == 2){fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd,1);}else {fd = open(filename, O_RDONLY);dup2(fd,0);}
}
void RunUserCommand(int mod)
{pid_t id = fork();        //执行指令,为子进程运行指令的可执行程序,//父进程为shell程序,不能被影响if(id == 0)               //返回的id为0,为子进程{ChangeFile(mod);        //以mod方式打开文件execvp(argv[0], argv);  //进程替换,替换为要执行的指令exit(errno);}else if(id > 0)           //父进程{int status;if(id == waitpid(id, &status, 0) )  //父进程等待子进程,子进程结束后回收,{                                   //并且记录退出信息statuserrorcode = WEXITSTATUS(status);  //获取退出码if(errorcode != 0)                //错误码不为0,则子进程执行异常,打印异常信息printf("No success:%s:%d\n", strerror(errorcode), errorcode);}}else {Die();                    //创建进程失败,退出shell}
}void Cd()
{const char* path = argv[1];  //用path记录要进入的目录int falg = 0;if(path == NULL ||strcmp(path, "~") == 0 ) path = GetHome();  //如果用户没有指定目录,或者目录为"~"就进入家目录if(strcmp(path,"-") == 0 ){falg = 1;path = GetOldPwd();       //如果用户要进入的目录为 - ,则为最近一次进入的目录,//环境变量oldpwd会记录最近一次进入的目录}char tmp[COMMAND_SIZE * 2];getcwd(tmp,sizeof(tmp)); //将当前工作路径保存,更改路径后用于更新oldpwdif( -1 == chdir(path))   //更改工作路径为path{errorcode =  errno;printf("No success:%s:%d\n", strerror(errorcode), errorcode);return;}snprintf(oldcwd, sizeof(oldcwd), "OLDPWD=%s", tmp);  //在缓冲区更新环境变量OLDPWDgetcwd(tmp,sizeof(tmp));                             //获取更改后的工作路径,//用于更新环境变量PWDsnprintf(cwd, sizeof(cwd),  "PWD=%s", tmp);          //在缓冲区更新环境变量PWDputenv(cwd);                                         //更新环境变量PWDputenv(oldcwd);                                      //更新环境变量OLDPWDif(falg) printf("%s\n", tmp);
}int CheckBuildin(int mod)
{//是内建命令就执行,并且mod不为0时,设置相应的重定向内容int ret = 0;if(strcmp(argv[0],"cd") == 0 ){ret = 1;Cd();}else if(strcmp(argv[0],"exit") == 0 )  //用户输入exit就退出shell程序{ret = 1;exit(0);}else if(strcmp(argv[0], "echo") == 0 && strcmp(argv[1], "$?") == 0 ) //echo $?打印退出码,若有重定向则更换file的指向{ret = 1;FILE* file = stdout;if(mod == 1)file = fopen(filename, "w");else if(mod == 2){file = fopen(filename, "a");}fprintf(file, "%d\n", errorcode);errorcode = 0;                      //更新退出码if(mod != 0)fclose(file);}return ret;
}int main()
{int quit = 1;while(quit){//打印命令行提示符ShowUserCommand();//读取命令行参数char usercommand[ARGV_SIZE * 2];int n = GetUserCommand(usercommand,sizeof(usercommand));if(n < 0) return 1;if(n == 0) continue;//切分命令行参数int mod = SplitCommand(usercommand);//判断是否是内建命令n = CheckBuildin(mod);//执行命令if(n == 0)RunUserCommand(mod);}return 0;
}

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

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

相关文章

vue+elementUI上传图片至七牛云组件封装及循环使用

1.效果&#xff08;解决循环组件赋值问题&#xff09; 废话不多说直接上代码 2.下载七牛云依赖 npm install qiniu-js # 或者使用 yarn yarn add qiniu-js3.在vue组件中引入 import * as qiniu from qiniu-js4.在components文件夹下创建UploadImg1/uploadImg.vue组件 <templ…

2025年6月电子学会青少年软件编程(C语言)等级考试试卷(一级)

答案和更多内容请查看网站&#xff1a;【试卷中心 -----> 电子学会 ----> C/C ----> 一级】 网站链接 青少年软件编程历年真题模拟题实时更新 一、编程题 第 1 题 希望如光 题目描述 在充满挑战的生活中&#xff0c;希望往往是支撑人们穿越黑暗的核心力量。这…

拒绝复杂,AI图表制作简单化

在信息爆炸的时代&#xff0c;数据可视化已成为传递信息的核心手段。无论是职场汇报中的业绩分析&#xff0c;还是学术研究里的实验数据呈现&#xff0c;一张清晰直观的图表往往能胜过千言万语。而 AI 技术的介入&#xff0c;彻底改变了图表制作的传统模式 —— 它不仅让零基础…

easypoi生成多个sheet的动态表头的实现

在使用 EasyPOI 导出 Excel 时&#xff0c;生成多个 Sheet 且每个 Sheet 的表头是动态的&#xff08;即每个 Sheet 的列数和列名可能不同&#xff09;&#xff0c;可以通过如下方式实现&#xff1a;✅ 实现原理简述 使用 Workbook workbook ExcelExportUtil.exportExcel(expor…

移除链表元素+反转链表+链表的中间节点+合并两个有序链表+环形链表约瑟夫问题+分割链表

一、移除链表元素 给你一个链表的头节点 phead 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 (列表中的节点数目在范围 [0, 104] 内) 示例&#xff1a;输入&#xff1a;head [1,2,6,3,4,5,6], val 6 …

vue3+arcgisAPI4示例:轨迹点模拟移动(附源码下载)

demo源码运行环境以及配置运行环境&#xff1a;依赖Node安装环境&#xff0c;需要安装Node。 运行工具&#xff1a;vscode或者其他工具。 配置方式&#xff1a;下载demo源码&#xff0c;vscode打开&#xff0c;然后顺序执行以下命令&#xff1a; &#xff08;1&#xff09;下载…

Design Compiler:Milkyway库的创建与使用

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 DC Ultra推出了拓扑模式&#xff0c;在综合时会对标准单元进行粗布局(Coarse Placement)并使用虚拟布线(Virtual Routing)技术计算互联延迟&#xff0c;关于拓…

嵌入式教学的云端革命:高精度仿真如何重塑倒车雷达实验与工程教育——深圳航天科技创新研究院赋能新一代虚实融合实训平台

一、嵌入式教学的困境与破局之道 在传统嵌入式系统教学中&#xff0c;硬件依赖始终是核心痛点。以“倒车雷达实验”为例&#xff0c;学生需操作STM32开发板、超声波传感器、蜂鸣器等硬件&#xff0c;面临设备损耗、接线错误、调试效率低等问题。更关键的是&#xff0c;物理硬件…

flutter-boilerplate-project 学习笔记

项目地址&#xff1a; https://github.com/zubairehman/flutter_boilerplate_project/tree/master 样板包含创建新库或项目所需的最小实现。存储库代码预加载了一些基本组件&#xff0c;例如基本应用程序架构、应用程序主题、常量和创建新项目所需的依赖项。通过使用样板代码…

集成电路学习:什么是CMSIS微控制器软件接口标准

CMSIS,即Cortex Microcontroller Software Interface Standard(Cortex微控制器软件接口标准),是由ARM公司与多家不同的芯片和软件供应商紧密合作定义的一个标准。该标准旨在为基于ARM Cortex处理器的微控制器提供一套与供应商无关的硬件抽象层,从而简化软件的开发、重用,…

由浅入深使用LangGraph创建一个Agent工作流

创建一个简单的工作流&#xff1a;Start ——> 节点1(固定输入输出) ——> Endfrom langchain_core.messages import SystemMessage, HumanMessage, AIMessage from langgraph.graph import StateGraph, START, END from typing_extensions import TypedDict from typing…

PL-0功能拓展及基于VSCode的IDE配置

title: PL/0功能拓展及基于VSCode的IDE配置 date: 2024-08-06 22:46:38 tags: 做过的实验||项目复盘 top: true 概述PL/0语言可以看成PASCAL语言的子集,它的编译程序是由C语言编写的编译解释执行系统。PL/0能充分展示高级语言的最基本成分。拓展了pl0语言的基础功能&#xff08…

【低空经济】大型露天矿区安全生产无人机巡查与管理系统设计

1. 引言 大型露天矿区因其广阔的作业区域和复杂的环境条件&#xff0c;安全生产管理面临着严峻的挑战。随着科技的进步&#xff0c;无人机作为一种现代化的巡查工具&#xff0c;逐渐被应用于矿区的安全生产管理中。无人机具备高效、灵活、成本相对低廉等优点&#xff0c;可以在…

SpringCloud学习第一季-3

目录 11.服务网关-Gateway新一代网关 一、Gateway概述 1、Gateway是什么 1.1 概述 2、 能干嘛 3、微服务架构中网关在哪里 4、为什么选择gateway? 4.1 SpringCloud Gateway具有如下特性 4.2 SpringCloud Gateway 与 Zuul的区别 5、Zuul1.x模型 6、gateway模型 二、…

超越边界:MongoDB 16MB 文档限制的 pragmatic 解决方案

在软件开发中&#xff0c;我们选择的技术栈往往带有一些固有的设计边界。对于 MongoDB 而言&#xff0c;其最著名的边界之一便是 BSON 文档最大 16MB 的大小限制。在大多数场景下&#xff0c;这个限制是绰绰有余的&#xff0c;它鼓励开发者设计更为精简和规范的数据模型。然而&…

深入探讨:PostgreSQL正则表达式中的邮政编码匹配

引言 在处理大量数据时,如何高效地从字符串中提取特定模式的文本,如邮政编码,是一个常见且具有挑战性的任务。本文将通过一个具体实例,探讨在PostgreSQL中使用正则表达式匹配加拿大邮政编码的问题,并提供解决方案。 问题描述 我们希望能够从字符串中提取所有符合加拿大…

集合框架(重点)

第十五天集合框架1.什么是集合 Collections集合Collection&#xff0c;也是一个数据容器&#xff0c;类似于数组&#xff0c;但是和数组是不一样的。集合是一个可变的容器&#xff0c;可以随时向集合中添加元素&#xff0c;也可以随时从集合中删除元素。另外&#xff0c;集合还…

深度学习核心:神经网络-激活函数 - 原理、实现及在医学影像领域的应用

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#,Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发…

OneCode3.0 核心表达式技术深度剖析:从架构设计到动态扩展

一、引言&#xff1a;表达式技术在企业级框架中的核心价值 在当今快速变化的企业级应用开发中&#xff0c;动态性和灵活性已成为衡量框架优劣的关键指标。OneCode 3.0 框架作为企业级应用开发的重要工具&#xff0c;其核心表达式技术提供了一种强大的解决方案&#xff0c;使开发…

[css]旋转流光效果

实现一个矩形的旋转流光边框效果。 需要使用css属性梯度渐变&#xff1a;链接: conic-gradient&#xff0c;他指的是圆锥形变化的梯度。 // html<div class"demo"></div>// css body {width: 100%;height: 100%;background-color: black; }.demo {width…