完成了一个简单的shell,完成了assignment提到的所有功能,
执行非built in 指令,执行built in 指令 1.cd 2. exit 3 !# 类似history !#N 显示最近N条指令。 !3执行历史记录3的命令。
核心就是fork exec wait 不过纯用c感觉比较烦,资源需要小心管理,难于控制复杂程度,代码也仅仅是完成要求的基本功能,如果加入更多功能代码就更加容易出错,在记录历史和输入的顺序上处理的不太好,当前输入后counter就++了,应该先输入,然后分析命令,然后执行,如果historyf命令出错不加入历史,否则加入历史命令列表,counter++.
下一步考虑改用c++实现。
测试如下
allen:~/study/system_programming/uiuc_assignment/smp1$ ./shell
Shell(pid=16153)1:
Child failed to exec: No such file or directory
Shell(pid=16153)2:
allen:~/study/system_programming/uiuc_assignment/smp1$ ./shell
Shell(pid=16159)1:ls
CS241.txt Makefile README.pdf README.txt shell shell2 shell2.c shell2.cc shell.c shell.cc shell.h shell.o smp1.zip svn-commit.tmp TEAM.txt test
Shell(pid=16159)2:/bin/ls
CS241.txt Makefile README.pdf README.txt shell shell2 shell2.c shell2.cc shell.c shell.cc shell.h shell.o smp1.zip svn-commit.tmp TEAM.txt test
Shell(pid=16159)3:pwd
/home/allen/study/system_programming/uiuc_assignment/smp1
Shell(pid=16159)4:cd ..
Shell(pid=16159)5:pwd
/home/allen/study/system_programming/uiuc_assignment
Shell(pid=16159)6:cd smp1
Shell(pid=16159)7:ls
CS241.txt Makefile README.pdf README.txt shell shell2 shell2.c shell2.cc shell.c shell.cc shell.h shell.o smp1.zip svn-commit.tmp TEAM.txt test
Shell(pid=16159)8:ls -l
总用量 236
-rwxr-xr-x 1 allen allen 8064 2009-07-14 16:41 CS241.txt
-rwxr-xr-x 1 allen allen 182 2009-07-17 10:41 Makefile
-rwxr-xr-x 1 allen allen 119738 2009-07-14 16:41 README.pdf
-rwxr-xr-x 1 allen allen 7429 2009-07-14 16:41 README.txt
-rwxr-xr-x 1 allen allen 16764 2009-07-17 17:54 shell
-rwxr-xr-x 1 allen allen 11764 2009-07-17 12:39 shell2
-rw-r--r-- 1 allen allen 3727 2009-07-17 12:39 shell2.c
-rwxr-xr-x 1 allen allen 3197 2009-07-17 11:01 shell2.cc
-rwxr-xr-x 1 allen allen 8516 2009-07-17 17:54 shell.c
-rw-r--r-- 1 allen allen 3475 2009-07-16 21:54 shell.cc
-rw-r--r-- 1 allen allen 943 2009-07-16 22:02 shell.h
-rw-r--r-- 1 allen allen 11748 2009-07-17 17:54 shell.o
-rwxr-xr-x 1 allen allen 7296 2009-07-14 16:41 smp1.zip
-rw-r--r-- 1 allen allen 57 2009-07-15 10:45 svn-commit.tmp
-rwxr-xr-x 1 allen allen 560 2009-07-14 16:41 TEAM.txt
drwxr-xr-x 2 allen allen 4096 2009-07-17 11:32 test
Shell(pid=16159)9:cd shell
shell is not a directory
Shell(pid=16159)10:cd dfsdf
Cannot stat dfsdf: No such file or directory
Shell(pid=16159)11:!#
! 1 ls
! 2 /bin/ls
! 3 pwd
! 4 cd ..
! 5 pwd
! 6 cd smp1
! 7 ls
! 8 ls -l
! 9 cd shell
! 10 cd dfsdf
! 11 !#
Shell(pid=16159)12:!#3
! 10 cd dfsdf
! 11 !#
! 12 !#3
Shell(pid=16159)13:!9
cd shell
shell is not a directory
Shell(pid=16159)14:!#
! 1 ls
! 2 /bin/ls
! 3 pwd
! 4 cd ..
! 5 pwd
! 6 cd smp1
! 7 ls
! 8 ls -l
! 9 cd shell
! 10 cd dfsdf
! 11 !#
! 12 !#3
! 13 cd shell
! 14 !#
Shell(pid=16159)15:exit
allen:~/study/system_programming/uiuc_assignment/smp1$
shell.c
1 /* SMP1:Simple Shell */
2 // to do 可以引入vector 管理,vector < vector < char*> > 不要用string因为c api接口问题
3 /* LIBRARY SECTION */
4 #include <ctype.h> /* Character types */
5 #include <stdio.h> /* Standard buffered input/output */
6 #include <stdlib.h> /* Standard library functions */
7 #include <string.h> /* String operations */
8 #include <sys/types.h> /* Data types */
9 #include <sys/wait.h> /* Declarations for waiting */
10 #include <unistd.h> /* Standard symbolic constants and types */
11 #include <sys/stat.h> /* wee need stat */
12 /* DEFINE SECTION */
13 #define SHELL_BUFFER_SIZE 256 /* Size of the Shell input buffer */
14 const int CommandsNum = 10; //初始commands_vec 的容量
15 static int capacity; //当前commands_vec的最大容量,会根据指令数目超出后自动增加容量每次*2,模拟vector
16
17 static pid_t pid; //shell 进程id
18 static int counter; //指令计数,从0开始,当用户输入一个命令,counter++,当前命令应为counter - 1,注意print的时候从1开始
19 static char ***commands_vec; //记录所有的命令,commands_vec[0]表示第0个命令,该命令会由多个args构成(空格已去)
20
21 // 初始化,得到进程id,counter置0..
22 void Init()
23 {
24 pid = getpid();
25 counter = 0;
26 commands_vec = (char ***) malloc (CommandsNum * sizeof(char **));
27 capacity = CommandsNum;
28 }
29
30 //打印命令,所有的args
31 void EchoArgs(char **args)
32 {
33 while(*args) {
34 printf("%s ", *args);
35 args++;
36 }
37 printf("\n");
38 }
39
40 //释放一个命令所占的内存资源
41 void FreeResouce(char **args)
42 {
43 if (!args)
44 return;
45 char **arg = args;
46 while(*arg) {
47 free(*arg);
48 *arg = 0;
49 arg++;
50 }
51 free(args);
52 }
53
54 //是否历史记录的所有命令所占资源
55 void FreeComandsResouce()
56 {
57 int i;
58 for (i = 0; i < counter; i++) {
59 FreeResouce((char **)commands_vec[i]);
60 commands_vec[i] = NULL; //do not forget to make it NULL
61 }
62 free(commands_vec);
63 commands_vec = NULL;
64 }
65
66 //将命令arglist加入命令历史列表中,counter计数+1,如需要增加
67 //历史记录列表的容量,直接调用realloc更好
68 void AddHistory(char **arglist)
69 {
70 commands_vec[counter++] = arglist;
71 if (counter == capacity) {
72 char ***tmp;
73 tmp = (char ***) malloc (2 * capacity * sizeof(char **));
74 int i;
75 for (i = 0; i < capacity; i++) {
76 tmp[i] = commands_vec[i];
77 commands_vec[i] = NULL;
78 }
79 //memset(tmp + capacity, NULL, capacity * sizeof(char **));
80 free(commands_vec);
81 commands_vec = tmp;
82 capacity *= 2;
83 }
84 }
85
86 //打印提示信息,接受用户输入的命令,并进行预处理去掉空格存储,
87 //然后将命指针加入历史记录列表,返回当前命令指针
88 char** GetInput()
89 {
90 char input[SHELL_BUFFER_SIZE];
91 //char *arglist[SHELL_BUFFER_SIZE];
92 char** arglist;
93 arglist = (char **) malloc(SHELL_BUFFER_SIZE * sizeof(char *));
94 //memset(arglist, NULL, SHELL_BUFFER_SIZE * sizeof (char *));
95 int i = 0;
96 int j = 0;
97 int start = 0;
98 char c;
99
100 //打印提示信息
101 printf("Shell(pid=%d)%d:",pid,counter + 1);
102
103 //接受用户输入command,并且将各个命令参数存储起来,去空格识别
104 while (i < SHELL_BUFFER_SIZE && (c = getchar()) != '\n') {
105 if (c == ' ') {
106 if (input[i - 1] != ' ') {
107 arglist[j] = (char *) malloc(i - start + 1);
108 strncpy(arglist[j], input + start, i - start);
109 arglist[j][i - start] = '\0';
110 j++;
111 start = i + 1;
112 } else {
113 start++;
114 }
115 }
116
117 input[i++] = c;
118 }
119 arglist[j] = (char *) malloc(i - start + 1);
120 strncpy(arglist[j], input + start, i - start);
121 arglist[j][i - start] = '\0';
122 j++;
123 arglist[j] = NULL; //标志结束
124
125 if (i == SHELL_BUFFER_SIZE) { //错误处理,输入的command大小要小于256包括空格
126 while(c = getchar() != '\n') //需要把剩下的输入数据读完!
127 ;
128 printf("Commands should be withen the size of 256\n");
129 FreeResouce(arglist);
130 return NULL;
131 }
132
133 AddHistory(arglist);
134
135 return arglist;
136 }
137
138 //通过fork新开子进程执行non built in 命令
139 //通过主进程wait
140 void ExcuteNonBuiltinCommand(char **args)
141 {
142 pid_t p_id;
143 p_id = fork();
144
145 if (p_id == -1) {
146 perror("Failed to fork");
147 return;
148 }
149
150 //child code
151 if (p_id == 0) {
152 if (strstr(args[0],"/")) //如果命令中找到/,则按照绝对路径寻找
153 execv(args[0],args);
154 else //find command from PATH
155 execvp( args[0], args );
156
157 perror("Child failed to exec");
158 exit(1); //注意不要用return 子进程应该结束,即使它失败了,exit(1) not exit(0)
159 }
160
161 //parent code p_id != 0
162 if (wait(NULL) != p_id) {
163 perror("Parent failed to wait for child");
164 }
165
166 }
167
168 //打印最近n个命令历史记录
169 void PrintCommandHistory(int n) {
170 int i;
171 for (i = counter - n; i < counter; i++) {
172 printf(" !%3d ", i + 1);
173 EchoArgs(commands_vec[i]);
174 }
175 }
176
177 // 删除当前命令历史记录,因为按照当前的处理流程,用户输入后命令就被记录
178 // 对于需要删除不执行并且不记录的命令,需要将其删除
179 void DeleteCurrentCommand()
180 {
181 free(commands_vec[counter - 1]);
182 commands_vec[counter - 1] = NULL;
183 counter--;
184 }
185
186 int IsNum(char a)
187 {
188 if (a >= '0' && a <= '9')
189 return 1;
190 else
191 return 0;
192 }
193 //给定字符串中一个起始一个结束位置,返回所对应的数字大小,只处理正数
194 //如果出现非数字视为出错则返回-1,调用者确保arg,i, j的正确性
195 int GetNumFromString(char *arg, int start, int end)
196 {
197 int i;
198 int base = 1;
199 int sum = 0;
200 for(i = end; i >= start; i--) {
201 if(!IsNum(arg[i]))
202 return -1;
203 sum += (arg[i] - '0') * base;
204 base *= 10;
205 }
206 return sum;
207 }
208
209 //执行命令,区别built in 和 non built in
210 //built in 当前支持 cd, exit, !#, !,
211 //non built in 调用ExcuteNonBuiltinCommand
212 void ExcuteCommand(char **args)
213 {
214 if (!args)
215 return;
216
217 /* built in command */
218
219 /* cd */
220 char * str = "cd";
221 if (strcmp(args[0], str) == 0) {
222
223 struct stat info;
224
225 /*if no such file or dir */
226 if ( stat( args[1] , &info ) == -1 ){
227 fprintf(stderr, "Cannot stat ");
228 perror(args[1]);
229 return;
230 }
231 if(!S_ISDIR(info.st_mode)) {
232 fprintf(stderr, "%s is not a directory\n", args[1]);
233 return;
234 }
235
236 /*now ok to cd*/
237 chdir(args[1]);
238 return;
239 }
240
241 /* exit */
242 str = "exit";
243 if (strcmp(args[0], str) == 0) {
244 FreeComandsResouce();
245 exit(0);
246 return;
247 }
248
249 /* !# print history */
250 int len = strlen(args[0]);
251 if (args[0][0] == '!' && args[0][1] == '#') {
252
253 if (len == 2) {
254 /* !# print all the history*/
255 PrintCommandHistory(counter);
256 }
257
258 /* !#N print the last N commands */
259 if (len > 2) {
260 int n = GetNumFromString(args[0], 2, len - 1);
261 if (n == -1 || n > counter) {
262 printf("Not valid\n");
263 DeleteCurrentCommand();
264 return;
265 }
266 PrintCommandHistory(n);
267 }
268
269 return;
270 }
271
272 /* !N 执行历史记录标号为N的指令 */
273 if(args[0][0] == '!' && len > 1 && args[0][1] != '#') {
274 int n = GetNumFromString(args[0], 1, len - 1);
275
276 if (n == -1 || n == 0 || n >= counter) {
277 printf("Not valid\n");
278 DeleteCurrentCommand();
279 return;
280 }
281
282 DeleteCurrentCommand();
283 EchoArgs(commands_vec[n - 1]);
284 AddHistory(commands_vec[n - 1]);
285 ExcuteCommand(commands_vec[n - 1]);
286 return;
287 }
288
289 /* non built in command*/
290 ExcuteNonBuiltinCommand(args);
291 }
292
293
294 /* MAIN PROCEDURE SECTION */
295 int main(int argc, char **argv)
296 {
297 Init();
298 while (1) {
299 char **args = GetInput();
300 ExcuteCommand(args);
301 args = NULL;
302 }
303 FreeComandsResouce();
2 // to do 可以引入vector 管理,vector < vector < char*> > 不要用string因为c api接口问题
3 /* LIBRARY SECTION */
4 #include <ctype.h> /* Character types */
5 #include <stdio.h> /* Standard buffered input/output */
6 #include <stdlib.h> /* Standard library functions */
7 #include <string.h> /* String operations */
8 #include <sys/types.h> /* Data types */
9 #include <sys/wait.h> /* Declarations for waiting */
10 #include <unistd.h> /* Standard symbolic constants and types */
11 #include <sys/stat.h> /* wee need stat */
12 /* DEFINE SECTION */
13 #define SHELL_BUFFER_SIZE 256 /* Size of the Shell input buffer */
14 const int CommandsNum = 10; //初始commands_vec 的容量
15 static int capacity; //当前commands_vec的最大容量,会根据指令数目超出后自动增加容量每次*2,模拟vector
16
17 static pid_t pid; //shell 进程id
18 static int counter; //指令计数,从0开始,当用户输入一个命令,counter++,当前命令应为counter - 1,注意print的时候从1开始
19 static char ***commands_vec; //记录所有的命令,commands_vec[0]表示第0个命令,该命令会由多个args构成(空格已去)
20
21 // 初始化,得到进程id,counter置0..
22 void Init()
23 {
24 pid = getpid();
25 counter = 0;
26 commands_vec = (char ***) malloc (CommandsNum * sizeof(char **));
27 capacity = CommandsNum;
28 }
29
30 //打印命令,所有的args
31 void EchoArgs(char **args)
32 {
33 while(*args) {
34 printf("%s ", *args);
35 args++;
36 }
37 printf("\n");
38 }
39
40 //释放一个命令所占的内存资源
41 void FreeResouce(char **args)
42 {
43 if (!args)
44 return;
45 char **arg = args;
46 while(*arg) {
47 free(*arg);
48 *arg = 0;
49 arg++;
50 }
51 free(args);
52 }
53
54 //是否历史记录的所有命令所占资源
55 void FreeComandsResouce()
56 {
57 int i;
58 for (i = 0; i < counter; i++) {
59 FreeResouce((char **)commands_vec[i]);
60 commands_vec[i] = NULL; //do not forget to make it NULL
61 }
62 free(commands_vec);
63 commands_vec = NULL;
64 }
65
66 //将命令arglist加入命令历史列表中,counter计数+1,如需要增加
67 //历史记录列表的容量,直接调用realloc更好
68 void AddHistory(char **arglist)
69 {
70 commands_vec[counter++] = arglist;
71 if (counter == capacity) {
72 char ***tmp;
73 tmp = (char ***) malloc (2 * capacity * sizeof(char **));
74 int i;
75 for (i = 0; i < capacity; i++) {
76 tmp[i] = commands_vec[i];
77 commands_vec[i] = NULL;
78 }
79 //memset(tmp + capacity, NULL, capacity * sizeof(char **));
80 free(commands_vec);
81 commands_vec = tmp;
82 capacity *= 2;
83 }
84 }
85
86 //打印提示信息,接受用户输入的命令,并进行预处理去掉空格存储,
87 //然后将命指针加入历史记录列表,返回当前命令指针
88 char** GetInput()
89 {
90 char input[SHELL_BUFFER_SIZE];
91 //char *arglist[SHELL_BUFFER_SIZE];
92 char** arglist;
93 arglist = (char **) malloc(SHELL_BUFFER_SIZE * sizeof(char *));
94 //memset(arglist, NULL, SHELL_BUFFER_SIZE * sizeof (char *));
95 int i = 0;
96 int j = 0;
97 int start = 0;
98 char c;
99
100 //打印提示信息
101 printf("Shell(pid=%d)%d:",pid,counter + 1);
102
103 //接受用户输入command,并且将各个命令参数存储起来,去空格识别
104 while (i < SHELL_BUFFER_SIZE && (c = getchar()) != '\n') {
105 if (c == ' ') {
106 if (input[i - 1] != ' ') {
107 arglist[j] = (char *) malloc(i - start + 1);
108 strncpy(arglist[j], input + start, i - start);
109 arglist[j][i - start] = '\0';
110 j++;
111 start = i + 1;
112 } else {
113 start++;
114 }
115 }
116
117 input[i++] = c;
118 }
119 arglist[j] = (char *) malloc(i - start + 1);
120 strncpy(arglist[j], input + start, i - start);
121 arglist[j][i - start] = '\0';
122 j++;
123 arglist[j] = NULL; //标志结束
124
125 if (i == SHELL_BUFFER_SIZE) { //错误处理,输入的command大小要小于256包括空格
126 while(c = getchar() != '\n') //需要把剩下的输入数据读完!
127 ;
128 printf("Commands should be withen the size of 256\n");
129 FreeResouce(arglist);
130 return NULL;
131 }
132
133 AddHistory(arglist);
134
135 return arglist;
136 }
137
138 //通过fork新开子进程执行non built in 命令
139 //通过主进程wait
140 void ExcuteNonBuiltinCommand(char **args)
141 {
142 pid_t p_id;
143 p_id = fork();
144
145 if (p_id == -1) {
146 perror("Failed to fork");
147 return;
148 }
149
150 //child code
151 if (p_id == 0) {
152 if (strstr(args[0],"/")) //如果命令中找到/,则按照绝对路径寻找
153 execv(args[0],args);
154 else //find command from PATH
155 execvp( args[0], args );
156
157 perror("Child failed to exec");
158 exit(1); //注意不要用return 子进程应该结束,即使它失败了,exit(1) not exit(0)
159 }
160
161 //parent code p_id != 0
162 if (wait(NULL) != p_id) {
163 perror("Parent failed to wait for child");
164 }
165
166 }
167
168 //打印最近n个命令历史记录
169 void PrintCommandHistory(int n) {
170 int i;
171 for (i = counter - n; i < counter; i++) {
172 printf(" !%3d ", i + 1);
173 EchoArgs(commands_vec[i]);
174 }
175 }
176
177 // 删除当前命令历史记录,因为按照当前的处理流程,用户输入后命令就被记录
178 // 对于需要删除不执行并且不记录的命令,需要将其删除
179 void DeleteCurrentCommand()
180 {
181 free(commands_vec[counter - 1]);
182 commands_vec[counter - 1] = NULL;
183 counter--;
184 }
185
186 int IsNum(char a)
187 {
188 if (a >= '0' && a <= '9')
189 return 1;
190 else
191 return 0;
192 }
193 //给定字符串中一个起始一个结束位置,返回所对应的数字大小,只处理正数
194 //如果出现非数字视为出错则返回-1,调用者确保arg,i, j的正确性
195 int GetNumFromString(char *arg, int start, int end)
196 {
197 int i;
198 int base = 1;
199 int sum = 0;
200 for(i = end; i >= start; i--) {
201 if(!IsNum(arg[i]))
202 return -1;
203 sum += (arg[i] - '0') * base;
204 base *= 10;
205 }
206 return sum;
207 }
208
209 //执行命令,区别built in 和 non built in
210 //built in 当前支持 cd, exit, !#, !,
211 //non built in 调用ExcuteNonBuiltinCommand
212 void ExcuteCommand(char **args)
213 {
214 if (!args)
215 return;
216
217 /* built in command */
218
219 /* cd */
220 char * str = "cd";
221 if (strcmp(args[0], str) == 0) {
222
223 struct stat info;
224
225 /*if no such file or dir */
226 if ( stat( args[1] , &info ) == -1 ){
227 fprintf(stderr, "Cannot stat ");
228 perror(args[1]);
229 return;
230 }
231 if(!S_ISDIR(info.st_mode)) {
232 fprintf(stderr, "%s is not a directory\n", args[1]);
233 return;
234 }
235
236 /*now ok to cd*/
237 chdir(args[1]);
238 return;
239 }
240
241 /* exit */
242 str = "exit";
243 if (strcmp(args[0], str) == 0) {
244 FreeComandsResouce();
245 exit(0);
246 return;
247 }
248
249 /* !# print history */
250 int len = strlen(args[0]);
251 if (args[0][0] == '!' && args[0][1] == '#') {
252
253 if (len == 2) {
254 /* !# print all the history*/
255 PrintCommandHistory(counter);
256 }
257
258 /* !#N print the last N commands */
259 if (len > 2) {
260 int n = GetNumFromString(args[0], 2, len - 1);
261 if (n == -1 || n > counter) {
262 printf("Not valid\n");
263 DeleteCurrentCommand();
264 return;
265 }
266 PrintCommandHistory(n);
267 }
268
269 return;
270 }
271
272 /* !N 执行历史记录标号为N的指令 */
273 if(args[0][0] == '!' && len > 1 && args[0][1] != '#') {
274 int n = GetNumFromString(args[0], 1, len - 1);
275
276 if (n == -1 || n == 0 || n >= counter) {
277 printf("Not valid\n");
278 DeleteCurrentCommand();
279 return;
280 }
281
282 DeleteCurrentCommand();
283 EchoArgs(commands_vec[n - 1]);
284 AddHistory(commands_vec[n - 1]);
285 ExcuteCommand(commands_vec[n - 1]);
286 return;
287 }
288
289 /* non built in command*/
290 ExcuteNonBuiltinCommand(args);
291 }
292
293
294 /* MAIN PROCEDURE SECTION */
295 int main(int argc, char **argv)
296 {
297 Init();
298 while (1) {
299 char **args = GetInput();
300 ExcuteCommand(args);
301 args = NULL;
302 }
303 FreeComandsResouce();
304 } /* end main() */