【Linux】手把手教你制作一个简易shell——(进程创建fork进程替换wait与进程等待exec的应用)(自定义shell程序设计)

news2024/9/23 17:47:25

前言

大家好吖,欢迎来到 YY 滴Linux系列 ,热烈欢迎! 本章主要内容面向接触过C++ Linux的老铁
主要内容含:
在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

  • YY的《C++》专栏
  • YY的《C++11》专栏
  • YY的《Linux》专栏
  • YY的《数据结构》专栏
  • YY的《C语言基础》专栏
  • YY的《初学者易错点》专栏
  • YY的《小小知识点》专栏
  • YY的《单片机期末速过》专栏
  • YY的《C++期末速过》专栏
  • YY的《单片机》专栏
  • YY的《STM32》专栏
  • YY的《数据库》专栏
  • YY的《数据库原理》专栏

目录

  • 一.前置知识
    • 【1】Shell和Bash简述
    • 【2】Bash的输入原理——指针数组
  • 二.自定义shell程序设计
    • 【1】<主函数模块>——大体框架
      • 1.程序设计框架
      • 2.程序设计细节
    • 【2】<打印提示符>模块
      • 1.程序设计框架
      • 2.程序设计细节
    • 【3】<分割字符串>模块
      • 1.程序设计框架
      • 2.程序设计细节
    • 【4】<执行对应的命令>模块
      • 1.程序设计框架
      • 2.程序设计细节

一.前置知识

【1】Shell和Bash简述

  • Shell 是一种命令行界面,是用户与系统之间的接口,允许用户执行命令来 管理系统资源、运行程序等
  • Bash 是 Shell 的一种实现,也是目前最流行的 Shell 之一

【2】Bash的输入原理——指针数组

  1. 我们运行Linux时会出现, bash提示符和命令行 ,我们接下来也要实现这两点

  2. 本质是通过 空格 作为分隔符,把一个一个字符串分隔开载入 指针数组中 ;

  3. 在父进程bash进程中,创建一个子进程,环境变量也会传递给子进程,并进行 进程等待wait
    在这里插入图片描述

  4. 在子进程中通过 进程替换exec ,执行 指针数组中 中的命令(通过环境变量)
    在这里插入图片描述

ifn<=0,直接结束省的创建子进程

cd就不行。因为是子进程的cd…

二.自定义shell程序设计

【1】<主函数模块>——大体框架

1.程序设计框架

  • 根据前置知识中的实现原理
    在这里插入图片描述
  • 我们主函数中要有对应模块:
  1. 打印提示符&&获取用户命令字符串获取成功: getUserCommand函数
  2. 分割字符串: commandSplit函数
  3. 执行对应的命令: execute函数

2.程序设计细节

  1. 设置一个命令行获取字符数组:usercommand
  2. 设置一个存储———分割usercommand数组后的字符串的地址——的指针数组:argv
#define NUM 1024
#define SIZE 64

int main()
{
    while(1){  //shell要不停跑,死循环
    
        char usercommand[NUM];
        char *argv[SIZE];
        // 1. 打印提示符&&获取用户命令字符串获取成功
        int n= getUserCommand(usercommand, sizeof(usercommand));
        // 2. 分割字符串
        // "ls -a -l" -> "ls" "-a" "-l"
        commandSplit(usercommand, argv);
        // 3. 执行对应的命令
        execute(argv);
    }
}

【2】<打印提示符>模块

1.程序设计框架

  • 提示符信息包括:1.HOME 2.USER 3.HOSTNAME
  • 我们将上面3个信息分别封装,在getUserCommand函数中统一打印
  • 我们通过getenv函数可以获取 环境变量的地址,进而打印
    在这里插入图片描述
  • command参数 接收命令行获取 字符数组usercommand
  • num参数 接收 字符数组长度

2.程序设计细节

  1. C语言默认会打开三个输入输出流:stdin键盘 stdout显示器stderr显示器,我们用到stdin获取输入流

  2. 不用scanf,用fget函数的原因:scanf遇到空格停下来,命令行本身就会出现空格。故采用行获取接口fgets
    在这里插入图片描述

  3. command参数 接收命令行获取 字符数组usercommand ,我们输入命令后,最终你还是会输入\n——导致执行结果和shell之间出现空行;所以我们在输入完后要把command最后一个字符'\n'换成'\0'

int getUserCommand(char *command, int num)
{
    printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());
    char *r = fgets(command, num, stdin); // 最终你还是会输入\n
    if(r == NULL) return -1;
    // "abcd\n" "\n"
    command[strlen(command) - 1] = '\0'; // 有没有可能越界?不会
    return strlen(command);
}

char *homepath()
{
    char *home = getenv("HOME");
    if(home) return home;
    else return (char*)".";
}

const char *getUsername()
{
    const char *name = getenv("USER");
    if(name) return name;
    else return "none";
}
const char *getHostname()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname) return hostname;
    else return "none";
}

【3】<分割字符串>模块

