【Linux】模拟实现简易shell命令行:基于进程创建、进程等待、进程替换等知识的练习

程序功能:模拟实现一个自己的shell执行命令行。

涉及知识:字符串输入及操作函数、子进程创建、进程等待、进程替换、环境变量及获取、添加环境变量的函数

额外需要了解的功能函数:chdir(char* path)函数——改变当前工作路径 

名词解释:内建命令(Built-in Commands)是指在命令行解释器(如 Bash、Zsh 等)中直接实现的命令。这些命令不需要调用外部程序或二进制文件,而是由解释器本身提供和处理。

注意事项:

易错点 :int putenv(char *str);

putenv函数参数的指针str指向的数组,在getenv使用通过该函数导入的环境变量时,必须保证在调用getenv函数的区域中,str指针指向的数组依然有效才可以。

指针有效性问题:putenv 函数将传入的字符串指针直接接管为环境变量的一部分,而不会复制传入的字符串。因此,一旦 putenv 被调用后,传入的指针必须保持有效,直到程序退出或者重新用新值调用 putenv。

// 程序功能:模拟实现一个自己的shell执行命令行。
// 涉及知识:字符串输入及操作函数、子进程创建、进程等待、进程替换、环境变量及获取、添加环境变量的函数
// 额外需要了解的功能函数:chdir(char* path)函数——改变当前工作路径#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdio>
#include <errno.h>#define SIZE 512 // 定义缓冲区大小默认512字节
#define NUM 32
#define CUT_CHAR " "
char cwd[SIZE*2] = {'\0'};
// 程序设计思路:
// 对象设计:shell
// 成员变量:1、string——获取命令行参数  2、string——储存命令行信息
class shell
{
public:// 默认构造函数// 1、获取命令行信息,包括:使用者USER,主机名HOSTNAME,当前工作路径PWDvoid get_command_line(){// 确保环境变量都存在于默认环境变量表中,如果不存在,getenv函数会返回为NULL,这个时候需要提前使用指针接收并判断是否为NULL;// 由于Ubuntu系统中不存在HOSTNAME默认环境变量,我们先对其使用putenv函数导入自定义的HOSTNAME环境变量char tmp_hostname[SIZE];snprintf(tmp_hostname, SIZE, "HOSTNAME=hecs-135712");if (putenv(tmp_hostname) != 0){perror("export env fail!!!");exit(errno);}user = getenv("USER");hostname = getenv("HOSTNAME");pwd = getenv("PWD");if (user == nullptr){std::cerr << "Error: user variables not set." << std::endl;exit(errno);}if (hostname == nullptr){std::cerr << "Error: hostname variables not set." << std::endl;exit(errno);}if (pwd == nullptr){std::cerr << "Error: pwd variables not set." << std::endl;exit(errno);}char *tmp = getenv("HOME");if (strncmp(pwd, tmp, strlen(tmp)) == 0){pwd[0] = '~';strcpy(pwd + 1, pwd + strlen(tmp));}}void print_command_line(){printf("%s@%s:%s> ", user, hostname, pwd);fflush(stdout);}void get_command_option(){char buffer[SIZE] = {'\0'};fgets(buffer, sizeof(buffer), stdin);buffer[strlen(buffer) - 1] = '\0';argv[0] = strtok(buffer, CUT_CHAR);int index = 1;while (argv[index++] = strtok(NULL, CUT_CHAR));}void execute_command(){// 创建子进程,使用进程替换执行对应的命令int id = fork();if (id == 0){int ret = execvp(argv[0], argv);if (ret == -1){perror("process replace fail!!!");exit(errno);}}else if (id > 0){int status = 0;pid_t ret = waitpid(id, &status, 0);if (WIFEXITED(status)){int lastnode = WEXITSTATUS(status);if (lastnode != 0){printf("%s:%s\n", argv[0], strerror(errno));}}}else{perror("build child process fail!!!");exit(errno);}}void test(){printf("test:");puts(getenv("PWD"));}void cd(){const char *path = argv[1];if (argv[1] == NULL){path = getenv("HOME");}int ret = chdir(path);if (ret == -1){perror("change directory fail!!!");exit(errno);}char str[SIZE] = {'\0'};getcwd(str, sizeof(str));//char cwd[SIZE*2] = {'\0'}; 定义为全局变量//原因:你在执行cd命令后更新了PWD环境变量,但是在下一个循环迭代中,getenv("PWD")返回空指针。//这是因为putenv函数将传入的字符串指针接管为环境变量的一部分,并不会复制传入的字符串,而是直接使用传入的指针。//这意味着,一旦你调用putenv(cwd)之后,cwd指向的内存必须保持有效,直到程序结束或者通过putenv重新设置新的值。snprintf(cwd, sizeof(cwd), "PWD=%s", str);puts(cwd);putenv(cwd);}bool is_bash_command(){bool yes = false;if (strcmp(argv[0], "cd") == 0){cd();yes = true;}return yes;}private:char *user = NULL;char *hostname = NULL;char *pwd = NULL;char *argv[NUM] = {nullptr}; // 存储命令及选项
};
int main()
{shell tmp;while (1){tmp.test();//获取命令行tmp.get_command_line();// 打印命令行tmp.print_command_line();// 获取命令及选项tmp.get_command_option();// 判断是否为内建命令if (!tmp.is_bash_command())// 执行命令qtmp.execute_command();}return 0;
}

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

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

相关文章

PostgreSQL入门与进阶学习,体系化的SQL知识,完成终极目标高可用与容灾,性能优化与架构设计,以及安全策略

​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 概述基础篇初级篇进阶篇…

安装 GCC 编译器和开发工具

安装 GCC 编译器和开发工具 更新软件包管理器 在 CentOS 终端中&#xff0c;首先更新软件包管理器&#xff1a; sudo yum update -y安装 GCC 编译器和其他开发工具 运行以下命令安装 GCC 编译器和其他必要的开发工具&#xff1a; sudo yum groupinstall -y "Development T…

通过不同方式在 Vue 3 中传递路由参数

如何通过不同方式在 Vue 3 中传递路由参数&#xff0c;并在组件中使用 defineProps 或其他组合式 API 获取这些参数&#xff1f; 1. 通过 path 参数传递 最常见的方式&#xff0c;通过在路由路径中定义动态参数&#xff0c;并在路由配置中设置 props: true&#xff0c;将参数…

S5730举例

校园网络拓扑和设备 大学校园网络通常使用多层级结构&#xff0c;核心是承载所有交换机的中心点&#xff0c;而分支则连接到核心以支持更广泛的网络覆盖。华为的S5730交换机通常用于大型企业和校园网络中&#xff0c;提供高性能和可靠性。 STP (Spanning Tree Protocol) 作用&…

事务、函数和索引

目录 什么是事务&#xff1f; 事务的ACID原则&#xff1a; 事务的操作 事务的原子性、一致性、持久性 事务的隔离性 什么是事务的隔离性? 用什么方法实现事务的隔离性&#xff1f; MySQL中的锁 锁分类&#xff1a; 事务的隔离级别 事务并发问题 InnoDB的MVCC MVCC…

【C++】红黑树的应用(封装map和set)

✨ 青山一道同云雨&#xff0c;明月何曾是两乡 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &…

Unity UGUI 实战学习笔记(3)

仅作学习&#xff0c;不做任何商业用途 不是源码&#xff0c;不是源码! 是我通过"照虎画猫"写的&#xff0c;可能有些小修改 不提供素材&#xff0c;所以应该不算是盗版资源&#xff0c;侵权删 拼UI 提示面板的逻辑 using System.Collections; using System.Col…

大数据——Hive原理

摘要 Apache Hive 是一个基于 Hadoop 分布式文件系统 (HDFS) 的数据仓库软件项目&#xff0c;专为存储和处理大规模数据集而设计。它提供类似 SQL 的查询语言 HiveQL&#xff0c;使用户能够轻松编写复杂的查询和分析任务&#xff0c;而无需深入了解 Hadoop 的底层实现。 Hive…

Firefox扩展程序和Java程序通信

实现Firefox扩展程序&#xff0c;和Java RMI Client端进行通信。 在Firefox工具栏注册按钮&#xff0c;点击按钮后弹出Popup.html页面&#xff0c;引用Popup.js脚本&#xff0c;通过脚本向Java RMI client发送消息&#xff0c;Java RMI Client接收消息后转发到Java RMI Server…

python写的登陆邮箱 接收邮件

import time from datetime import datetime, timedelta from mail import get_mails, server_login from notion import sync_bills from data_handler import alipay_data, wechat_data server server_login() if server ! -1: print(“尝试获取邮件”) waiting_time da…

大模型-鲁棒性总结-2024-7-28

文章目录 1.大语言模型的鲁棒性概述2.自然噪声的鲁棒性2.1.真实标签任务的性能2.2.开放式任务的表现 3.评估分布外&#xff08;OOD&#xff09;任务的弹性3.1.OOD检测3.2.OOD泛化 4.对抗鲁棒性5.提示的分布假设6.幻觉检测7.遗忘鲁棒性技术评估8.数学推理任务中的鲁棒性评估9.代…

MyBatis的入门操作--打印日志和增删改查(单表静态)

下面介绍注解和xml实现crud的操作 目录 一、日志打印和参数传递 1.1.使用mybatis打印日志 1.2.参数传递细节 二、crud&#xff08;注解实现&#xff09; 2.1.增(insert) 2.2.删(delete) 和 (update) 2.3.查(select) 三、crud&#xff08;xml实现&#xff09; 3.1.准备…

【算法】浅析遗传算法【附完整示例】

遗传算法&#xff1a;模拟自然选择&#xff0c;优化问题求解 1. 引言 在计算机科学和优化问题求解中&#xff0c;遗传算法是一种借鉴生物进化理论的启发式搜索算法。它模拟自然选择和遗传机制&#xff0c;通过迭代搜索最优解。本文将介绍遗传算法的原理、步骤及其在实际应用中…

中国居民膳食指南书籍知识点汇总

人如果吃不好&#xff0c;就不能好好思考&#xff0c;好好爱&#xff0c;好好休息。——维吉尼亚伍儿夫 文章目录 书籍简介饮食准则推荐膳食图示 准则一&#xff1a;食物多样&#xff0c;合理搭配合理搭配的方法平衡膳食的科学原理均衡饮食的作用食物功效&#xff08;有科学实验…

LeeCode Practice Journal | Day25_Backtracking04

491. 非递减子序列 题目&#xff1a;491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a;代码随想录 (programmercarl.com) solution public class Solution {public List<IList<int>> results new List<IList<int>>();pu…

力扣224【基本计算器】

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 注意:不允许使用任何将字符串作为数学表达式计算的内置函数&#xff0c;比如 eval() 。 1 < s.length < 3 * 105 s 由数字、‘’、‘-’、‘(’、‘)’、和 ’ ’ 组成 s 表示一个有效的…

02、爬虫数据解析-Re解析

数据解析的目的是不拿到页面的全部内容&#xff0c;只拿到部分我们想要的内容内容。 Re解析就是正则解析&#xff0c;效率高准确性高。学习本节内容前需要学会基础的正则表达式。 一、正则匹配规则 1、常用元字符 . 匹配除换行符以外的字符 \w 匹配字母或数字或下划…

基于Python的房产数据分析系统的设计与实现(源码+lw+部署文档+讲解等)

文章目录&#xff1a; 目录 详细视频演示 设计文档详细参考 技术开发的参考技术栈&#xff01; 2.1 Python语言 2.2 Django框架 2.3 MySQL 2.4 Hadoop介绍 2.5 Scrapy介绍 4.2 系统结构设计 4.3 数据库设计 界面设计与功能实现 5.1系统登录注册实现 5.2管理员模块…

【请求代理】springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能

springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能 一、前言二、解决思路三、基于gateway实现四、基于过滤器Filter实现五、问题总结 **注&#xff1a;本文源码获取或者更多资料&#xff0c;关注公众号&#xff1a;技术闲人**一、前言 在项目开发时会遇到w…

基于yolov8的口罩检测模型

项目介绍 本项目基于yolov8对图像进行训练&#xff0c;可以检测戴口罩的人与没有带口罩的人的图片和视频&#xff0c;除此之外&#xff0c;还提供了数据分析界面&#xff0c;支持检测过的信息转化为excel&#xff0c;信息可视化等功能 配置过程 软件开发环境:python3.9 系统…