shell是Unix/Linux中的重要工具,用来解析用户输入的命令。下面我们来实现一个简单的shell程序,来练习fork/exec/wait/exit的使用,顺便推荐一本书籍《Understanding Unix/Linux Programming - A Guide to Theory and Practice》,这本书写的非常好,适合Unix/Linux系统编程初学者使用。

下面是我们shell的主程序:

 1 int main(void)
 2 {
 3     char *cmdline;
 4     char *prompt;
 5     char **arglist;
 6     int result;
 7 
 8     prompt = PROMPT;
 9     signal(SIGINT, SIG_IGN);
10     signal(SIGQUIT,SIG_IGN);
11 
12     while ((cmdline = next_cmd(prompt, stdin)) != NULL) {
13         if ((arglist = split_line(cmdline)) != NULL) {
14             result = execute(arglist);
15             freelist(arglist);
16         }
17         free(cmdline);
18     }
19 
20     return 0;
21 }

其中,next_cmd()函数的主要功能是从输入流中读入下一个命令,碰到文件结束符返回NULL。下面是该函数的代码:

 1 char *next_cmd(char *prompt, FILE *file)
 2 {
 3     char *cmdline;
 4     int  length = 0;
 5     int  c;
 6     int  location = 0;
 7 
 8     printf("%s", prompt);
 9 
10     while ((c = getc(file)) != '\n') {
11         if (location + 1 >= length) {
12             cmdline = (char *)malloc(BUFSIZ);
13             length = BUFSIZ;
14         } 
15         else if (location >= BUFSIZ) {
16             cmdline = realloc(cmdline, length + BUFSIZ);
17             length += BUFSIZ;
18         }
19         cmdline[location++] = c;
20     }
21     cmdline[location] = '\0';
22 
23     return cmdline;
24 }

split_line()函数的主要功能是将输入的一行字符串拆解成字符串数组,该字符串数组以NULL结束。下面是该函数的代码:

 1 char **split_line(char *cmd)
 2 {
 3     char **arglist;
 4     int  row = 0;
 5     int  len = 0;
 6     char *cp = cmd;
 7     char *start = cmd;
 8     arglist = malloc(BUFSIZ);
 9 
10     while (*cp != '\0') {
11         while (*cp != ' ' && *cp != '\t') {
12             ++cp;
13             ++len;
14         }
15         arglist[row] = malloc(len + 1);
16         strncpy(arglist[row], start, len);
17         ++row;
18         len = 0;
19         while (*cp == ' ' || *cp == '\t') {
20             ++cp;
21         }
22         start = cp;
23     }
24     arglist[row] = NULL;
25 
26     return arglist;
27 }

execute()函数的主要功能是使用fork, execvp和wait函数来运行一个命令,并返回命令的结束状态。下面该函数的代码:

 1 int execute(char **argv) 
 2 {
 3     pid_t pid;
 4     int child_info = -1;
 5 
 6     if ((pid = fork()) == -1) {
 7         perror("fork error");
 8     }
 9 
10     if (pid == 0) {
11         signal(SIGINT, SIG_DFL);
12         signal(SIGQUIT, SIG_DFL);
13         execvp(argv[0], argv);
14         perror("exec error");
15         exit(1);
16     }else {
17         if (wait(&child_info) == -1) {
18             perror("wait error");
19         }
20     }
21 
22     return child_info;
23 }

freelist()函数是释放上面分配的字符串数组的空间。下面是该函数的代码:

 1 void freelist(char **list) 
 2 {
 3     char **cp = list;
 4     while (*cp) {
 5         free(*cp);
 6         ++cp;
 7     }
 8 
 9     free(list);
10 }

由于个人水平有限,欢迎讨论,非喜勿喷,thank you!!