?? my_shell.c
字號:
/************************************************* 目的: 1、應用UNIX的fork()等系統調用,編寫一個c程序具有以下功能: a) 實現Shell的基本功能,包括有:打印提示符; 接受和分析命令行(濾去無效的空格、tab符號以及換行符等); 執行命令(要有出錯處理;輸入exit或者bye退出);返回父進程; b) 處理后臺程序(不需要wait) c) 處理多行命令(分析命令行中的‘;’并處理之) d)應用 dup(), pipe()系統調用具有輸入輸出重定向以及管道功能; 文件名:my_shell.c 作者:鹿珂珂 2005011534 日期:2008年5月31日 ***************************************************/ /************頭文件************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> /**************全局變量定義***************/ const int BUFFERSIZE=80;//接收命令的最大字符數 char buffer[80];//緩存用戶輸入的命令 int is_back=0;//是否是后臺進程,默認不是后臺執行的程序 int status=0;//狀態變量 pid_t pid;//進程 /**************函數聲明***************/ char *get_command(int *input_len);//獲取輸入命令 void dispose_command(char *deal_in,int len);//解析處理命令 int redirect(char * in, int len);//實現輸入輸出重定向的函數 int piple(char * in, int li_inlen);//實現管道功能的函數 int is_fileexist(char * comm);//用來查找命令是否存在 void multi_command(char * m_in, int m_len);//處理用分號區分的多個命令 int main()//主函數:調用get_command()和dispose_command()實現shell的基本功能 { char * path;//當前路徑 char *user;//用戶名 char *input=NULL;//用戶輸入命令 int command_len=0;//輸入字符個數 while(1) { command_len=0; path = get_current_dir_name();//獲取當前目錄 user=getlogin();//獲取當前登錄用戶 printf("%s@%s-desktop:~%s$",user,user, path);//用戶輸入提示符:用戶名@用戶名-desktop:~當前目錄$ if(input) free(input);//釋放上一次輸入命令內存空間 input=get_command(&command_len);//獲取命令 if(input) { dispose_command(input,command_len);//處理執行命令 } } } char *get_command(int * input_len)//獲取用戶輸入命令 { char lc_char;//輸入字符 char *get_in; (*input_len)=0; /* 開始獲取輸入 */ lc_char = getchar(); while(lc_char != '\n' && (*input_len) < BUFFERSIZE) { buffer[(*input_len) ++] = lc_char; lc_char = getchar(); } /* 命令超長處理*/ if((*input_len) >= BUFFERSIZE) { printf("Your command too long ! Please reenter your command !\n"); (*input_len) = 0; /* Reset */ gets(buffer); strcpy(buffer,""); get_in=NULL; return NULL; } else buffer[(*input_len)] = '\0'; /*加上串結束符,形成字符串*/ if((*input_len)==0)return NULL;//處理直接敲入回車的情況 /* 將命令從緩存拷貝到in中*/ get_in = (char *)malloc(sizeof(char) * ((*input_len) + 1)); strcpy(get_in, buffer); strcpy(buffer,""); return get_in; } void dispose_command(char *deal_in,int len)//調用multi_command()、piple()、redirect()分別實現多個命令、管道、重定向 { char * arg[30];//存儲指令或參數 int i=0, j=0, k=0;//計數變量 pid_t pid;//進程 /* 獲取命令和參數并保存在arg中*/ for( i=0;i<=len;i++) { if(deal_in[i]==';') { multi_command(deal_in,len); return; } } for( i = 0, j=0,k=0; i<=len; i ++) { /*管道和重定向單獨處理*/ if(deal_in[i]=='<' || deal_in[i] == '>' || deal_in[i] == '|' || deal_in[i]==';') { if(deal_in[i] == '|') { piple(deal_in, len); return; } else if(deal_in[i]=='>' ||deal_in[i]=='<') { redirect(deal_in, len); return; } } /*處理空格,TAB和結束符。不用處理'\n',大家如果仔細分析前面的獲取輸入的程序的話,不難發現回車符并沒有寫入buffer*/ if(deal_in[i]== ' ' || deal_in[i] == '\t' || deal_in[i] == '\0') { if(j == 0) /*這個條件可以略去連在一起的多個空格或者TAB */ continue; else { buffer[j ++] = '\0'; //結束上一個字符串 arg[k] = (char *)malloc(sizeof(char) * j);//將第一個字符串拷貝至arg /* 將指令或參數從緩存拷貝到arg中*/ strcpy(arg[k], buffer); j = 0; /* 準備取下一個參數*/ k ++; } } else { /* 如果字符串最后是 '&',則置后臺運行標記為 1 */ if(deal_in[i]== '&' && deal_in[i + 1] == '\0') { is_back = 1;//是后臺進程 return;//后臺進程則返回,繼續接收指令 } buffer[j ++] = deal_in[i]; } } if(!strcmp(arg[0], "bye") || !strcmp(arg[0],"exit")) { //如果輸入 bye或者exit 則退出程序 printf("bye-bye\n"); exit(0); } /* 在使用exec執行命令的時候,最后的參數必須是NULL指針,所以將最后一個參數置成空值*/ arg[k] = (char *)malloc(sizeof(char)); arg[k ++] = (char *)0; /* 判斷指令arg[0]是否存在*/ if(is_fileexist(arg[0]) == -1) { printf("This is command is not founded ?!\n"); for(i = 0; i < k; i ++) free(arg[i]); return; } if((pid = fork()) == 0) /*子進程*/ execv(buffer, arg); else /*父進程*/ if(is_back == 0) /* 并非后臺執行指令*/ waitpid(pid, &status, 0); /* 釋放申請的空間*/ for(i = 0; i < k; i ++) free(arg[i]); } int redirect(char * in, int len)//實現重定向功能 { char * argv[30], *filename[2]; int fd_in, fd_out, is_in = -1, is_out = -1, num = 0; int is_back=0; int I,i, j, k=0;//計數變量 int status=0; /* 這里是重定向的命令解吸過程,其中filename用于存放重定向文件,is_in, is_out分別是輸入重定向標記和輸出重定向標記*/ for(I = 0, j = 0, k = 0; I <= len; I ++) { if(in[I] == ' ' || in[I] == '\t' || in [I] == '\0' || in[I] == '<' || in[I] == '>') { if(in[I] == '>' || in[I] == '<') { /* 重定向指令最多 '<', '>'各出現一次,因此上num最大為2,否則認為命令輸入錯誤*/ if(num < 3) { num ++; if(in[I] == '<') is_in = num - 1; else is_out = num - 1; /* 處理命令和重定向符號相連的情況,比如 ls > a*/ if(j > 0 &&num == 1) { buffer[j ++] = '\0'; argv[k] = (char *)malloc(sizeof(char) * j); strcpy(argv[k], buffer); k ++; j = 0; } } else { printf("The format is error !\n"); return - 1; } } if(j == 0) continue; else { buffer[j ++] = '\0'; /* 尚未遇到重定向符號,字符串時命令或參數*/ if(num == 0){ argv[k] = (char *)malloc(sizeof(char) * j); strcpy(argv[k], buffer); k ++; } /* 是重定向后符號的字符串,是文件名*/ else { filename[status] = (char *)malloc(sizeof(char) * j); strcpy(filename[status], buffer); } j = 0; /* Initiate */ continue; } } else{ if(in[I] == '&' && in[I + 1] == '\0') { is_back = 1; return; } buffer[j ++] = in[I]; } } argv[k] = (char *)malloc(sizeof(char)); argv[k ++] = (char *)0; if(is_fileexist(argv[0]) == -1) { printf("This command is not founded !\n"); for(I = 0; I < k; I ++) free(argv[I]); return 0; } if((pid = fork()) == 0) { /* 存在輸出重定向*/ if(is_out != -1) { if((fd_out = open(filename[is_out], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR )) == -1) { printf("Open out %s error \n", filename[is_out]); return -1; } } /* 存在輸入重定向*/ if(is_in != -1) { if((fd_in = open(filename[is_in], O_RDONLY, S_IRUSR|S_IWUSR)) == -1) { printf("Open in %s error \n", filename[is_in]); return -1; } } if(is_out != -1) { /* 使用dup2函數將標準輸出重定向到fd_out上,dup2(int oldfd, int newfd) 實現的是報oldfd所指的文件描述符復制到newfd。若newfd為已已打開的文件描述符, 則newfd所指的文件先會被關閉,dup2復制的文件描述符與原來的文件描述符共享各種文件狀態*/ if(dup2(fd_out, STDOUT_FILENO) == -1) { printf("Redirect Standard Out Error !\n"); exit(1); } } if(is_in != -1) { if(dup2(fd_in, STDIN_FILENO) == -1) { printf("Redirect Standard In error !\n"); exit(1); } } execv(buffer, argv); } else if(is_back == 0) /* Run on the TOP */ waitpid(pid, &status, 0); for(I = 0; I < k; I ++) free(argv[I]); if(is_in != -1) { free(filename[is_in]); close(fd_in); } if(is_out != -1){ free(filename[is_out]); close(fd_out); } return 0; } void multi_command(char * m_in, int m_len)//處理用分號區分的多個命令 { int pos; //記錄分號位置 int i=0,j=0; char *command=NULL; //拆分存儲命令 for(i=0;i<=m_len;i++) { if(m_in[i]==';') //處理分號前的第一個命令 { pos=i; command=(char *)malloc(sizeof(char) * (i+1)); for(j=0;j<i;j++) command[j]=m_in[j]; command[j]='\0'; dispose_command(command,i+1); //printf("%s %d %d\n",command,m_len,pos); free(command); //釋放動態開辟的存儲空間 break; } } if(m_in[pos+1]=='\0') //如果以分號結尾,則退出循環 return; if(pos>=m_len)return; //與上面一句相同作用 command=(char *)malloc(sizeof(char) * (m_len-pos)); //處理第一個分號以后的命令 for(i=pos+1,j=0;i<=m_len;i++) { command[j++]=m_in[i]; } dispose_command(command,m_len-pos); //遞歸調用處理剩余命令 free(command); } int piple(char * in, int li_inlen)//實現管道功能 { char * argv[2][30]; int count; is_back = 0; int li_comm = 0, fd[2], fpip[2]; char lc_char=' '; char lc_end[1]; pid_t child1, child2; int I,i, j, k=0;//計數變量 /* 管道的命令解析過程*/ for(I = 0, j = 0, k = 0; I<=li_inlen; I ++) { if(in[I]== ' ' || in[I] == '\t' ||in[I] == '\0' || in[I] == '|') { if(in[I] == '|') { /* 管道符號*/ if(j > 0) { buffer[j ++] = '\0'; /* 因為管道時連接的是兩個指令,所以用二維數組指針來存放命令和參數, li_comm是表示的幾個指令*/ argv[li_comm][k] = (char *) malloc(sizeof(char) * j); strcpy(argv[li_comm][k++], buffer); } // argv[li_comm][k] = (char *)malloc(sizeof(char)); argv[li_comm][k++] = (char *)0; /* 遇到管道符,第一個指令完畢,開始準備第二個指令*/ li_comm++; count = k; k = 0; j = 0; } if(j == 0) continue; else { buffer[j ++] = '\0'; argv[li_comm][k] = (char *)malloc(sizeof(char) * j); strcpy(argv[li_comm][k], buffer); k ++; } j = 0; /* Initiate */ continue; } else { if(in[I] == '&' && in[I + 1] == '\0') { is_back = 1; //是后臺進程則返回 return 0; //后臺進程不進行處理,直接返回 } } buffer[j ++] = in[I]; } //argv [li_comm][k] = (char *)malloc(sizeof(char)); argv [li_comm][k++] = (char *)0; if(is_fileexist(argv[0][0]) == -1) { printf("This first command is not founed !\n"); for(I = 0; I < count; I ++) free(argv[0][I]); return 0; } /* 指令解析結束*/ /* 建立管道*/ if(pipe(fd) == -1) { printf("Open pipe error !\n"); return -1; } /*創建的一個子進程執行管道符前的命令,并將輸出寫道管道*/ if((child1 = fork()) == 0) { /*關閉讀端*/ close(fd[0]); if(fd[1] != STDOUT_FILENO) { /* 將標準的輸出重定向到管道的寫入端,這樣該子進程的輸出就寫入了管道*/ if(dup2(fd[1], STDOUT_FILENO) == -1) { printf("Redirect Standard Out error !\n"); return -1; } /*關閉寫入端*/ close(fd[1]); } execv(buffer, argv[0]); } else { /*父進程*/ /* 先要等待寫入管道的進程結束*/ waitpid(child1, &li_comm,0); /* 然后我么必須寫入一個結束標記,告訴讀管道進程的數據到這里就完了*/ lc_end[0] = 0x1a; write(fd[1], lc_end, 1); close(fd[1]); if(is_fileexist(argv[1][0]) == -1) { printf("This command is not founded !\n"); for(I = 0; I < k; I ++) free(argv[1][I]); return 0; } /* 創建第二個進程執行管道符后的命令,并從管道讀書入流*/ if((child2 = fork()) == 0) { if(fd[0] != STDIN_FILENO) { /* 將標準的輸入重定向到管道的讀入端*/ if(dup2(fd[0], STDIN_FILENO) == -1) { printf("Redirect Standard In Error !\n"); return -1; } close(fd[0]); } execv(buffer, argv[1]); } else /*父進程*/ if(is_back == 0) waitpid(child2, NULL, 0); } for(I = 0; I < count; I ++) free(argv[0][I]); for(I = 0; I < k ; I ++) free(argv[1][I]); return 0; } int is_fileexist(char * comm)//查找命令是否存在 { char * env_path, * p; int i=0; /* 使用getenv函數來獲取系統環境變量,用參數PATH表示獲取路徑*/ env_path = getenv("PATH"); p = env_path; while(*p != '\0') { /* 路徑列表使用":"來分隔路徑*/ if(*p != ':') buffer[i ++] = *p; else { buffer[i ++] = '/'; buffer[i] = '\0'; /* 將指令和路徑合成,形成pathname,并使用access函數來判斷該文件是否存在*/ strcat(buffer, comm); if(access(buffer, F_OK) == 0) /* 文件被找到*/ return 0; else /* 繼續尋找其他路徑*/ i = 0; } p ++; } /* 搜索完所有路徑,依然沒有找到則返回 –1*/ return -1; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -