綜合上篇博文的知識,我們可以實作一個簡易的shell -->/*上篇文章連結*/
用下圖的時間軸來表示時間的發生次序。其中時間從左到右。shell由辨別符為sh的方塊代表,它随着時間的流逝從左到右移動。shell從使用者讀入字元串"ls" 。shell建議一個新的子程序,然後在子程序中運作ls程式并等待子程序結束。其中這部分包含了程序的建立,程序程式替換,程序等待部分知識,在上篇博文都有介紹。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yN4UzNyQmZjF2N1YGO5MWNzYzXzMzNwETM5IzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
然後shell讀取新的一行輸入,建立一個新的程序,在這個程序中運作程式,并等待這個程序結束。
思路和步驟
是以要寫一個shell,需要循環一下過程:
- 擷取指令行
- 解析指令行(使用strtok分割字元串)
- 建立子程序(fork)
- 替換子程序(execvp)
- 父程序等待子程序退出(waitpid)
代碼實作
根據這些思路和步驟,我們來看一下實作的代碼:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#define SEP " "
#define SIZE 128
#define NUM 1024
char command_line[NUM];
char* command_args[SIZE];
char env_buffer[NUM];
extern char** environ;
int ChangeDir(const char* new_path)
{
chdir(new_path);
return 0;//調用成功
}
void PutEnvInMyShell(char* new_env)
{
putenv(new_env);
}
int main()
{
//一個shell 本質上就是一個死循環
while(1)
{
//不關心擷取這些屬性的接口
//1.顯示提示符
printf("[張三@我的主機名 目前目錄]# ");
fflush(stdout);
//2.擷取使用者輸入
memset(command_line,'\0',sizeof(command_line)*sizeof(char));
fgets(command_line,NUM,stdin);//擷取 輸入 stdin
command_line[strlen(command_line) - 1] = '\0';//清空\n
//printf("%s\n",command_line);
//3."ls -l -a -i" --> "ls","-l","-a","-i" 字元串切分
command_args[0] = strtok(command_line, SEP);
int index = 1;
//給ls添加顔色
if(strcmp(command_args[0]/*程式名*/,"ls") == 0)
command_args[index++] = (char*)"--color=auto";
//strtok 截取成功 傳回字元串起始位址
//截取失敗 傳回NULL
while(command_args[index++] = strtok(NULL,SEP));
// for debug
//for(int i = 0;i<index;++i)
//{
// printf("%d:%s\n",i,command_args[i]);
//}
//4.TODO
//如果直接exec*執行cd,最多隻是讓子程序進行路徑切換,
//子程序是一運作就完畢的程序!我們在shell中,更希望
//父程序的路徑切換
//如果有些行為必須讓父程序shell執行,不想讓子程序
//這種情況下不能建立子程序,隻能讓父程序自己實作對應的代碼
//這部分由父shell自己執行的指令稱之為内建指令
if(strcmp(command_args[0],"cd") == 0 && command_args[1] != NULL)
{
ChangeDir(command_args[1]);
continue;
}
if(strcmp(command_args[0],"export") == 0 && command_args[1] != NULL)
{
strcpy(env_buffer,command_args[1]);
PutEnvInMyShell(env_buffer);
continue;
}
//5.建立子程序
pid_t id = fork();
if(id == 0)
{
//child
//6.程式替換
execvp(command_args[0],/*裡面儲存的就是執行的名字*/command_args);
exit(1);//執行到這裡一定失敗了
}
int status = 0;
pid_t ret = waitpid(id,&status,0);
if(ret>0)
{
printf("等待子程序成功: sig:%d, code:%d\n",status&0x7F,(status>>8)&0xFF);
}
}
return 0;
}