简单模拟shell解释器
💦 简单模拟shell解释器
子进程执行新程序的需求 ❓
在之前,我们举过 1 个例子:你是村长家的儿子,是程序员,你不擅长和女生打交道,所以你去通过王婆去找如花表达你的爱意,村里人都知道如花已经心有所属了,而你又是村长家的儿子。王婆心想,这趟浑水我可不不趟,万一搞砸了,可能会影响到自己以后的职业发展,但又碍于你是村长家的儿子,不敢得罪。所以,机智的王婆说:呀!最近的活太多了,这样吧,我给你找我们公司的销冠(其实是实习生,比较好欺负),你自己跟销冠对接。就算谈不成,王婆也可以周旋(再给你找业务好的实习生)。王婆会根据工作的难易程度,简单的自己做,复杂的交给其它人。
这里你是用户;王婆是命令行解释器中的 bash;销冠(实习生)是子进程;如花是操作系统;销冠(实习生)谈砸了,不影响王婆就如子进程崩了不会影响父进程。
✔ 测试用例一:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define NUM 128
#define SIZE 32
char command_line[NUM];
char* command_parse[SIZE];
int main()
{
while(1)
{
//1.获取命令
memset(command_line, '\0', sizeof(command_line));
printf("[DanceBit@myhost myshell]$ ");
fflush(stdout);
if(fgets(command_line, NUM - 1, stdin))
{
command_line[strlen(command_line) - 1] = '\0';//\n = '\0'
//2.加工命令
int index = 0;
command_parse[index] = strtok(command_line, " ");//strtok以空格分割"ls -a -l"于指针数组,strtok可以适用于"ls -a -l"。
while(1)
{
index++;
command_parse[index] = strtok(NULL, " ");
if(command_parse[index] == NULL)
{
break;
}
}
//3.执行非内置命令
if(fork() == 0)
{
//child
execvp(command_parse[0], command_parse);//我们选择v、p。
exit(1);//父进程拿到1,说明execvp替换失败。
}
//father
int status = 0;
pid_t ret = waitpid(-1, &status, 0);
if(ret > 0 && WIFEXITED(status))
{
printf("Exit Code: %d\n", WEXITSTATUS(status));
}
}
}
return 0;
}
💨运行结果:
可以看到我们自己模拟的 shell 可以支持大部分命令,但有部分命令是不支持的,如 ll、>、| 。不支持的原因也很好理解,重定向和管道是需要我们理解了它的原理,然后才能实现的,后面我们再对 myshell 进行完善。
myshell 和 mini_shell 中使用的 echo 是同一个 echo 吗 ❓
不一定,如果感觉有些抽象的话,可以这么理解:有些命令实际让子进程去运行,子进程是不影响父进程的,此时有可能就会出现一些奇怪的现象,如。
为什么 ???
-
ps ajx | head -1 && ps ajx | grep mini_shell
查看 mini_shell 进程信息。 -
ls /proc/20339 -al
查看 mini_shell 20339 进程相关资源。其中 cwd 是当前所处的目录,exe 是执行程序的名字。我们发现在 myshell 中
cd ../../../
执行是成功的,但路径并没有任何变化。这里的 cd 更改的是父进程所在路径还是子进程所在路径 ??
我们需要明确一件事,在 cd.. 时,是期望更改子进程还是父进程的路径呢。子进程是目标程序,父进程是 shell。实际上我们想改的并不是子进程,因为子进程的路径一改,子进程就退出了,改就没有意义了。所以我们要改的是父进程的路径,换言之,你要改父进程的路径的前提是不能创建子进程执行 cd。父进程也不能执行 cd,因为父进程一旦替换就会把父进程的代码替换成 cd 的代码,父进程不仅越俎代庖,自己的本职工作也没做好。
其实虽然 cd 像命令,但实际上在 shell 中就压根不能使用程序替换执行,它使用系统接口来完成 “ 命令的执行 ”,这个接口是 chdir。
#include<unistd.h> int chdir(const char* path);
✔ 测试用例二:
支持 cd。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define NUM 128
#define SIZE 32
char command_line[NUM];
char* command_parse[SIZE];
int main()
{
while(1)
{
//1.获取命令
memset(command_line, '\0', sizeof(command_line));
printf("[DanceBit@myhost myshell]$ ");
fflush(stdout);
if(fgets(command_line, NUM - 1, stdin))
{
command_line[strlen(command_line) - 1] = '\0';//\n = '\0'
//2.加工命令
int index = 0;
command_parse[index] = strtok(command_line, " ");//strtok以空格分割"ls -a -l"于指针数组,strtok可以适用于"ls -a -l"。
while(1)
{
index++;
command_parse[index] = strtok(NULL, " ");
if(command_parse[index] == NULL)
{
break;
}
}
//3.执行第三方命令(内置命令)
if(strcmp(command_parse[0], "cd") == 0 && chdir(command_parse[1]) == 0)
{
continue;
}
//4.执行非内置命令
if(fork() == 0)
{
//child
execvp(command_parse[0], command_parse);//我们选择v、p。
exit(1);//父进程拿到1,说明execvp替换失败。
}
//father
int status = 0;
pid_t ret = waitpid(-1, &status, 0);
if(ret > 0 && WIFEXITED(status))
{
printf("Exit Code: %d\n", WEXITSTATUS(status));
}
}
}
return 0;
}
💨运行结果:
可以看到这样的 cd 命令并没有创建子进程去执行,本质 cd 是内置命令,它是 shell 内的一个函数调用。所以这里简单的工作,shell 自己做,复杂的就交给子进程做。
- 点赞
- 收藏
- 关注作者
评论(0)