简易shell

目录

一、整体功能概述

函数准备

1.env命令

2.getenv()函数

3.snprintf

4.strtok()函数

三、全局变量

四、核心功能函数解析

1. 信息获取函数

2. 命令行交互

3. 命令解析

4. 普通命令执行

5. 内置命令处理(核心功能)

五、主函数流程

六、总代码


一、整体功能概述

这是一个增强版的命令行解释器,具有以下功能:

  • 显示彩色命令行提示符
  • 支持内置命令(cd、export、echo)
  • 支持环境变量操作
  • 记录上一条命令的退出状态
  • 为 ls 命令自动添加颜色选项

函数准备

1.env命令

输出对应的环境变量

2.getenv()函数

这是一个获取操作系统环境变量的函数

所需参数:

#include<stdlib.h>
char *getenv(const char *name);//获取当前进程环境变量值

代码:

结果:

3.snprintf

snprintf 是一个安全版本的字符串格式化函数,用于将格式化的数据写入字符串缓冲区,同时防止缓冲区溢出。

函数参数:

int snprintf(char *str, size_t size, const char *format, ...);

参数说明:

str:目标字符串缓冲区

size:缓冲区大小(包括结尾的 null 字符)

format:格式化字符串

...:可变参数列表

返回值:

成功:返回想要写入的字符数(不包括结尾的 null)

失败:返回负值

例子:

char buffer[50];
int n = snprintf(buffer, sizeof(buffer), "Hello, %s!", "World");
// buffer = "Hello, World!"
// n = 13 (实际写入的字符数)

4.strtok()函数

strtok 是 C 标准库中的一个字符串分割函数,用于将字符串按指定的分隔符拆分为多个子串。该函数属于<string.h>头文件,常用于解析字符串数据

参数说明:

str:要分割的字符串(第一次调用时传入,后续传入 NULL)

delim:分隔符字符串(包含所有可能的分隔字符)

返回值:

成功:返回下一个令牌的指针

失败/结束:返回 NULL

例子:

char str[] = "apple,banana,cherry";
char *token = strtok(str, ",");while (token != NULL) {printf("Token: %s\n", token);token = strtok(NULL, ",");
}

结果:

Token: apple
Token: banana
Token: cherry

二、头文件和宏定义

#include <stdio.h>       // 标准输入输出
#include <stdlib.h>      // 标准库函数
#include <string.h>      // 字符串处理
#include <assert.h>      // 断言
#include <unistd.h>      // Unix标准函数
#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 44     // 子进程退出码

三、全局变量

int lastcode = 0;        // 上一条命令的退出码
int quit = 0;            // 退出标志
extern char **environ;   // 系统环境变量
char commandline[LINE_SIZE]; // 命令行输入缓冲区
char *argv[ARGC_SIZE];   // 参数数组
char pwd[LINE_SIZE];     // 当前工作目录缓冲区char myenv[LINE_SIZE];   // 自定义环境变量存储

四、核心功能函数解析

1. 信息获取函数

const char *getusername() // 获取用户名
{return getenv("USER"); // 从环境变量获取
}const char *gethostname() // 获取主机名
{return getenv("HOSTNAME"); // 从环境变量获取
}void getpwd() // 获取当前工作目录
{getcwd(pwd, sizeof(pwd)); // 系统调用获取当前目录
}

2. 命令行交互

void interact(char *cline, int size)
{getpwd(); // 更新当前目录// 显示提示符:[user@host directory]#printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);char *s = fgets(cline, size, stdin); // 读取用户输入assert(s); // 确保读取成功cline[strlen(cline)-1] = '\0'; // 去掉换行符
}

3. 命令解析

int splitstring(char cline[], char *_argv[])
{int i = 0;argv[i++] = strtok(cline, DELIM); // 分割第一个参数while(_argv[i++] = strtok(NULL, DELIM)); // 继续分割剩余参数return i - 1; // 返回参数个数
}

4. 普通命令执行

void NormalExcute(char *_argv[])
{pid_t id = fork(); // 创建子进程if(id < 0){perror("fork");return;}else if(id == 0){// 子进程执行命令execvp(_argv[0], _argv); // 自动搜索PATHexit(EXIT_CODE); // 执行失败退出}else{// 父进程等待子进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id) {lastcode = WEXITSTATUS(status); // 记录退出码}}
}

5. 内置命令处理(核心功能)

int buildCommand(char *_argv[], int _argc)
{// cd 命令:切换目录if(_argc == 2 && strcmp(_argv[0], "cd") == 0){chdir(argv[1]); // 切换目录getpwd(); // 更新当前目录sprintf(getenv("PWD"), "%s", pwd); // 更新PWD环境变量return 1; // 表示是内置命令,不需要执行NormalExcute}// export 命令:设置环境变量else if(_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]); // 复制到全局变量putenv(myenv); // 设置环境变量return 1;}// echo 命令:输出内容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;}// 为 ls 命令自动添加颜色选项if(strcmp(_argv[0], "ls") == 0) {_argv[_argc++] = "--color"; // 添加颜色参数_argv[_argc] = NULL; // 保持数组以NULL结束}return 0; // 不是内置命令,需要执行NormalExcute
}

五、主函数流程

int main()
{while(!quit){ // 主循环// 1. 显示提示符并获取命令interact(commandline, sizeof(commandline));// 2. 解析命令int argc = splitstring(commandline, argv);if(argc == 0) continue; // 空命令跳过// 3. 调试输出(注释状态)// for(int i = 0; argv[i]; i++) printf("[%d]: %s\n", i, argv[i]);// 4. 处理内置命令int n = buildCommand(argv, argc);// 5. 注释中描述了管道功能的实现思路// 这里预留了管道功能的框架// 6. 执行普通命令if(!n) NormalExcute(argv);}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);char *s = fgets(cline, size, stdin);assert(s);(void)s;// "abcd\n\0"cline[strlen(cline)-1] = '\0';
}// ls -a -l | wc -l | head 
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. 交互问题,获取命令行, ls -a -l > myfile / ls -a -l >> myfile / cat < file.txtinteract(commandline, sizeof(commandline));// commandline -> "ls -a -l -n\0" -> "ls" "-a" "-l" "-n"// 3. 子串分割的问题,解析命令行int argc = splitstring(commandline, argv);if(argc == 0) continue;// 4. 指令的判断 // debug//for(int i = 0; argv[i]; i++) printf("[%d]: %s\n", i, argv[i]);//内键命令,本质就是一个shell内部的一个函数int n = buildCommand(argv, argc);// ls -a -l | wc -l// 4.0 分析输入的命令行字符串,获取有多少个|, 命令打散多个子命令字符串// 4.1 malloc申请空间,pipe先申请多个管道// 4.2 循环创建多个子进程,每一个子进程的重定向情况。最开始. 输出重定向, 1->指定的一个管道的写端 // 中间:输入输出重定向, 0标准输入重定向到上一个管道的读端 1标准输出重定向到下一个管道的写端// 最后一个:输入重定向,将标准输入重定向到最后一个管道的读端// 4.3 分别让不同的子进程执行不同的命令--- exec* --- exec*不会影响该进程曾经打开的文件,不会影响预先设置好的管道重定向// 5. 普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

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

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

相关文章

网关资源权限预加载:从冷启动阻塞到优雅上线的完整闭环

网关资源权限预加载:从冷启动阻塞到优雅上线的完整闭环 基于 Spring Cloud Gateway + Spring Cloud Alibaba Nacos ——一篇可落地的技术方案与源码级实现 1. 场景与痛点 在微服务网关层做 统一资源权限校验 时,必须满足: 启动阻塞:所有权限规则加载完成前,不监听端口,拒…

open webui源码分析8—管道

我们可以把Open WebUI想象成一个管道系统&#xff0c;数据通过管道和阀门流动。管道作为open webui的插件&#xff0c;可以为数据构建新的通路&#xff0c;可以自定义逻辑和处理数据&#xff1b;阀门是管道的可配置部件&#xff0c;控制数据流过管道时的行为。管道可以理解成用…

深入理解 C 语言 hsearch 哈希表:限制、技巧与替代方案

概述 C 语言标准库中的 hsearch 系列函数提供了一套简单易用的哈希表实现,包含在 <search.h> 头文件中。这组函数虽然接口简洁,但在实际使用中存在一些重要的限制和注意事项。本文将深入探讨 hsearch 的功能特点、设计局限,并提供实用的解决方案和替代建议。 hsearc…

Web网络开发 -- HTML和CSS基础

HTML 超文本编辑语言 HTML 介绍 HTML的英文全称是 Hyper Text Markup Language&#xff0c;即超文本标记语言。HTML是由WEB的发明者 Tim Berners-Lee &#xff08;蒂姆伯纳斯李&#xff09;和同事 Daniel W. Connolly于1990年创立的一种标记语言&#xff0c; 它是标准通用化标…

Python爬虫实战:研究开源的高性能代理池,构建电商数据采集和分析系统

1. 绪论 1.1 研究背景与意义 随着互联网技术的飞速发展,网络数据已成为信息时代的核心资源之一。从商业角度看,企业通过分析竞争对手的产品信息、用户评价等数据,可制定更精准的市场营销策略;从学术研究角度,研究者通过爬取社交媒体数据、学术文献等,可开展社会网络分析…

项目设计文档——爬虫项目(爬取天气预报)

一、项目背景以及项目意义 项目背景&#xff1a; 爬虫技术的核心目的是自动化地从互联网上采集&#xff0c;提取和存储数据。网络爬虫是一种自动化程序&#xff0c;用于从互联网上抓取数据并进行处理。C语言因其高效性和接近硬件的特性&#xff0c;常被用于开发高性能的网络爬…

Python 操作 PPT 文件:从新手到高手的实战指南

在日常工作和学习中&#xff0c;PPT 是我们展示信息和进行演示的重要工具。无论是制作报告、演讲还是教学课件&#xff0c;PPT 都扮演着不可或缺的角色。然而&#xff0c;当面对大量重复性的 PPT 编辑任务时&#xff0c;手动操作不仅耗时耗力&#xff0c;还容易出错。幸运的是&…

系统设计中的幂等性

1. 基本概念 幂等性&#xff08;Idempotence&#xff09;是系统设计中经常提到的概念。如果某个操作执行一次或多次都能产生相同的结果&#xff0c;那么它就是幂等的。2. 代码示例 下面这段代码是幂等的。无论你调用多少次&#xff0c;show_my_button 的最终状态都是False。 de…

Pandas vs Polars Excel 数据加载对比报告

📊 Pandas vs Polars Excel 数据加载对比报告 1. 数据基本情况 数据文件:data.xlsx 数据规模:23,670 行 3 列 字段: case_time:日期/时间 case_name:公司名称(字符串) board:所属板块(字符串) 2. 加载方式与代码 Pandas import pandas as pdfrom tools import…

Kafka 为什么具有高吞吐量的特性?

Kafka 高吞吐量原因&#xff1a;面试题总结 在面试中&#xff0c;Kafka 的高吞吐量设计是高频考点&#xff0c;核心需围绕“架构设计”“存储优化”“网络效率”“资源利用”四个维度展开&#xff0c;以下是结构化总结&#xff1a; 一、核心架构&#xff1a;并行化与分层设计分…

MCP 协议原理与系统架构详解—从 Server 配置到 Client 应用

1. MCP MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;是开发 Claude 模型的(Anthropic)公司推出的一个开放标准协议&#xff0c;就像是一个 “通用插头” 或者 “USB 接口”&#xff0c;制定了统一的规范&#xff0c;不管是连接数据库、第三方…

uniapp安卓真机调试问题解决总结

uniapp安卓真机调试遇到各种连接不上问题&#xff1a; 手机上打开调试数据线不行&#xff0c;换数据线电脑重启手机重启拔出数据线&#xff0c;换个USB插口。

Linux Qt创建和调用so库的详细教程

一、创建so库1.文件-->新建文件或项目-->Library->C Library&#xff0c;如下图2.工程命名为Example3.一直下一步就可以4、工程创建完成&#xff0c;如下图5、删除Example_global.h6、配置.pro文件# 设置输出目录 DESTDIR $$PWD/output #只生成.so文件 CONFIG plugi…

【深度学习】蒙特卡罗方法:原理、应用与未来趋势

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 之前的文章参考下面的链接&#xf…

区块链技术原理(18)-以太坊共识机制

文章目录前言什么是共识&#xff1f;什么是共识机制&#xff1f;共识机制的核心目标共识机制的类型PoW&#xff08;工作量证明&#xff09;协议&#xff1a;&#xff08;2015-2022&#xff09;PoS&#xff08;权益证明&#xff09;协议&#xff1a;&#xff08;PoS&#xff0c;…

java基础(十五)计算机网络

网络模型概述 为了使得多种设备能通过网络相互通信&#xff0c;并解决各种不同设备在网络互联中的兼容性问题&#xff0c;国际标准化组织&#xff08;ISO&#xff09;制定了开放式系统互联通信参考模型&#xff08;OSI模型&#xff09;。与此同时&#xff0c;TCP/IP模型作为实际…

idea将服务封装为一个jar包

你使用的是 IntelliJ IDEA 2018&#xff0c;这个版本虽然不是最新的&#xff0c;但完全支持通过 图形化界面 打 JAR 包&#xff08;无需命令行&#xff09;&#xff0c;非常适合你在公司内部将 Snowflake 模块打包成通用组件。下面我将 手把手、一步一步、图文流程式地教你&…

ZYNQ [Petalinux的运行]

一、下载ubuntu 下载地址很多&#xff0c;这里提供了一个&#xff1a;http://mirrors.aliyun.com/ubuntu-releases/14.04/ 推荐开始浏览器下载之后复制下载链接使用迅雷下载。 二、虚拟机安装Ubuntu vmware中安装Ubutun–这部分不展示 安装ssh sudo apt install openssh-s…

excel 破解工作表密码

破解Excel工作表密码可通过易用宝工具、VBA脚本或修改文件格式实现&#xff0c;具体方法需根据文件类型和密码保护类型选择。 ‌使用易用宝工具&#xff08;推荐&#xff09;‌ 适用于Excel 2007及以上版本&#xff0c;操作简便且无需编程基础&#xff1a; 下载安装Excel易用…

Deepseek + RAGFlow 搭建本地知识库问答系统

Deepseek RAGFlow 搭建本地知识库问答系统原因为什么要本地部署RAG模型和微调模型区别本地部署流程1. 下载 ollama &#xff0c;通过ollama把Deepseek模型下载到本地运行。2. 下载RAGFlow 源代码和 Docker &#xff0c;通过Docker部署RAGFlow。3. 在RAGFlow中构建个人知识库并…