之前已经花了不少篇幅学习了linux系统编程的很多知识点:文件与io、进程、信号、管道,而零散的知识点,怎么能够综合的串接起来是学习的一个很重要的目的,当然最好的方式就是用所学的知识点做一个项目了,所以接下来会实现一个小型的迷你shell程序,跟缩减版的系统shell程序,不要看着项目很小,但是五脏俱全,先来看一下我们要实现的功能:
如exit会退出程序等。
另外还能捕捉一些信号,如:ctrl+c,也能忽略一些信号,如:ctrl+\,禁止退出自己的程序,这里就不演示了。
下面就来从零开始一步步实现我们自己的shell程序,首先进行模块的划分:
-------main.c //这个是一个主控程序
-------parse.c、parse.h //这个主要是用来进行shell命令的解析的
再编写一个Makefile,由于项目中会由多个.c文件构成,所以很有必要进行整体编译,关于Makefile的写法,可以参考博文:http://www.cnblogs.com/webor2006/p/3789589.html
这个Makefile的编写是比较简单的,这里就不详述了,里面内容如下:
这样,总体的项目编译环境就已经搭建好了,另外说明一下实现的思路:先搭建好一个整体的框架,然后于对其每个模块进行一一细化,最终完善整个功能,所有功能的实现都会按这个思路来,而且很重要的一条就是:步步为营,也就是当写完一段代码后,就立马进行编译运行,来确保每小段代码都成功,这样的话,一点点功能进行拆分,最终实现一个项目,所以接下来,先要实现一个简单的框架功能:
#include#include #include #include #include "parse.h"#include "externs.h"#include "init.h"/* * shell主循环 */void shell_loop(void){ while(1){ printf("[myshell]$ "); fflush(stdout); /* 初始化环境 */ init(); if(read_command() == -1) break; parse_command(); execute_command(); } printf("\nexit\n");}/* * 读取命令 * 成功返回0,失败或者读取到文件结束符(EOF)返回-1 */int read_command(void){ if(fgets(cmdline,MAXLINE,stdin) == NULL){ return -1; } return 0;}/* * 解析命令 * 成功返回解析到的命令个数,失败返回-1 */int parse_command(void){ char *cp = cmdline; char *avp = avline; int i = 0; while(*cp != '\0'){ /** 去除左空格 **/ while(*cp == ' ' || *cp == '\t') cp++; /* 如果到了行尾,跳出循环 */ if(*cp == '\0' || *cp == '\n') break; cmd.args[i] = avp; while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n') *avp++ = *cp++; //printf("[%s]\n",cmd.args[i]); *avp++ = '\0'; i++; } return 0;}/* * 执行命令 * 成功返回0,失败返回-1 */int execute_command(void){ pid_t pid = fork(); if(pid == -1){ //进程创建失败 ERR_EXIT("fork"); } if(pid == 0) { //子进程去执行替换函数 execvp(cmd.args[0],cmd.args); } //父进程等待子进程的退出,这样并不会改变父进程本身的行为,所以进程就不会退出了 wait(NULL); return 0;}
这节的最终运行效果如下: