linux shell详解,以及基于进程对shell的简单模拟实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40921797/article/details/82150328

什么是shell

shell 翻译成中文就是壳,外壳的意思。它是一个命令行解释器,负责接收用户命令,然后调用相应的应用程序。它将用户和系统内核隔离开来,做他们两者间交互的桥梁。
这就是为什么我们发送一系列命令,系统都知道对应的操作是什么。
shell的功能
1.用户发送命令给shell:将使用者的命令翻译解释给中央内核处理
2.内核发送反馈结果给shell:将核心处理结果翻译呈现给使用者
举个例子:古代为了保证闺中少女的名节,保护少女们收到伤害,和男子决定婚嫁沟通是由媒婆来完成之间交流等活动的。那我们这个shell是不是和媒婆很像呢。
shell这个命令行解释器是一个统称概念,具体到Linux中是有/bin/bash//根目录下bin目录中的bash完成的

模拟shell过程

我们先观察一下Linux下的shell是工作呈现的结果是什么吧。
以ls命令为例子:
这里写图片描述
我们的显示格式有命令提示符:[用户@主机名 目录] 以及我们的命令组成。
我们暂且忽略命令提示符,先完成命令部分。

1.首先利用vim一个文件
具体操作见博客linux vi/vim详解

   vim myshell//进入
   //从普通模式进入可写模式,然后开始写代码 

2.从输入终端获取字符串,因为scanf(%s)不能接收空格,所以采用另一种格式接收整一行数据
2.分解字符串,按照空格位置拆分到一个一个的命令参数,分别存入数组的各个单元中去
3.创建一个子进程,在子进程中调用exec进行进程替换,让子进程去运行命令
关于exec函数,父子进程以及进程替换详细见博客linux 如何创建子进程fork和fork,vfork,exec函数组详细介绍
4.父进程阻塞式等待子进程运行完毕获取退出状态码,
详细参见博客,为什么父进程要等待。linux 进程等待和退出码
5.循环接收命名,重复操作,直至接收到中断信息。
具体代码如下:


#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

int argc;//获取拆分后字符串个数
char* argv[32];//存储拆分后的字符串

//按照空格位置拆分大的字符串为小的字符串在逐一存入数组
int Spl_str(char *buff)
{
    if(buff == NULL)
        return -1;
    char *ptr = buff;//利用ptr遍历buff字符串
    char *pro_ptr = ptr;//pro_ptr记录每单个字符串开始字节位置
    argc = 0;
    while((*ptr) != '\0'&&*(ptr)!='\n')
    {
    //如果ptr遍历中遇到\n换行,或者\0字符串结束标志则遍历完成
        if(*ptr == ' '&& *(ptr+1) != ' ')
        {
        //这里是避免出现空格后还是空格,及多个空格连续出现的情况。
        //因为没有命令中含有空格,所以如果空格后还是空格,则继续找,直到找到非空格前最后一个空格才进行操作
            *ptr = '\0';//这个空格同时也是上一个字符串的结束标志。
            argv[argc] = pro_ptr;
            //pro_ptr存储的是单个小字符串的开始位置,这句话则是将上一个刚刚确定结尾位置的字符串存入到argv数组中。
            pro_ptr = ptr + 1;//记录下一个字符串的开始字符位置
            argc++;//因为argc位置已经存了,则++,
        }
        ptr++;//ptr++继续向后走,遍历查找
    }
    if(*(pro_ptr)!='\0'&&*(pro_ptr)!='\n')
        argv[argc++] = pro_ptr;
        //这句话的意思,因为最后一位的标志不是空格,位置字符串结束。如果这时候pro_ptr就是小字符串的开始位置不是最后无用字符,而依然是字符命令,则接收。
    argv[argc] = NULL;
    //argv 数组在execvp传参要求标志末尾用一个NULL
    return 0;
}

//先通过fork创建子进程,在通过父子进程不同的返回值,让子进程实现进程替换,
int exec_cmd()
{
    int pid = -1;
    pid = fork();
    if(pid < 0)
    {
        return -1;
        //创建失败
    }
    else if(pid == 0)
    {
    //pid==0证明在子进程
        exit(execvp(argv[0],argv));
        //调用只前创建的argv,argc,传参具体格式见博客
    }
    int status;
    wait(&status);
    //通过获取退出码,查看出现的错误,并且转换成为退出码所对应的错误信息并且打印。
    if(WIFEXITED(status))
    {
        //利用系统提供的宏子进程的退出码,也可以用&操作拿到第7位,并转换为文本信息打印,可以不打印,不显示。
        printf("%s\n",strerror(WEXITSTATUS(status)));
    }
    return 0;
}

//主函数:
int main()
{
    while(1)
    {
        printf("[myshell]$ ");
        fflush(stdout);//清空缓冲区,因为没有换行,需要人为清空缓冲区
        {
            char buff[1024]={0};
            //scanf("%s",buff);
            scanf("%32[^\n]",buff);
            scanf("%*c");
            //%[^\n]获取数据直到遇到\n为止
            //%*c 清空缓冲区,接收\n并丢弃
            Spl_str(buff);
        }
        exec_cmd();
    }
    return 0;
}

运行显示结果如下:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40921797/article/details/82150328