shell实际上是一个命令的解释程序,是一个用户与系统内核沟通的桥梁。那我们能不能通过代码自己实现一个shell呢?当然可以,看完这篇博文,我相信你会受益匪浅!
通常情况下,我们linux系统默认的shell是bash
,下面是bash
的工作流程:
总结有以下四步骤:
- 等待用户标准输入
- 对输入的信息进行读取和解析,得到命令名称及命令运行所需要的参数
- 创建子进程,在子进程中进行程序替换,让子进程去运行读取解析后的指定命令
- 父进程等待子进程的退出
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;
}
运行结果:
我们已经获取到了用户输入的命令参数,并且存到了字符数组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中
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;
}
运行测试:
如果觉得这篇博客对你有用记得一键三连哦!!!