一.项目简介
本项目旨在实现一个简易的linux shell命令行。我们将通过逐步分析和实现来构建这个shell,使其能够执行基本的命令行操作。
二.分析项目实现

实现一个shell需要循环以下过程:
获取命令行解析命令行建立一个子进程(fork)替换子进程(execvp)父进程等待子进程退出(wait)
三.逐步实现项目功能
1.获取命令行
我们将获取命令行设计为一个循环,除非用户主动退出,否则一直保持命令行接收指令的状态。具体实现逻辑如下:
int main(){ while(!quit){ // 2.交互问题,获取命令行内容 interact(commandline, sizeof(commandline)); // 3.分割命令字符串strtok(),解析命令行 int argc = splitstring(commandline, argv); if(argc == 0) continue; // 4.指令的判断 int n = buildCommand(argv, argc); // 5.普通命令的执行 if(!n) NormalExcute(argv);}return 0;
}
具体的获取命令行逻辑如下函数:
const char* getusername(){// 通过getenv()获取环境变量中的用户名return getenv("USER");}void getpwd(){// 通过getcwd系统接口获取并更新pwdgetcwd(pwd, sizeof(pwd));}
void interact(char *cline, int size){// 需要环境变量相关的系统调用函数来获取命令行提示信息// 获取主机名char hostname[64];gethostname(hostname, sizeof(hostname));
// 1.打印bash命令行前面的提示信息getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), hostname, pwd);// 2.接收用户输入信息fgets(cline, size, stdin);assert(cline != NULL);(void)cline; // 防止编译器报错定义而未使用的变量(假装用一下)cline[strlen(cline)-1] = ' ';
}
2.解析命令行
解析命令行主要是将获取到的字符串按空格切分开来放入一个新数组中。我们使用strtok()来完成这个工作,具体实现代码如下:
int splitstring(char cline[], char _argv[]){int i = 0;_argv[i++] = strtok(cline, DELIM);while(_argv[i++] = strtok(NULL, DELIM));return i-1;}
3.指令的判断
虽然我们可以借助fork()创建子进程来实现诸多普通命令,但对于很多内建命令来说,创建子进程执行命令的结果并不会影响父进程,这会导致父进程命令无效。因此对于内建命令我们要先判断,再让父进程自主完成这些内建命令,代码如下:
一键职达
AI全自动批量代投简历软件,自动浏览招聘网站从海量职位中用AI匹配职位并完成投递的全自动操作,真正实现'一键职达'的便捷体验。
79 查看详情
int buildCommand(char _argv[], int _argc){// 4.指令的判断// cd命令if(_argc == 2 && strcmp(_argv[0], "cd") == 0){// 更改目录chdir(_argv[1]);getpwd();// 更改环境变量sprintf(getenv("PWD"), "%s", pwd);return 1;}// export命令else if(_argc == 2 && strcmp(_argv[0], "export") == 0){ // 因为_argv一直被我们用来存新的指令,环境变量会因此被覆盖 // 所以需要一个固定的存环境变量的地方来保存环境变量 strcpy(myenv, _argv[1]); putenv(myenv); return 1;}// echo命令else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){ if(strcmp(_argv[1], "$?") == 0){ printf("%dn", lastcode); lastcode = 0; } else if(*_argv[1] == '$'){ char *val = getenv(_argv[1]+1); if(val) printf("%sn", val); } else { printf("%sn", _argv[1]); } return 1;}// ls命令if(strcmp(_argv[0], "ls") == 0){ _argv[_argc++] = "--color"; _argv[_argc] = NULL;}return 0;
}
4.普通命令的执行
普通命令的执行不会影响父进程,因此我们可以使用fork()创建子进程,然后使用exec系列进程替换函数来完成相关操作,代码如下:
void NormalExcute(char _argv[]){// 5.普通命令的执行pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){// 子进程execvp(_argv[0], _argv);perror("execvp");exit(1);}else{// 父进程int status;waitpid(id, &status, 0);if(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}}}
四.完整项目代码
完整项目代码如下:
#includeinclude
include
include
include
include
include
include
define LEFT "["
define RIGHT "]"
define LABLE "$"
define DELIM " t"
define LINE_SIZE 1024
define ARGC_SIZE 32
define EXIT_CODE 55
int lastcode = 0;int quit = 0;char commandline[LINE_SIZE];char *argv[ARGC_SIZE];char pwd[LINE_SIZE];// 自定义环境变量表,做成二维数组就需要维护了char myenv[LINE_SIZE];// 自定义本地变量表
const char* getusername(){// 通过getenv()获取环境变量中的用户名return getenv("USER");}
void getpwd(){// 通过getcwd系统接口获取并更新pwdgetcwd(pwd, sizeof(pwd));}
void interact(char *cline, int size){// 1.打印bash命令行前面的提示信息// 需要环境变量相关的系统调用函数来获取命令行提示信息char hostname[64];gethostname(hostname, sizeof(hostname));getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), hostname, pwd);
// 2.接收用户输入信息fgets(cline, size, stdin);assert(cline != NULL);(void)cline; // 防止编译器报错定义而未使用的变量(假装用一下)cline[strlen(cline)-1] = ' ';
}
int splitstring(char cline[], char *_argv[]){int i = 0;_argv[i++] = strtok(cline, DELIM);while(_argv[i++] = strtok(NULL, DELIM));return i-1;}
void NormalExcute(char *_argv[]){// 5.普通命令的执行pid_t id = fork();if(id < 0){perror("fork");exit(1);}else if(id == 0){// 子进程execvp(_argv[0], _argv);perror("execvp");exit(1);}else{// 父进程int status;waitpid(id, &status, 0);if(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}}}
结语
希望这篇关于在Linux中实现一个简易的shell命令行的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流。
学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!
以上就是【Linux】实现一个简易的shell命令行的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/486283.html
微信扫一扫
支付宝扫一扫