Linux一学就会——编写自己的shell

这篇具有很好参考价值的文章主要介绍了Linux一学就会——编写自己的shell。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

编写自己的shell

进程程序替换

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数
以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

Linux一学就会——编写自己的shell

替换函数

其实有几种以exec开头的函数,统称exec函数:

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

解释
exec是函数替换的开头,后面跟的都是多加的功能:
l:list的简写,表示参数采用列表。
p:path的简写,就是自动搜索并添加环境变量。可以使用环境变量PATH,无需写全路径。
v:vector的简写,是可以用参数数组。
e:environment的简写,就是环境变量。就是带e都要自己组装环境变量,而且是数组形式传入。

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。

Linux一学就会——编写自己的shell
可变参数
我们刚刚可以看到int execl(const char *path, const char *arg, …);
比如:

int func(int, ... )  {
   .
   .
   .
}
 
int main() {
   func(2, 2, 3);
   func(3, 2, 3, 4);
}

函数 func() 最后一个参数写成省略号,即三个点号(…),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
使用宏 va_end() 来清理赋予 va_list 变量的内存。

也就是说可变参数是放在传入参数最后,放在中间必须在输入结束之后再输入一个NULL,而且可变参数和前面放的参数类型一致。
exec调用举例:

#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在
man手册第3节。这些函数之间的关系如下图所示。

Linux一学就会——编写自己的shell

开始写自己的shell

用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左
向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结
束。Linux一学就会——编写自己的shell
每当输入一个命令时,bash就会创建一个子进程来实现的要的命令进程,上述就是ls,等待子进程退出,主进程继续等待命令输入和读取命令,再创建子进程等…

第一步创建一个界面然后让他一直死循环

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include  <ctype.h>

#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{
    memset(command, 0x00, MAX_CMD);
    printf("[Tomshell$]#");
    fflush(stdout);
    if (scanf("%[^\n]%*c", command) == 0)
    {
        getchar();
        return -1;
    }
    return 0;
}
int main(int argc, char *argv[])
{
    while (1) // shell主循环
    {
        myshell_face()}
    return 0;
}

当然这个界面是可以输入命令的,但是你怎么输入都没用。

接下来是解析你输入的命令了。
把刚刚输入的命令行分析出来,比如遇到空格就会再次push_back命令行数组,当有空格就跳过空格,知道遇到NULL为止。

char **do_parse(char *command)
{
    int argc = 0;
    static char *argv[32];
    char *ptr = command;
    while (*ptr != '\0')
    {
        if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格
        {
            argv[argc++] = ptr;
            while ((!isspace(*ptr)) && (*ptr) != '\0')//#include  <ctype.h>   isspace检测是否遇到空格
            {
                ptr++;              //如果不是空格就一直读取命令,直到遇到空格
            }
        }
        else
        {
            while (isspace(*ptr))//如果命令前几个是空格就消除空格
            {
                //*ptr = '\0';//这句就不用加了
                ptr++;
            }
        }
    }
    argv[argc] = NULL;
    return argv;
}

解析完之后返回的是命令行参数数组指针

开始创建子进程并且用execvp替换子进程。

int do_exec(char *command)//进程替换函数=》用的就是exec
{
    char **argv = {NULL};
    int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。
    if (pid == 0)
    {
        argv = do_parse(command);
        if (argv[0] == NULL)
        {
            exit(-1);
        }
        execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)
    }                          // 可以把exec当作call(goto)函数,exit当作return函数。
    else
    {
        waitpid(pid, NULL, 0);
    }
    return 0;
}

这样就可以在子进程实现命令行进程了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include  <ctype.h>

#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{
    memset(command, 0x00, MAX_CMD);
    printf("[Tomshell$]#");
    fflush(stdout);
    if (scanf("%[^\n]%*c", command) == 0)
    {
        getchar();
        return -1;
    }
    return 0;
}
char **do_parse(char *command)
{
    int argc = 0;
    static char *argv[32];
    char *ptr = command;
    while (*ptr != '\0')
    {
        if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格
        {
            argv[argc++] = ptr;
            while ((!isspace(*ptr)) && (*ptr) != '\0')//#include  <ctype.h>   isspace检测是否遇到空格
            {
                ptr++;              //如果不是空格就一直读取命令,直到遇到空格
            }
        }
        else
        {
            while (isspace(*ptr))//如果命令前几个是空格就消除空格
            {
                //*ptr = '\0';//这句就不用加了
                ptr++;
            }
        }
    }
    argv[argc] = NULL;
    return argv;
}
int do_exec(char *command)//进程替换函数=》用的就是exec
{
    char **argv = {NULL};
    int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。
    if (pid == 0)
    {
        argv = do_parse(command);
        if (argv[0] == NULL)
        {
            exit(-1);
        }
        execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)
    }                          // 可以把exec当作call(goto)函数,exit当作return函数。
    else
    {
        waitpid(pid, NULL, 0);
    }
    return 0;
}
int main(int argc, char *argv[])
{
    while (1) // shell主循环
    {
        if (myshell_face() < 0)
            continue;
        do_exec(command);
    }
    return 0;
}

