1. 程式人生 > >實現一個簡單shell(支援重定向)

實現一個簡單shell(支援重定向)

5.16更新:支援重定向

我們知道對於Linux,shell就是個命令列直譯器,當我們輸入相關的命令,會去執行相關的操作。

比如當我們輸入ls -a -l命令,shell就會打印出當前目錄的內容,這是如何實現的?shell自己就是一個程序,當我們輸入ls之類的命令,它會通過fork,exec函式去建立一個新的子程序去執行相關操作。因此我們也可以利用這個來實現一個簡單的shell。

當然,這個shell足夠簡單,並不能像Linux內建的shell那麼強大支援各種操作,報錯,等等。

先來看看實現後的效果圖:

這裡寫圖片描述

紅色圈起來的是系統本身的shell,而藍色則是我自己模擬實現的一個簡單shell,可以看到當我輸入ls命令,它也完成列印操作,輸入./hello也成功執行了hello程式輸入hello。當然,類似管道等操作並沒有實現,具體可以參考

shell去進一步瞭解。

下面我們來聊聊如何實現簡單shell:

  1. 首先是提示符,[email protected]以及當前路徑,這個可以呼叫系統api直接獲取列印,這裡為了簡便,我直接用了printf實現。
  2. 解析命令,對於ls -a -l這種命令解析,我們只需要將存入到指標陣列中,char* shell _ argv[32],ls存到shell _ argv[0] ,-a存到shell _ argv[1],-l存入到shell _ argv[2] 。。。最後一個設定為NULL
  3. 利用exev呼叫新的程式

程式碼如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<sys/wait.h> #include<string.h> #include<ctype.h> #include<fcntl.h> int main() { for (;;) { printf("[email protected]:"); fflush(stdout); //解析輸入到shell上的字串 ls -a -l
char buffer[1024]; int read_size = read(1, buffer, sizeof(buffer)); if (read_size > 0) { buffer[read_size - 1] = 0; } char* shell_argv[32] = {NULL}; int shell_index = 0; char* start = buffer; while (*start != '\0') { while (*start != '\0' && isspace(*start)) { *start = '\0'; start++; } shell_argv[shell_index++] = start; while (*start != '\0' && !isspace(*start)) { start++; } } //建立子程序來exec pid_t pid = vfork(); if (pid < 0) { printf("vfork failure\n"); exit(1); } else if (pid == 0) { //考慮重定向 > //在字串陣列中找重定向標誌 int i = 0; int flag = 0; for (; shell_argv[i] != NULL; ++i ) { if (strcmp(">", shell_argv[i]) == 0) { flag = 1; break; } } int copyFd; shell_argv[i] = NULL; if (flag) { if (shell_argv[i+1] == NULL) { printf("command error\n"); exit(1); } close(1); int fd = open(shell_argv[i+1], O_WRONLY | O_CREAT, 0777); copyFd = dup2(1, fd); } execvp(shell_argv[0], shell_argv); if (flag) { close(1); dup2(copyFd, 1); } exit(1); } else //father process { int status = 0; int ret = waitpid(pid, &status, 0); if (ret == pid) { if (WIFEXITED(status)) { // printf("exitCode is %d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("signal is %d\n", WTERMSIG(status)); } } } } return 0; }

效果如下圖:

這裡寫圖片描述