编写一个简单的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循环即可。
上面代码还有不足之处,请大家多多指教。
- 编写一个简单的shell
- 编写一个简单的shell
- Linux 编写一个简单的Shell脚本
- 【linux】编写一个简单的shell
- 简单shell的编写
- 一个简单的shell脚本编写的GUI程序
- shell脚本入门---编写一个简单的脚本(批处理)
- 自己编写简单的shell
- 编写简单的shell脚本
- 自己编写的简单shell
- 一个简单的shell
- 一个简单的shell
- shell语法的简单学习以及编写一个简单的进度条
- 使用shell编写的一个简单的时钟(tput和date介绍)
- 编写自己的shell解析器(一)一个简单的循环命令输入和历史打印
- 如何编写一个简单的shell脚本.task3用到的脚本
- linux入门:编写一个简单的shell(仅支持部分ls,ps命令)
- 用C语言编写一个Linux下的简单shell程序
- 前端总结·基础篇·CSS(一)布局
- psr php
- 文件操作工具类
- 求最长不下降序列(逆推)
- Buildbot初探
- 编写一个简单的shell
- 线程的生命周期
- Linux中表示“时间”的结构体和相关函数
- make menuconfig提示'make menuconfig' requires the ncurses libraries解决方法
- erase容器中元素的原则
- HDU1874 畅通工程续 (dijkstra,floyd)
- H264实时传输心得总结
- 12. 指针、句柄、引用的区别
- WeakReference