1.程序设计框架

  • 这个模块,我们要通过 空格 作为分隔符,把一个一个字符串分隔开载入 指针数组 argv
  • in参数 接收命令行获取 字符数组usercommand
  • *out[]参数 输出型参数,用于传出 分割usercommand数组后的字符串的地址——的指针数组argv

2.程序设计细节

  • 通过strstok函数分割; 注意语法,分成首次分割,和剩余分割
    在这里插入图片描述
#define SEP " "
void commandSplit(char *in, char *out[])
{
    int argc = 0;
    out[argc++] = strtok(in, SEP);
    while( out[argc++] = strtok(NULL, SEP));
}

【4】<执行对应的命令>模块

1.程序设计框架

我们回顾原理部分:

  • 在父进程bash进程中,创建一个子进程,环境变量也会传递给子进程,并进行 进程等待wait
    在这里插入图片描述

  • 在子进程中通过 进程替换exec ,执行 指针数组中 中的命令(通过环境变量)
    在这里插入图片描述

于是我们设计出:

  1. fork函数创建子进程
  2. 子进程进行进程替换execvp函数,用到 分割usercommand数组后的字符串的地址——的指针数组argv
  3. 父进程等待子进程

2.程序设计细节

1. fork函数:
在这里插入图片描述

2. execvp函数: 由于我们用到了指针数组argv,所以用exec系列的vp尾缀,execvp 表示v(vector)数组,p(可以使用环境变量PATH,无需写全路径)
在这里插入图片描述

3. waitpid函数:不关心后续操作,status参数设置成NULL,options参数设置成0
在这里插入图片描述

int execute(char *argv[])
{
    pid_t id = fork();
    if(id < 0) return -1;
    else if(id == 0) //child
    {
        // exec command
        execvp(argv[0], argv); // cd ..
        exit(1);
    }
    else // father
    {
        int status = 0;
        pid_t rid = waitpid(id, NULL, 0);
    }
    return 0;
}

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

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

相关文章

华为昇腾智算中心-智算中心测试方案与标准

本方案是企业内训课程《华为昇腾智算中心深度技术研修》的一部分授课课件的样例。方案内容中详细阐述了华为昇腾环境下智算中心的测试方案和标准&#xff0c;以确保硬件和软件系统在实际部署和运行中的高效性和稳定性。主要内容包括集群硬件清单、节点拓扑配置以及环境配置。硬…

企业微信oauth2的code换用户身份一直40029解决方案

序&#xff1a; 雪狼的微信表情包&#xff0c;欢迎下载【程序员雪狼】微信表情 - 来自微信表情商店&#xff0c;扫二维码下载表情 正文&#xff1a; 雪狼在用oauth2返回的code要去请求getuserinfo3rd接口的时候&#xff0c;报错如下40029 一样&#xff0c;肝了一天&#xff0c…

本地部署高颜值某抑云音乐播放器Splayer并实现无公网IP远程听歌

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

湖北建筑类初级职称申报的全方位解读

湖北建筑类初级职称申报的全方位解读 湖北工程行业助理工程师/初级职称申报评审 湖北建筑类助理工程师/初级职称正常申报目前都是电子版证书&#xff0c;湖北省政务服务网查询生成&#xff0c;省网查询&#xff0c;后期都会同步G网查询。 湖北建筑类助理工程师纸质版 1.之前申…

kubernetes网络(一)之calico详解

摘要 本文介绍Kubernetes最流行的网络解决方案calico。 kubernetes中不同宿主上的pod需要相互通信&#xff0c;如果按TCP/IP协议分层进行分类&#xff1a; 二层方案&#xff1a;flannel的udp和vxlan模式 三层方案&#xff1a;flannel的host-gw模式&#xff1b;calico的IPIP模…

ReduceLROnPlateau学习率衰减设置

学习率衰减有多种方式&#xff0c;本次采用optim.lr_scheduler.ReduceLROnPlateau&#xff0c;这种方式代表在发现loss不再降低或者acc不再提高之后&#xff0c;降低学习率。 model GRU().to(device) criterion nn.CrossEntropyLoss().to(device) optimizer optim.AdamW(m…

YOLOv10独家改进:红外场景严重遮挡和重叠目标解决方案 | 一种新的自适应算法轻量级通道分割和变换(ALSS)模块,自适应特征提取优化策略

💡💡💡本文解决什么问题:红外检测场景存在严重遮挡和重叠目标时的局限性的问题点。 💡💡💡提出了一种新的自适应算法轻量级通道分割和变换(ALSS)模块。该模块采用自适应信道分裂策略优化特征提取,并集成信道变换机制增强信道间的信息交换。这改善了模糊特征的提…

简单多状态dp第一弹 leetcode -面试题17.16.按摩师 -213.打家劫舍II

