Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】手把手教你制作一个简易shell——(进程创建fork进程替换wait与进程等待exec的应用)(自定义shell程序设计)

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

作者头像
YY的秘密代码小屋
发布于 2024-09-24 11:27:10
发布于 2024-09-24 11:27:10
23100
代码可运行
举报
文章被收录于专栏:C++系列C++系列
运行总次数:0
代码可运行

前言 大家好吖,欢迎来到 YY 滴Linux系列 ,热烈欢迎! 本章主要内容面向接触过C++ Linux的老铁 主要内容含:

一.前置知识

【1】Shell和Bash简述

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

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

  1. 我们运行Linux时会出现, bash提示符和命令行 ,我们接下来也要实现这两点
  2. 本质是通过 空格 作为分隔符,把一个一个字符串分隔开载入 指针数组中 ;
  3. 在父进程bash进程中,创建一个子进程,环境变量也会传递给子进程,并进行 进程等待wait
  1. 在子进程中通过 进程替换exec ,执行 指针数组中 中的命令(通过环境变量)

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

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

二.自定义shell程序设计

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

1.程序设计框架
  • 根据前置知识中的实现原理
  • 我们主函数中要有对应模块:
  1. 打印提示符&&获取用户命令字符串获取成功: getUserCommand函数
  2. 分割字符串: commandSplit函数
  3. 执行对应的命令: execute函数
2.程序设计细节
  1. 设置一个命令行获取字符数组:usercommand
  2. 设置一个存储———分割usercommand数组后的字符串的地址——的指针数组:argv
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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
  1. command参数 接收命令行获取 字符数组usercommand ,我们输入命令后,最终你还是会输入\n——导致执行结果和shell之间出现空行;所以我们在输入完后要把command最后一个字符'\n'换成'\0'
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数分割; 注意语法,分成首次分割,和剩余分割
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
前言:在Linux的浩瀚宇宙中,Shell脚本无疑是连接用户与系统之间的桥梁,它赋予了用户强大的自动化处理能力,使得繁琐的重复性任务变得轻松高效。对于每一位Linux爱好者、系统管理员或是开发人员而言,掌握Shell脚本编写技能无疑是一项不可或缺的宝贵财富
Eternity._
2024/09/21
3850
【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
【Linux】进程实践项目 —— 自主shell编写
不管前方的路有多苦,只要走的方向正确,不管多么崎岖不平,都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》
叫我龙翔
2024/04/02
2100
【Linux】进程实践项目 —— 自主shell编写
【linux】自主shell编写
现在完成对命令行输出的编写,我们目标是将变量名放到一个输出型参数commandline中,这里需要一个函数snprintf:
用户11029103
2025/02/25
5690
【linux】自主shell编写
【Linux】进程替换
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343 🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html
秦jh
2024/10/01
3800
【Linux】进程替换
初识Linux · 自主Shell编写
本文介绍是自主Shell编写,对于shell,即外壳解释程序,我们目前接触到的命令行解释器,有bash,还有SSH,对于今天模拟实现的Shell编写,我们模拟的是bash,以及需要的预备知识前文已经介绍了,进程的多方面的知识,在自主Shell编写里面比较重要的是进程程序替换,进程终止,进程等待,进程状态什么的,都是自主Shell编写里面的辅助知识罢了。
_lazy
2024/10/16
1770
初识Linux · 自主Shell编写
从零开始手写Shell:详解命令行解释器的实现原理
本实现仅需200行C++代码,却能完整展现Shell的核心工作机制。让我们通过解剖麻雀的方式,逐步拆解这个微型Shell的实现过程。
DevKevin
2025/02/13
3160
从零开始手写Shell:详解命令行解释器的实现原理
深入了解linux系统—— 自定义shell
我们知道,我们程序启动时创建的进程,它的父进程都是bash也就是shell命令行解释器;
星辰与你
2025/05/12
1030
深入了解linux系统—— 自定义shell
【寻找Linux的奥秘】第九章:自定义SHELL
Shell 是一种用于与操作系统交互的命令行界面程序。它充当用户和操作系统内核之间的中介,通过用户输入的命令来执行操作,提供与操作系统的互动。
code_monnkey_
2025/06/02
1000
【寻找Linux的奥秘】第九章:自定义SHELL
【Linux课程学习】:《简易版shell实现和原理》 《哪些命令可以让子进程执行,哪些命令让shell执行(内键命令)?为什么?》
1.我们让子进程执行cd ..命令的时候,为什么我们执行pwd命令的时候,还是和之前一样,路径没有变化?
用户11396661
2024/12/09
2310
【Linux课程学习】:《简易版shell实现和原理》 《哪些命令可以让子进程执行,哪些命令让shell执行(内键命令)?为什么?》
【Linux】简易版Shell实现(附源码)
本篇文章,我们将一起踏上一段有趣的旅程,基于之前学习的进程相关概念以及控制接口,仿照CentOS – Bash的工作流程,实现一个功能虽然简单,但足以让你深刻理解 Shell 工作原理的迷你 Shell。
ephemerals__
2025/05/19
1840
【Linux】简易版Shell实现(附源码)
极简shell制作
  通过我们之前所学Linux知识以及C语言的知识,到目前为止,我们完全可以独立完成简易shell的制作,那么话不多说,开始今天的话题!
