当我们在Linux下打开一个终端时,可以通过在上面输入一些命令,来实现与终端的互动。例如:
可以看到,当我们输入一条命令后,终端会等待下一条命令的输入。这就类似于一个死循环,不停的输入命令。
在编写shell解释器之前,我们先看一下shell脚本执行过程:
fork创建子进程后执行的是和父进程相同的程序(但有可能是不同的代码分支),子进程调用exec函数以执行另一个程序。此时,该进程的用户空间代码和数据完全被新进程替换,从新进程的启动开始执行。这里需要注意的是:调用exec函数并不创建新进程,所以调用exec前后该进程的id并未改变。
因此,我们可以通过进程创建和进程替换来实现一个简单的shell。
需要循环以下过程:
· 获取命令行
· 解析命令行
· 创建子进程(fork)
· 替换子进程(execvp)
· 父进程等待子进程退出(wait)
具体实现:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
int main()
{
char cmd[128];
char *my_arg[32];
while(1)
{
printf("[myshell#] ");
fgets(cmd,sizeof(cmd),stdin);//获取命令行
pid_t id = fork();//创建子进程
if(id == 0)//子进程
{
cmd[strlen(cmd)-1] = 0;
char *p = cmd;
int i = 1;
my_arg[0] = cmd;
while(*p)
{
if(isspace(*p))
{
*p = 0;
p++;
my_arg[i++] = p;
}
else
{
p++;
}
}
my_arg[i] = NULL;
execvp(my_arg[0],my_arg);//进程替换
}
else
{ //父进程
int status = 0;
pid_t ret = waitpid(id,&status,0);//父进程等待子进程退出
if(ret>0)
{
printf("sig: %d,exit code: %d\n",status&0x7F,(status>>8)&0xFF);
}
else
{
printf("waitpid running error\n");
}
}
}
return 0;
}
我们在这里只是实现了一个简单的shell,不支持管道和重定向。