模拟实现命令解释器shell

shell的字面翻译是壳,所以在Linux中有时也被称为外壳程序,主要功能是用来进行用户与内核之间的交互,他接收用户的命令送给内核去执行。
今天来模拟实现一下Linux下的命令行解释器shell的部分功能,在此运用到主要的知识点有创建子进程,进程等待和进程替换,还有main函数的命令行参数等。
完整代码大家可以参考这个:模拟实现Shell完整代码

既然是命令解释器,那么当然要给我们一个输入命令的地方,这个地方要满足我们日常敲出的命令的长度,所以我们来申请一块缓冲区。有了缓冲区,还要考虑命令行解释器不会只输一次命令,多次输入自然是需要一个大循环来进行控制。并且在真正的shell下,按下回车才会执行,否则不执行。大体框架大概就是这个样子,我们来看看具体的代码:

#define IN 1
#define OUT 0

int main()
{
    char buf[100];
    while(1)
    {
        printf("冰可乐>");
        memset(buf, 0x00, sizeof(buf));
        scanf("%[^\n]%*c",buf);
        if( strncmp(buf, "exit", 4) == 0)
            exit(0);
        do_prase(buf);
    }
    return 0;
}

把所有输入的字符读入缓冲区之后,我们需要一个能够区分出输入几条命令选项的函数,这也就是我们的第二块功能函数了。在上面我们定义了两个宏,用这两个宏和isspace函数我们可以做到分开每一条命令。isspace函数的功能是读到空格类字符(空格,制表符(Tab),回车,换行符等)返回非零值,读到非空格类返回零值

void do_prase(char *buf)
{
    char* argv[8];
    int argc = 0;
    int flag = OUT;
    int i = 0;
    for(i = 0; buf[i] !='\0'; i++)
    {
        if(!isspace(buf[i]) && flag == OUT){
            flag = IN;
            argv[argc++] = &buf[i];
        }else if(isspace(buf[i])){
            flag = OUT;
            buf[i] = '\0';
        }
    }   
    argv[argc] = NULL;
    do_exec(argc, argv);
}

最后一个功能模块就是我们的创建子进程和进程替换了,因为shell在执行本命令后需要继续在界面检测等待下一条命令,所以shell本身并不执行命令的功能,而是创建子进程去执行,创建出子进程后,直接执行execvp函数进行替换。而exec一族的函数,可以将当前进程中的数据,代码,堆栈全部替换,但是进程ID不变。exec一族一般情况下所传参数是一个可执行文件的相关参数,也可以是Linux下的任何脚本文件,而此处 我们就用了第二种功能,执行Linux命令池中本身就有的脚本文件。

void do_exec(int argc, char* argv[])
{
    pid_t id;
    if( (id=fork()) == 0)
    {
        execvp(argv[0],argv);
        printf("command %s not found\n",argv[0]);
        exit(1);
    }
    int s;
    waitpid(id, &s, 0);
}

猜你喜欢

转载自blog.csdn.net/zym1348010959/article/details/80367814