用户11029129
2024/06/04
2440
极简shell制作
【Linux】模拟实现一个shell
一段时间的没有更新是由于最近开学期间比较的忙,同时也是由于刚开学的几门课才学习的时候有点迷糊,需要在学校课堂上花的时间更多了,所以才没有更新的,求放过。
薛定谔方程难
2024/10/01
2260
Linux模拟实现【简易版bash】
Linux 系统主要分为 内核(kernel) 和 外壳(shell),普通用户是无法接触到内核的,因此实际在进行操作时是在和外壳程序打交道,在 shell 外壳之上存在 命令行解释器(bash),负责接收并执行用户输入的指令,本文模拟实现的就是一个 简易版命令行解释器
北 海
2023/07/01
4190
Linux模拟实现【简易版bash】
Linux:进程替换
        为什么要有进程替换呢???比方说我们想用fork创建一个子进程去帮助我们完成一个工作,这个工作我们需要封装成一个函数去使用,但难道我们每次都要自己写一个函数吗?或者说子进程一定要用我父进程的代码吗?  难道不可以是我们之前已经写好的一个可执行程序,当我想去执行的时候直接让子进程用一下不就可以了,但是因为操作系统不相信任何人,所以我们也必须要有一些系统调用接口来完成这个工作。
小陈在拼命
2024/10/15
7350
Linux:进程替换
【Linux】实现一个简易的shell命令行
然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。所以要写一个shell,需要循环以下过程:
修修修也
2024/10/25
2840
【Linux】实现一个简易的shell命令行
【Linux系统编程】八、进程程序替换
​ 将磁盘中指定的程序加载到内存中,让指定的进程进行执行。不论是哪种后端语言写的程序,exec* 类的函数都可以调用。
利刃大大
2025/02/23
1360
【Linux系统编程】八、进程程序替换
Linux探秘坊-------9.进程控制
当进程调⽤⼀种exec函数时,该进程的 ⽤⼾空间代码和数据完全被新程序替换 ,从新程序的启动例程开始执⾏。调⽤exec并不创建新进程,所以调⽤exec前后该进程的 id并未改变。
hope kc
2025/03/22
1070
Linux探秘坊-------9.进程控制
利用C语言制作一个简易Shell
Shell 是一种提供用户与操作系统交互的命令行解释器,它接受用户的命令并调用操作系统的功能来执行这些命令。Shell 既可以作为一种交互式的命令行工具,又可以作为编写和运行脚本的编程环境。广泛使用于 Unix 和 Linux 系统中,Shell 也在其他操作系统中有类似的实现。
Yui_
2024/10/23
4210
利用C语言制作一个简易Shell
【Linux】从零开始手搓 Shell (超详解)
具体思路 》shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。
IsLand1314
2024/10/18
3860
【Linux】从零开始手搓 Shell (超详解)
【Linux】进程控制
在 Linux 中 fork 函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。我们在前面的学习中也遇到过,所以在此简单介绍一下。
YoungMLet
2024/03/01
2950
【Linux】进程控制
相关推荐
【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档