shell实际上是一个命令的解释程序,是一个用户与系统内核沟通的桥梁。那我们能不能通过代码自己实现一个shell呢?当然可以,看完这篇博文,我相信你会受益匪浅!

通常情况下,我们linux系统默认的shell是bash,下面是bash的工作流程:

linux编写dockerfile Linux编写一个简单的shell_#include


总结有以下四步骤:

  1. 等待用户标准输入
  2. 对输入的信息进行读取和解析,得到命令名称及命令运行所需要的参数
  3. 创建子进程,在子进程中进行程序替换,让子进程去运行读取解析后的指定命令
  4. 父进程等待子进程的退出

1、等待用户标准输入

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

 int main()
 {
     //1、等待标准输入
     char buf[1024] = { 0 };
     fgets(buf, 1023, stdin);//将输入的数据放在buf中
     buf[strlen(buf) - 1] = '\0';//将最后一个换行取消
     printf("buf:[%s]\n", buf);
     //2、对输入数据进行读取解析
     //3、创建子进程,在子进程中进行程序替换
     //4、父进程进程等待
     return 0;
 }

运行结果:

linux编写dockerfile Linux编写一个简单的shell_子进程_02


我们已经获取到了用户输入的命令参数,并且存到了字符数组buf中

2、读取解析buf中的命令

//2、对输入数据进行读取解析
    char *argv[32] = {NULL};
    int argc = 0;
    char *ptr = buf;
    // [   ls     -a    -l   ]
    while (*ptr != '\0')
    {          
     	if (*ptr != ' ')
         {
             argv[argc++] = ptr;
             while (*ptr != ' ' && *ptr != '\0')
             {
                 ptr++;
             }
             *ptr = '\0';
         }
          ptr++;
     }
     argv[argc] = NULL;//最后一个参数位置的下一个位置置NULL

从buf存到指针数组argv中

linux编写dockerfile Linux编写一个简单的shell_标准输入_03

3、创建子进程,在子进程中进行程序替换

//3、创建子进程,在子进程中进行程序替换
     pid_t pid = fork();
     if (pid == 0)//子进程
     {
          execvp(argv[0],argv);//程序替换成功就不会执行下面代码,而去执行新的程序
          perror("execvp error");//程序替换失败时会打印上一次系统调用接口使用的错误原因
          exit(0);
     }

4、父进程进行进程等待

wait(NULL);

完整代码:

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

int main()
{
	while (1)
	{
		printf("[WhiteShirtI@shell workspace]$ ");
		fflush(stdout);//刷新标准输出缓冲区
		
		//1、等待标准输入
		char buf[1023] = {0};
		fgets(buf, 1023, stdin); //将输入存放到buf中
		buf[strlen(buf) - 1] = '\0';//将最后一个换行取消
		
		//2、对输入数据进行读取和解析
		char *argv[32] = { NULL };
		int argc = 0;
		char *ptr = buf;
		//遍历命令和参数,不为空就继续遍历
		while (*ptr != '\0')
		{
			//遇到命令或者参数
			if (*ptr != ' ')
			{
				//让agv第argc的位置放ptr这个指针
				argv[argc++] = ptr;
				while (*ptr != ' ' && *ptr != '\0')
				{
					ptr++;
				}
				//一个命令或参数的结束标志
				*ptr = '\0';
			}
			ptr++;
		}
		argv[argc] = NULL;//最后一个参数位置的下一个位置置为NULL
		//3、创建子进程,在子进程中进行程序替换
		pid_t pid = fork();
		if (pid == 0) //子进程
		{
			//execvp(命令名称, 命令参数)
			execvp(argv[0], argv);//程序替换成功不会执行下面代码
			perror("execvp error");//打印上一次系统调用接口使用的错误原因
			exit(0);
		}
		//4、父进程进行进程等待
		wait(NULL);
	}
	return 0;
}

运行测试:

linux编写dockerfile Linux编写一个简单的shell_标准输入_04


linux编写dockerfile Linux编写一个简单的shell_linux编写dockerfile_05


如果觉得这篇博客对你有用记得一键三连哦!!!