写在前面

我们总会站在前人的肩膀上去眺望远方,无论是课程设计还是人生皆如此。

简单shell命令行解释器的设计与实现

要设计的 shell 类似于 sh,bash,csh 等,必须支持以下内部命令:

  • cd <目录>更改当前的工作目录到另一个<目录>。如果<目录>未指定,输出当前工作目录。如果<目录>不存在,应当有适当的错误信息提示。这个命令应该也能改变 PWD 的环境变量。
  • environ 列出所有环境变量字符串的设置(类似于 Linux 系统下的 env 命令)。
  • echo < 内容 > 显示 echo 后的内容且换行。
  • help 简短概要的输出你的 shell 的使用方法和基本功能。
  • jobs 输出 shell 当前的一系列子进程,必须提供子进程的命名和 PID 号。
  • quit,exit,bye 退出 shell。

提示:shell 的主体就是反复下面的循环过程

1
2
3
4
5
6
7
8
9
10
while(1){
//接收用户输入的命令行;
//解析命令行;
if(//用户命令为内部命令)
//直接处理;
else if(//用户命令为外部命令)
//创建子进程执行命令;
else
//提示错误的命令;
}

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
char command(char *s)
{
if(!strncasecmp(s,"exit",4)) return 'q';
else if(!strncasecmp(s,"quit",4)) return 'q';
else if(!strncasecmp(s,"bye",4)) return 'q';
else if(!strncasecmp(s,"cd",2)) return 1;
else if(!strncasecmp(s,"ls",2)) return 2;
else if(!strncasecmp(s,"rm",2)) return 3;
else if(!strncasecmp(s,"mkdir",5)) return 4;
else if(!strncasecmp(s,"echo",4)) return 5;
else if(!strncasecmp(s,"help",4)) return 6;
else if(!strncasecmp(s,"env",3)) return 7;
else if(!strncasecmp(s,"jobs",4)) return 8;
else if (!strncasecmp(s,"clean",5)) return 9;
else return 0;
}
void help()
{
printf( "\t*****************帮助********************\n"
"\t* 命令 功能\n"
"\t* cd 更改当前的工作目录到另一个<目录>\n"
"\t* environ 列出所有环境变量字符串的设置\n"
"\t* echo 显示echo后面的内容并换行\n"
"\t* help 显示帮助信息\n"
"\t* jobs 输出shell当前的一系列子进程,包括子进程的命名和PID号\n"
"\t* ls 显示当前目录的所有文件\n"
"\t* rm 删除当前目录下的文件或目录\n"
"\t* mkdir 在当前目录下创建新目录\n"
"\t* clean 清屏\n"
"\t* quit|exit|bye 退出shell\n"
"\t*****************************************\n");
}
int main()
{
char n[100],n1[100];
char b;
pid_t pid;
help();
while(1)
{
memset(n,0,100);
printf("%s_@_$:",getcwd(n1,100)); // print the information;
fgets(n,100,stdin); //input cmdline;
n[strlen(n)-1]='\0'; //delete the '\n';
b=command(n); //get the return values;
if(b=='q')
break; //exit the shell;
switch(b)
{
//change the directory
case 1:
if(chdir(n+3)!=0)
printf(" 打开工作目录(%s)失败!\n",n+3);//+3 = 'c','d','\0',后面的数字为输入命令的字符个数加+1(‘\0’占一个字符);
printf(" 当前工作目录:'%s'\n",getcwd(n1,100));
break;
//list
case 2:
if((pid=fork())<0)
{
printf(" fork error\n");
exit(EXIT_FAILURE);
}
else if(pid==0)
{
if(execl("/bin/ls","ls",NULL)<0);
printf(" execl error\n");
exit(EXIT_FAILURE);
}
waitpid(pid,0,0);
break;
//remove a directory
case 3:
remove(n+3);
printf(" 文件已删除!\n");
break;
//make a directory
case 4:
mkdir(n+6,S_IRWXU);
printf(" 文件创建成功!\n");
break;
//print something
case 5:
printf(" %s\n",n+5);
break;
//help
case 6:
help();
break;
//environment
case 7:
if((pid=fork())<0)
{
printf(" fork error\n");
exit(EXIT_FAILURE);
}
else if(pid==0)
{
if(execl("/bin/env","env",NULL)<0)
printf(" execl error\n");
exit(EXIT_FAILURE);
}
waitpid(pid,0,0);
break;
//ps the running process ;
case 8:
system("ps");//systemcall_ps
break;
case 9:
system("clear");
break;
//cmd can't find;
case 0:
printf("PID: %ld.没有此命令,请重新输入正确的命令。\n", (long)getpid());
help();
break;
}
}
return 0;
}

效果如图所示:

效果

输入不同的命令:

environ

help

ls

clean

总结

shell使用fork函数创建新的进程,用exec在新进程中运行用户指定的程序,最后shell用wait等待新进程的结束。wait系统调用同时从内核取得退出状态以告知子进程是如何结束的。