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

【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令

文章目录

  • 0.准备工作
    • 1.大体框架
  • 一、获取命令行
  • 二、解析命令行
  • 三、进程执行
    • 1.普通命令
    • 2.内建命令
  • 四、完整代码:


0.准备工作

在这里插入图片描述

1.大体框架

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
//用于修饰命令行
//类似:[hh@VM-4-10-centos ~]$ #define LINE_SIZE 1024//输入命令最大长度
#define ARGC_SIZE 32//命令行参数表的大小
#define EXIT_CODE 44//退出码int lastcode = 0;//上一次的退出码
int quit = 0char commandline[LINE_SIZE];//输入的命令
char *argv[ARGC_SIZE];//解析后保存的命令
char pwd[LINE_SIZE];//保存当前所在路径// 自定义环境变量表
char myenv[LINE_SIZE];//因为环境变量表里面保存的不是//变量本身,而是其地址,所以我们为了防止//自己导入的环境变量被覆盖,需要自己维护一段空间//这里myenv只能维护一个环境变量,因为只有一个地址const char *getusername()
{//获取用户名return getenv("USER");
}const char *gethostname()
{//获取主机名return getenv("HOSTNAME");
}void getpwd()
{
//将当前路径保存到pwd中getcwd(pwd, sizeof(pwd));
}void interact(char *cline, int size){ }//获取命令行int splitstring(char cline[], char *_argv[]){}//解析命令行void NormalExcute(char *_argv[]){}//执行普通命令int buildCommand(char *_argv[], int _argc){}//执行内键命令int main()
{while(!quit){// 1.// 2. 交互问题,获取命令行 interact(commandline, sizeof(commandline));// 3. 子串分割的问题,解析命令行int argc = splitstring(commandline, argv);if(argc == 0) continue;// 4. 指令的判断 //内键命令,本质就是一个shell内部的一个函数int n = buildCommand(argv, argc);// 5. 普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

一、获取命令行

在获取命令之前我们需要先建立一个命令行
类似这种效果:在这里插入图片描述

void interact(char *cline, int size)
{getpwd();//将当前路径写入到pwd中printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);fgets(cline, size, stdin);//这里不用scanf的原因是其遇到空格与回车不会读取//所以我们选择用fgets,将命令写入cline中// "abcd\n\0"//又因为fgets会读入回车键,所以我们要手动把回车位置改为'\0'cline[strlen(cline)-1] = '\0';
}

二、解析命令行

int splitstring(char cline[], char *_argv[])
{int i = 0;//strtok用于分割字符串,上面我们宏定义了只有要DELIM中的字符//就会发生分割,将分割后的字符串写入argv中argv[i++] = strtok(cline, DELIM);while(_argv[i++] = strtok(NULL, DELIM)); // 故意写的=return i - 1;//返回argv中存的字符串个数
}

三、进程执行

1.普通命令

void NormalExcute(char *_argv[])
{pid_t id = fork();//创建子进程if(id < 0){perror("fork");return;}else if(id == 0){//让子进程执行命令//execvp相当于一个加载器//该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行//会从环境变量中的路径中找到我们的可执行程序execvp(_argv[0], _argv);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id) {//等待子进程成功,更改退出码lastcode = WEXITSTATUS(status);}}
}

2.内建命令

在这里插入图片描述

int buildCommand(char *_argv[], int _argc)
{if(_argc == 2 && strcmp(_argv[0], "cd") == 0){//chdir改变当前进程路径//假如我们让子进程执行cd命令,子进程确实路径改变了//但子进程执行完就被父进程回收了,没屁用//因为进程的独立性我父进程路径不受影响。//所以我们要手动修改路径chdir(argv[1]);getpwd();//将新的路径写入pwd//改变环境变量PWD,用pwd对其进行写入sprintf(getenv("PWD"), "%s", pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0], "export") == 0){//自己维护环境变量空间strcpy(myenv, _argv[1]);//将环境变量放入自己的myenvputenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){if(strcmp(_argv[1], "$?") == 0){printf("%d\n", lastcode);lastcode=0;}else if(*_argv[1] == '$'){//_argv[1]+1为$后面的值例如$PATH//那么最后就获取PATH的值char *val = getenv(_argv[1]+1);if(val) printf("%s\n", val);}else{printf("%s\n", _argv[1]);}return 1;}// 特殊处理一下ls//因为ls中如果是可执行文件,其会显示为特殊颜色if(strcmp(_argv[0], "ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}

四、完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44int lastcode = 0;
int quit = 0;
extern char **environ;
char commandline[LINE_SIZE];
char *argv[ARGC_SIZE];
char pwd[LINE_SIZE];// 自定义环境变量表
char myenv[LINE_SIZE];const char *getusername()
{return getenv("USER");
}const char *gethostname()
{return getenv("HOSTNAME");
}void getpwd()
{getcwd(pwd, sizeof(pwd));
}void interact(char *cline, int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);fgets(cline, size, stdin);// "abcd\n\0"cline[strlen(cline)-1] = '\0';
}int splitstring(char cline[], char *_argv[])
{int i = 0;argv[i++] = strtok(cline, DELIM);while(_argv[i++] = strtok(NULL, DELIM));  return i - 1;
}void NormalExcute(char *_argv[])
{pid_t id = fork();if(id < 0){perror("fork");return;}else if(id == 0){//让子进程执行命令//execvpe(_argv[0], _argv, environ);execvp(_argv[0], _argv);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id) {lastcode = WEXITSTATUS(status);}}
}int buildCommand(char *_argv[], int _argc)
{if(_argc == 2 && strcmp(_argv[0], "cd") == 0){chdir(argv[1]);getpwd();sprintf(getenv("PWD"), "%s", pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]);putenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){if(strcmp(_argv[1], "$?") == 0){printf("%d\n", lastcode);lastcode=0;}else if(*_argv[1] == '$'){char *val = getenv(_argv[1]+1);if(val) printf("%s\n", val);}else{printf("%s\n", _argv[1]);}return 1;}// 特殊处理一下lsif(strcmp(_argv[0], "ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}int main()
{while(!quit){// 1.// 2. 交互问题,获取命令行  interact(commandline, sizeof(commandline));// 3. 子串分割的问题,解析命令行int argc = splitstring(commandline, argv);if(argc == 0) continue;// 4. 指令的判断 //内键命令,本质就是一个shell内部的一个函数int n = buildCommand(argv, argc);// 5. 普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

相关文章:

【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令

文章目录 0.准备工作1.大体框架 一、获取命令行二、解析命令行三、进程执行1.普通命令2.内建命令 四、完整代码&#xff1a; 0.准备工作 1.大体框架 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <u…...

Kafka -- 架构、分区、副本

1、Kafka的架构&#xff1a; 1、producer&#xff1a;消息的生产者 2、consumer&#xff1a;消息的消费者 3、broker&#xff1a;kafka集群的服务者&#xff0c;一个broker就是一个节点&#xff0c;主要是负责处理消息的读、写的请求和存储消息。在kafka cluster中包含很多的br…...

CSS特效001:鼠标放div上,实现旋转、放大、移动等效果

GPT能够很好的应用到我们的代码开发中&#xff0c;能够提高开发速度。你可以利用其代码&#xff0c;做出一定的更改&#xff0c;然后实现效能。 css实战中&#xff0c;经常会看到这样的场景&#xff0c;鼠标放到一个图片或者一个div块状时候&#xff0c;会出现旋转、放大、移动…...

gin 快速入门手册

文章目录 安装URL和路由分组2. 带参数的url3. 获取路由分组的参数 获取参数1. 获取get参数2. 获取post参数3. get、post混合 JSON 、 ProtoBuf渲染1. 输出json和protobuf2. PureJSON 表单验证1. 表单的基本验证 中间件和next函数1. 无中间件启动2. 使用中间件3. 自定义组件 设置…...

Window下安装 Mongodb,并实现单点事务

在window操作系统下安装Mongodb&#xff0c;并让单点mongodb支持事务&#xff0c;mongodb5以上时才支持事务&#xff0c;所以必须时mongodb5及以上版本才支持。 1、下载mongodb安装文件 &#xff08;1&#xff09; 下载mongodb msi 安装文件 地址&#xff1a;mongocommunity &…...

【通信原理】第三章 随机过程——例题

一、随机过程 1. 数学特征 ① 随机信号&#xff08;三角函数表达式&#xff09; ② 随机信号&#xff08;求和表达式&#xff09; 2. 功率谱密度 ① 相位确定&#xff0c;求功率谱密度 ② 已知相位分布&#xff0c;求功率谱密度 ③ 信号为两信号之和&#xff0c;求功率谱密度…...

线性【SVM】数学原理和算法实现

一. 数学原理 SVM是一类有监督的分类算法&#xff0c;它的大致思想是&#xff1a;假设样本空间上有两类点&#xff0c;如下图所示&#xff0c;我们希望找到一个划分超平面&#xff0c;将这两类样本分开&#xff0c;我们希望这个间隔能够最大化来使得模型泛化能力最强。 如上图所…...

R语言中的函数26:polyroot多项式求根函数

目录 介绍函数介绍参数含义 示例 介绍 R语言中的base::polyroot()可以用于对多项式求根&#xff0c;求根的多项式可以是复数域上的。 函数介绍 polyroot(z)该函数利用Jenkins-Traub算法对多项式 p ( x ) p(x) p(x)进行求根&#xff0c;其中 p ( x ) z 1 z 2 x ⋯ z n x…...

2023年辽宁省数学建模竞赛A题铁路车站的安全标线

2023年辽宁省数学建模竞赛 A题 铁路车站的安全标线 原题再现&#xff1a; 在火车站或地铁站台上&#xff0c;离站台边缘 1 米左右的地方都画有一条黄线(或白线)&#xff0c;这是为什么呢?   这条线称为安全线(业内称之为安全标线)&#xff0c;人们在候车时必须站在安全线以…...

半导体工厂将应用哪些制造创新技术?

半导体工厂是高科技产业的结晶&#xff0c;汇聚了世界上最新的技术。 在半导体的原料硅晶片上绘制设计图纸&#xff0c;不产生误差&#xff0c;准确切割并包装&#xff0c;然后用芯片生产出我们使用的电脑、智能手机、手表等各种电子产品。绝大多数半导体厂都采用一贯的工艺&a…...

[unity]深色模式/浅色模式

这里用的是Windows版的unity&#xff0c;具体版本号如下&#xff1a; 选项的路径如下&#xff1a;Edit—Preferences—General—Editor Theme 然后就可以选是dark还是light了&#xff1a;...

在react中组件间过渡动画如何实现?

一、是什么 在日常开发中&#xff0c;页面切换时的转场动画是比较基础的一个场景 当一个组件在显示与消失过程中存在过渡动画&#xff0c;可以很好的增加用户的体验 在react中实现过渡动画效果会有很多种选择&#xff0c;如react-transition-group&#xff0c;react-motion&…...

解析找不到msvcr100.dll文件的解决方法,4个方法修复msvcr100.dll

msvcr100.dll是Microsoft Visual C 2010运行库的组成部分&#xff0c;一些基于Visual C开发的软件运行时会依赖这个dll文件。出现“找不到msvcr100.dll”的错误提示&#xff0c;往往意味着这个文件在你的计算机系统中丢失或损坏&#xff0c;导致相关程序无法正常运行。以下是找…...

达梦主备部署

达梦主备部署 一.概括1&#xff09;环境软件下载2&#xff09;集群规划 二.安装1&#xff09;安装前2&#xff09;安装数据库 三.主备机器部署1)初始化数据库&#xff08;1&#xff09;主库配置&#xff08;2&#xff09;备库配置 2)脱机备份&#xff08;1&#xff09;主服务器…...

后期混音效果全套插件Waves 14 Complete mac中文版新增功能

Waves 14 Complete for Mac是一款后期混音效果全套插件&#xff0c;Waves音频插件,内置混响&#xff0c;压缩&#xff0c;降噪和EQ等要素到建模的模拟硬件&#xff0c;环绕声和后期制作工具&#xff0c;包含全套音频效果器&#xff0c;是可以让你使用所有功能。Waves 14 Comple…...

HTML5笔记

前端学习笔记专栏区别于官网中全面的知识讲解&#xff0c;主要记录学习技术栈时对于重点内容的提炼&#xff0c;便于对技术栈知识的快速回顾以及使用 1.canvas元素 内部坐标&#xff1a;坐标均以左上角为(0, 0)&#xff0c;单一坐标均作为起始坐标创建对象&#xff1a; <c…...

前端架构师需要解决那些问题

假设你是一个大型后台管理系统的前端架构师&#xff0c;你需要解决那些问题&#xff1f; 1、Ui设计规范 大型系统UI得统一吧&#xff1f;各个业务模块的UI设计得高效吧&#xff1f;那就得有规范&#xff0c;直观的说就是原子设计那套东西。加一堆推荐设计稿。 2、基础组件库…...

使用python快速搭建接口自动化测试脚本实战总结

导读 本文将介绍如何使用python快速进行http/https接口自动化测试脚本搭建&#xff0c;实现自动请求、获取结果、数据对比分析&#xff0c;导出结果到Excel等功能&#xff0c;包括python的requests、pandas、openpyxl等库的基本使用方法。 测试需求介绍 通常&#xff0c;在我…...

android studio 字节码查看工具jclasslib bytecode viewer

jclasslib bytecode viewer 是一款非常好用的.class文件查看工具&#xff1b; jclasslib bytecode editor is a tool that visualizes all aspects of compiled Java class files and the contained bytecode. Many aspects of class files can be edited in the UI. In addit…...

Ubuntu上搭建FTP服务

要在Ubuntu上搭建FTP服务器&#xff0c;可以使用常见的FTP服务器软件如vsftpd&#xff08;Very Secure FTP Daemon&#xff09;或ProFTPD。以下是使用vsftpd在Ubuntu上设置FTP服务器的基本步骤&#xff1a; 步骤 1: 安装 vsftpd 打开终端并运行以下命令安装 vsftpd&#xff1a…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...