最终成果:
Linux一学就会——编写自己的shell文章来源地址https://www.toymoban.com/news/detail-435248.html

到了这里,关于Linux一学就会——编写自己的shell的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • macOS安装Python&&Pycharm详解!保姆级教程,一学就会!

    有需要打包好的PycharmPython激活码和安装包也可直接扫下方 首先登陆python官网:www.python.org/ 点击Download按钮,选择自己电脑的系统,有些时候网页可以自动识别 下载最新的python 版本号是随着版本发展不断演进,上图中显示3.12.2。但都是python3 等待下载完成,打开文件,一直点

    2024年04月25日
    浏览(43)
  • 【一学就会】Facebook脸书视频下载一秒保存手机

    Facebook作为一家全球知名的社交媒体平台和在线社交网络服务公司,提供了一个在线平台,使用户分享照片、视频、状态更新和链接等内容,然而,令人遗憾的是,Facebook没有为用户提供直接将照片和视频保存到本地的功能,因此,无法保存自己喜欢的视频图片。 今天在这里

    2024年02月04日
    浏览(162)
  • postman导入请求到jmeter进行简单压测,开发同学一学就会

    这个事情也是最近做的,因为线上nginx被我换成了openresty,然后接入层服务也做了较大改动,虽然我们这个app(内部办公类)并发不算高,但好歹还是压测一下,上线时心里也稳一点。 于是用jmeter简单压测下看看,这里记录一下。 这次也就找了几个接口来压:登录接口、登录

    2024年04月25日
    浏览(37)
  • 详解二分算法(一学就懂,一写就会)

    小明:\\\"小张,我问你一个问题:在1,3,5,6,7,9这些数中5在那个位置?\\\" 小张:\\\"这还不简单,5在第三个位置!我是按顺序来找的:1,3,5。\\\" 小明:\\\"那我告诉你1~100这些数,让你找其中100这个数,你也从1~100来一个一个数吗?\\\" 小张:\\\"那我会从100~1来数。\\\" 小明:\\\"那如果你是一个机器

    2024年02月16日
    浏览(43)
  • RocketMQ-dockefile制作镜像过程(一学就废)

    下载rockermq包: https://rocketmq.apache.org/zh/download/ source开源和binary二进制安装 开始编写rmqnamesrv的dockerfile 开始编写rmqbroker的dockerfile 生成镜像 查看我实验生成的images 命令启动rockermq服务 这里NAMESRV_ADDR=1.1.1.1:9876是需要指定你安装的rmqnamesrv的ip+端口,-v挂载的日志以及对于的配置

    2024年02月11日
    浏览(52)
  • msf--Linux反弹shell--一看就会的实验

    环境如下 vps :Linux ubuntu 4.15.0-180-generic (已经打开8989端口) win:Windows Feature Experience Pack 120.2212.4180.0(可以随意) 靶机:Linux ubuntu 4.18.0-25-generic 过程如下 1.在vps里面生成木马  msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=xx.xx.xx.xx LPORT=8989 -f elf asd  2.将木马下载到本机桌面 sz a

    2023年04月13日
    浏览(53)
  • Linux shell脚本编写

    一、常用shell脚本指令 echo: 输出指定的文本或变量值到标准输出。 read: 从标准输入读取用户输入,并将其保存到指定的变量中。 if: 执行条件语句,如果满足指定条件则执行特定操作,否则执行其他操作。 for: 循环执行特定操作,每次迭代更新变量值。 while: 循环执行

    2024年02月16日
    浏览(51)
  • Linux编写简易shell

    思路:​ ​ ​ 所以要写一个shell,需要循环以下过程:​ 获取命令行 解析命令行 建立一个子进程(fork) 替换子进程(execvp) 父进程等待子进程退出(wait) 实现代码:​         以上就是本文的全部内容,如果对你有帮助,欢迎点赞收藏转发评论! 

    2024年01月20日
    浏览(35)
  • Linux自定义shell编写

    经过了创建进程,终止进程,进程等待和进程程序替换之后, 我们就可以借助这些知识实现一个简单的shell命令行解释器了 温馨提示: 建议大家自己写一遍,这些代码分块之后每一个函数都很简单, 不过实现过程中可能会有各种各样非常细枝末节的地方被我们所忽视 因此可能会发生

    2024年02月04日
    浏览(41)
  • 【Linux】编写一个 shell 脚本&执行

    在Linux中编写和执行脚本相对简单。下面是一个基本的步骤指南,帮助你创建一个简单的bash脚本并运行它: 1. 创建脚本文件 首先,你需要使用文本编辑器创建一个新的文件。这个文件通常会有 .sh 的扩展名,以表明它是一个shell脚本。例如,你可以创建一个名为 myscript.sh 的文

    2024年04月26日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包