编写一个简单的shell

来源:互联网 发布:中宏数据库 讲座 编辑:程序博客网 时间:2024/04/28 07:23

编写一个简单的shell:今天只是写个框架,后面会完善shell ,注:暂且不支持内建命令,不支持重定向,不支持管道

  先介绍一下shell:Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序。它不属于内核部分,而是在核心之外,是操作系统的外壳程序,以用户态方式运行。其基本功能是解释并执行用户打入的各种命令,实现用户与Linux核心的接口。系统初启后,核心为每个终端用户建立一个进程去执行Shell解释程序。

 shell的执行过程:

(1)读取用户由键盘输入的命令行。 

(2)分析命令,以命令名作为文件名,并将其它参数改造为系统调用execve( )内部处理所要求的形式。 

(3)终端进程调用fork( )建立一个子进程。 
(4)终端进程本身用系统调用wait4( )来等待子进程完成(如果是后台命令,则不等待)。当子进程运行时调用execve(),子进程根据文件名(即命令名)到目录中查找有关文件(这是命令解释程序构成的文件),将它调入内存,执行这个程序(解释这条命令)。 
(5)如果命令末尾有&号(后台命令符号),则终端进程不用系统调用wait4( )等待,立即发提示符,让用户输入下一个命令,转⑴。如果命令末尾没有&号,则终端进程要一直等待,当子进程(即运行命令的进程)完成处理后终止,向父进程(终端进程)报告,此时终端进程醒来,在做必要的判别等工作后,终端进程发提示符,让用户输入新的命令,重复上述处理过程。

具体实现代码:

1)为了模仿系统的shell,先把名字,主机名,当前路径,打印出来

     1) 先介绍两个获取用户名的函数:

getuid函数:函数返回一个调用程序的真实用户ID,一般来说,这个函数都是会调用成功。

函数原型:uid_t getuid(void);

函数头文件:#include <unistd.h>    #include <sys/types.h>


getpwuid函数:根据用户id,把用户信息存储在一个结构体中

函数原型:struct passwd *getpwuid(uid_t uid);

函数头文件: #include <sys/types.h>   #include <pwd.h>

现在贴代码 获取用户名:

void GetLogName(){struct passwd* pass;pass = getpwuid(getuid());printf("[%s@",pass->pw_name);}
  

2)获取主机名

 gethostname函数:

函数原型: int gethostname(char *name, size_t len);

函数头文件:#include <unistd.h>

函数参数:name保存获取到信息,len是期望获取字符个数

void GetHostName(){char name[128];gethostname(name,sizeof(name)-1);printf("%s",name);}

    3)获取当前路径

getcwd函数:

函数原型:char *getcwd(char *buf, size_t size);

函数参数:获取当前路径保存到buf中

函数返回值:成功返回存储路径的指针,失败NULL。

 

void GetDir(){char pwd[128];getcwd(pwd,sizeof(pwd)-1);int len = strlen(pwd);char* p = pwd+len-1;while(*p != '/'){p--;}p++;printf(" %s]@",p);}

注意:执行完这三个函数必须用fflush(stdout) 刷新缓存区;

(2)从键盘读取命令,存取到cmd数组中,然后把命令分解存放到_argv指针数组中

read函数:从文件标识符fd中读取count个字符到buf中

函数原型: ssize_t read(int fd, void *buf, size_t count);

直接贴代码解释吧:

char cmd[128]; //存放命令ssize_t _s = read(0, cmd, sizeof(cmd)-1);// 从标准输入流0 读取命令if(_s > 0)//read success, _s is read number{cmd[_s-1] = '\0'; //由于最后读取一个回车\n}else{perror("read");return 1;}char* _argv[32]; // 指针数组 存放cmd命令,为execvp函数_argv[0] = cmd;char* start = cmd;// start作为一个循环标志int i =1;while(*start){if(isspace(*start)){*start ='\0';start++;_argv[i] = start;i++;continue;}start++;}_argv[i] = NULL;


(3)终端进程调用fork函数创建子进程,子进程调用execvp函数进行解释命令

 execvp函数不在解释,前面博客有介绍,

说下获取程序退出的状态的宏

WIFEXITED:这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。

WEIXTSTATUS:获得子进程exit()返回的结束代码,一般先要用WIFEXITED判断是否正常结束

贴代码:

pid_t id =fork();if(id <0){perror("fork failed\n");}else if(id == 0) //child -> run cmd{execvp(_argv[0],_argv);exit(1);}else //father{int status =0;pid_t ret = waitpid(id,&status,0);//等待子进程执行完if(ret > 0 && WIFEXITED(status)){printf("exit code: %d\n",WEXITSTATUS(status)); //获取退出码}else{perror("wait failed");}}

程序测试结果:

 现在就已经简单的实现了shell,但是有一个不足之处就是只能执行一次,这个问题很好解决:只要在外层加一个while循环即可。

  

上面代码还有不足之处,请大家多多指教。

1 0
原创粉丝点击