當我們在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,不支持管道和重定向。