Linux 用C語言實現簡單的shell(2)


不知不覺兩周沒有發文了,因為“一萬美金的福特獎學金答辯”,ACM比賽,網絡論文閱讀和網絡大作業一大堆事把時間沖散了,所以先寫一篇博文補上之前一坑。

之前發了一篇關於linux 用C語言實現簡單shell的博文,當時因為剛剛接觸linux,只是處理了:

1)外部命令

2)pwd,cd,exit內置命令

3)輸入輸出重定向

並且代碼相比較而言是一步一步添加的,代碼相對來講比較丑QAQ,所以在學完管道之后,相信不得不重新寫代碼才能實現了。

相比較之前的版本我對代碼進行了相關的修改:

1)對於shell指令采用結構體存儲,方便管道的切割與執行;

2)采用strtok_r對於指令進行了更加合理的切割;

3)增加了多管道處理;

4)處理了一些簡單的異常問題,包括文件路徑問題,空行問題,指令錯誤等。

 

評論中有朋友想讓我加入后台運行‘&’功能的,其實基本上沒什么區別,只是對於增加了‘&’后台運行的命令主進程不再需要等待子進程結束再操作了,我已經對我的代碼進行了一些修改並加上一定的注釋。

/*author:Samsons
  date:2015.4.28
  1)對於shell指令采用結構體存儲,方便管道的切割與執行;
  2)采用strtok_r對於指令進行了更加合理的切割;
  3)增加了多管道處理;
  4)處理了一些簡單的異常問題,包括文件路徑問題,空行問題,指令錯誤等。  
date:2015.4.30
增加了后台處理命令“&”的處理
*/ #include <stdio.h> #include <signal.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <sys/wait.h> #define MAX 100 #define LEN 100 //shell指令單個管道結構體 struct cmd_list{ int argc; //單個管道參數個數 char *argv[MAX]; }; struct cmd_list *cmdv[MAX]; //shell指令 int num;//shell管道個數 int flagdo;//是否為后台處理命令標記 //執行外部命令 void execute(char *argv[]) { int error; error=execvp(argv[0],argv); if (error==-1) printf("failed!\n"); exit(1); } //切分單個管道 void split_cmd(char *line) { struct cmd_list * cmd = (struct cmd_list *)malloc(sizeof(struct cmd_list)); cmdv[num++] = cmd; cmd->argc = 0; char *save; char *arg = strtok_r(line, " \t", &save);//切分空格 while (arg) { cmd->argv[cmd->argc] = arg; arg = strtok_r(NULL, " \t", &save); cmd->argc++; } cmd->argv[cmd->argc] = NULL; } //切分管道 void split_pipe(char *line) { char *save; char * cmd = strtok_r(line, "|", &save); while (cmd) { split_cmd(cmd); cmd = strtok_r(NULL, "|", &save); } } //執行管道命令 void do_pipe(int index) { if (index == num - 1) execute(cmdv[index]->argv); int fd[2]; pipe(fd);//創建管道,0讀,1寫 if (fork() == 0) { dup2(fd[1], 1); close(fd[0]); close(fd[1]); execute(cmdv[index]->argv); } dup2(fd[0], 0); close(fd[0]); close(fd[1]); do_pipe(index + 1); } //執行內部指令 int inner(char *line) { char *save,*tmp[MAX]; char t[LEN],p[LEN]; strcpy(t,line); char *arg = strtok_r(line, " \t", &save);//切分空格 int i=0; while (arg) { tmp[i] = arg; i++;//記錄命令個數 arg = strtok_r(NULL, " \t", &save); } tmp[i] = NULL; if (strcmp(tmp[i-1],"&")==0)//判斷是否為后台處理命令 { flagdo=1; i--; } if (strcmp(tmp[0],"exit")==0)//exit { exit(0); return 1; } else if (strcmp(tmp[0],"pwd")==0)//pwd { char buf[LEN]; getcwd(buf,sizeof(buf));//得到當前路徑 printf("Current dir is:%s\n",buf); return 1; } else if (strcmp(tmp[0],"cd")==0)//cd { char buf[LEN]; if (chdir(tmp[1])>=0) { getcwd(buf,sizeof(buf)); printf("Current dir is:%s\n",buf); } else { printf("Error path!\n"); } return 1; } else return 0; } //輸入重定向 void cat_in(char *q) { char t[30]; int fd; if (q[0]=='<') { strcpy(t,q+1); fd=open(t,O_RDONLY); cmdv[0]->argv[cmdv[0]->argc-1]=NULL; //默認重定向為參數的最后一個 cmdv[0]->argc--; if (fd==-1) { printf("file open failed\n"); return; } dup2(fd,0); close(fd); } } //輸出重定向 void cat_out(char *q) { char t[30]; int fd; if (q[0]=='>') { strcpy(t,q+1); cmdv[num-1]->argv[cmdv[num-1]->argc-1]=NULL; cmdv[num-1]->argc--; fd=open(t,O_CREAT|O_RDWR,0666); //0666為權限 if (fd==-1) { printf("file open failed\n"); return; } dup2(fd,1); close(fd); } } int main() { int i,pid; char buf[LEN],p[LEN]; while (1) { fgets(buf,LEN,stdin);//讀入shell指令 if (buf[0]=='\n') continue; buf[strlen(buf)-1]='\0'; strcpy(p,buf); int inner_flag; inner_flag=inner(buf);//內置指令執行 if (inner_flag==0) { pid=fork();//建立新的進程 if (pid==0) { split_pipe(p);//管道的切割 //如果是后台處理命令將&符號刪除 if (strcmp(cmdv[num-1]->argv[cmdv[num-1]->argc-1],"&")==0) { cmdv[num-1]->argc--; cmdv[num-1]->argv[cmdv[num-1]->argc]=NULL; } //默認輸入輸出重定向都是最后一個參數,輸入時第一個管道,輸出是最后一個管道 if (cmdv[0]->argv[cmdv[0]->argc-1]!=NULL) { char q[LEN]; strcpy(q,cmdv[0]->argv[cmdv[0]->argc-1]); cat_in(q);//輸入重定向 } if (cmdv[num-1]->argv[cmdv[num-1]->argc-1]!=NULL) { char q[LEN]; strcpy(q,cmdv[num-1]->argv[cmdv[num-1]->argc-1]); cat_out(q);//輸出重定向 } do_pipe(0);//執行管道 exit(0); } if (flagdo==0)//非后台處理命令主進程才需等待子進程處理 waitpid(pid,NULL,0); } } return 0; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM