🐶博主主页: @ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列: 线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++
🔥座右铭: "不要等到什么都没有了,才下定决心去做"
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
感觉,有很久都没有写博客,主要是最近学的内容难以理解,所以时间不太充足,就没有写博客的时间,今天为什么要这一篇文章呢?主要是我感觉实现一个自己的命令行小程序还是比较有趣的。我们平时都是在linux的shell命令行上直接输入指令,有没有想过自己也可以去实现一个,自己的bash呢?
那就让我们一起来探索属于自己自己的bash
这次我就直接给出源代码,没有把其中的方法,单独拿出来分析,我主要觉得那样有点显得代码冗余,其实也不用担心看不懂,我在源码中加了很多注释,大家也不怕看不懂。其实这样做还有一个好处,就是我们可以直接复制到我们的linux文件中,直接运行。
源码
cpp#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #define SIZE 100 //用于定义命令的最大长度 #define NUM 1024 //用于定义命令参数的最多个数 #define SEP " " //分割字符的时候,默认以空格作为分隔符 char _cwd[1024]; //用于存储PWD的环境变量 char env_val[1024]; //用于自定义一个环境变量,这里有一个缺点,就是env_val是一个数组,只能添加一个自定义的环境变量,下次添加时,就会覆盖上次添加的环境变量 int lastcode=0; //用于定义最近一个进程的退出码 //获取用户名字 const char* getUsername(void) { const char* name=getenv("HOME");//getenv如果没有获取到环境变量会返回空值 if(name)return name; else return "none"; } //获取主机名字 const char* getHostname(void) { const char* host=getenv("HOSTNAME"); if(host)return host; else return "none"; } //获取工作目录 const char* getCwd(void) { const char* cwd=getenv("PWD"); if(cwd)return cwd; else return "none"; } //获取用户输入的命令 int getUserCommand(char* usercommand,int num) { char* r=fgets(usercommand,num,stdin); if(r==NULL) return -1; usercommand[strlen(usercommand)-1]='\0'; return strlen(usercommand); } //分割字符串 int commandSplist(char* usercommand,char* argv[]) { int argc=0; argv[argc++]=strtok(usercommand,SEP); while(argv[argc++]=strtok(NULL,SEP)); return 0; } //执行命令 int execute(char* argv[]) { pid_t id=fork(); if(id<0)return -1; else if(id==0) { //child execvp(argv[0],argv);//程序替换 exit(1);//程序替换失败返回1 } else { //farther int status=0;//保存子进程退出时的退出码和退出信号 pid_t rid=waitpid(id,&status,0);//阻塞等待 if(rid>0) lastcode=WEXITSTATUS(status);//获取子进程退出时的退出码 } return 0; } //改变工作路径 void cd(const char* path) { chdir(path);//改变工作路径 //虽然路径改了,但是环境变量中PWD存储的工作路径并没有改变 char temp[1024]; getcwd(temp, sizeof(temp)); sprintf(_cwd, "PWD=%s",temp); putenv(_cwd);//为什么这里putenv[temp]?因为temp是一个临时变量,putenv只是将temp这个指针放到环境变量体系中,当函数调用结束,指针就销毁了,这个工作路径的环境变量就访问不了了 } //什么叫做内建命令:内建命令就是bash自己执行的,类似于自己内部的一个函数! //1.是内建命令 0不是内建命令 //执行的是内建命令 int doBuildin(char* argv[]) { if(strcmp(argv[0],"cd")==0)//cd命令 { char* path=NULL; if(argv[1]==NULL) path="."; else path=argv[1]; cd(path); return 1; } else if(strcmp(argv[0],"export")==0)//export命令 { if(argv[1]==NULL)return 1; strcpy(env_val,argv[1]); putenv(env_val); return 1; } else if(strcmp(argv[0],"echo")==0)//echo命令 { char val_0=*argv[1]; char* val=argv[1]+1;//argv[1]+1:例如$? 则argv[1]是$ argv[1]+1是? if(val_0=='$'&&strcmp(val,"?")==0)//我们定义?保存着最近一个进程的退出码 { printf("%d\n",lastcode); lastcode=0; } else if(val_0=='$') { printf("%s\n",getenv(val));//打印环境 } else//echo打印字符 { printf("%s\n",argv[1]); } return 1; } else { //可以添加其他的内建命令 } return 0; } int main() { while(1) { char usercommand[SIZE];//存储用户输入的命令 char* argv[NUM];//存储命令行参数 //打印命令行提示符 printf("[%s@%s %s]$",getUsername(),getHostname(),getCwd()); //输入命令 int n=getUserCommand(usercommand, sizeof(usercommand)); if(n<=0)continue; //分割字符 commandSplist(usercommand, argv); //判断是否是内建命令 n=doBuildin(argv); if(n==1)continue; //执行命令 execute(argv); } return 0; }
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