a​​​​​​​面试题 17.16. 按摩师 按摩师 题目: 分析: 使用动态规划解决 状态表示: dp[i] 表示&#xff1a;选择到 i 位置时&#xff0c;此时的最长预约时长。 但是我们这个题在 i 位置的时候&#xff0c;会面临 选择 或者 不选择 两种抉择&#xff0c;所依赖的状态需要…

响应式的几种解决方案——媒体查询、flex、grid、多列布局、瀑布流和数据可视化屏幕的缩放处理

media媒体查询 媒体查询入门指南 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…

Java面试篇基础部分- Java中的阻塞队列

首先队列是一种前进后出的操作结构,也就是说它只允许从队列前端进入,从队列后端退出。这个前端和后端看个人如何理解,也就是通常所说的入队和出队,队头和队尾。 阻塞队列和一般队列的不同就在于阻塞队列是可以阻塞的,这里所说的并不是说队列中间或者队头队尾被拦截了,而是…

HTML与JavaScript结合实现简易计算器

目录 背景&#xff1a; 过程&#xff1a; 代码: HTML部分解析&#xff1a; body部分解析&#xff1a; JavaScript部分解析&#xff1a; 效果图 &#xff1a; 总结: 背景&#xff1a; 计算器是一个典型的HTML和javaScript结合使用的例子&#xff0c;它展示了如何使用H…

Kibana中突然看不到日志ElasticSearch突然采集不到日志问题解决分析

问题原因 起因之前我们项目是采用elk&#xff08;elasticsearchlogstashkibana&#xff09;的方式下部署日志采集系统&#xff0c;今天突然发现Kibana中所有项目日志都没打印&#xff0c;更奇怪的是线上的项目都正常运行&#xff0c;并无异常&#xff0c;这时让人陷入了深思&a…

电商新纪元:37即拼模式如何重塑市场格局

在电商领域的浩瀚星海中&#xff0c;拼团模式犹如一颗璀璨的星辰&#xff0c;历经近十年的洗礼&#xff0c;依旧熠熠生辉&#xff0c;不断吸引着商家与消费者的目光。近期&#xff0c;一家电商平台凭借其独树一帜的“37即拼”模式&#xff0c;仅在一周内便狂揽5万订单&#xff…

移动硬盘未格式化:深度解析、恢复策略与预防措施

现象解读&#xff1a;移动硬盘的“未格式化”迷局 在数字信息爆炸的时代&#xff0c;移动硬盘作为数据存储与传输的重要工具&#xff0c;扮演着不可或缺的角色。然而&#xff0c;当用户试图访问移动硬盘时&#xff0c;却可能遭遇一个令人困惑的提示——“未格式化”。这一现象…

一大堆的图片怎么批量重命名从1到50?这个方法你必须知道

一大堆的图片怎么批量重命名从1到50&#xff1f;这个方法你必须知道。有许多的人因为工作的问题&#xff0c;经常需要用到给一些图片&#xff0c;文件&#xff0c;文档等重命名的事情。很多人都不知道有快速处理的办法&#xff0c;只能一个个右键重命名&#xff0c;或者批量选择…

Three.js 3D人物漫游项目(下)

本文目录 前言最终效果1、效果回顾2、编写人物模型动画执行类并调用2.1 代码2.2 代码解读2.3 实例化动画类并调用2.4 效果2.4.1 休息动画2.4.2 跑步动画2.4.3 走路动画2.4.4 舞蹈1动画2.4.5 舞蹈2动画3、键盘控制动画3.1 站立休息、走、跑、舞蹈1、舞蹈2代码3.1.1 效果3.2 跳跃…

1000Km弹射巡飞器技术详解

随着现代战争形态的演变及科技水平的飞速提升&#xff0c;远程侦察、打击与持久监视能力成为各国军事力量建设的重要方向。1000Km弹射巡飞器作为一种新型无人机系统&#xff0c;凭借其超远的航程、长时间的续航以及高度的灵活性&#xff0c;成为近年来军事技术领域的研究热点。…

开源ids snort (windows版)

Snort-IPS-on-Windows-main资源-CSDN文库 GitHub - eldoktor1/Snort-IPS-on-Windows: A comprehensive guide to installing and configuring Snort IPS on Windows, ensuring robust network security 手动打造Snortbarnyard2BASE可视化告警平台 - FreeBuf网络安全行业门户 …

记录|项目管理软件PingCode使用【待更新】

目录 前言一、项目的创建二、需求2.1 需求页面认识 2.2 添加需求更新时间 前言 参考视频&#xff1a; 快速上手PingCode&#xff1a;创建Scrum、看板、瀑布项目 这个主要用于细化自己和队友、团队之间的任务&#xff0c;进一步管理整个的软件开发的进度。【如下图】 一、项目的…

Python可迭代对象(2)

目录 3。字典 字典的构造 字典的添加和修改 字典推导式的使用 以上全部代码编译结果展示 字典复习 4。集合(set) 集合的创建 集合的运算 集合的添加和删除元素 以上全部代码的编译结果 序列解包&#xff08;Sequence unpacking&#xff09; 3。字典 字典的构造 #字…