目前我们Linux的系统默认的命令解释器是bash;
命令解释器(也称为命令行解释器或shell)是计算机操作系统中的一个重要组件,它负责接收用户输入的命令,并解释和执行这些命令。其实命令解释器就是解析命令,执行命令,输出反馈;
1.命令的分类
内置命令和普通命令
1.内置命令:cd exit 2普通命令:ls pwd cp ps 等等
如果是普通命令,那么使用which是可以找到的,比如which ps;which ls;which pwd;which cp;
也就是普通命令是一个可执行程序.
但是我们找cd和exit是找不到的; 因为内置命令cd,exit等它是在bash本身实现的; 而bash也是一个可执行程序,比如:which bash;
简单来讲,就是普通命令是通过fork+exec实现的;而内置命令是bash自身通过调用相应的接口实现的;
2.项目框架
3.strtok的介绍
字符串分割函数
注意:
strtok线程不安全,原因就是函数实现使用了一个static的变量(指针记录下次分割的地址,再次调用要沿用上次的,所以需要静态变量).
在多线程中,如果两个线程都使用了strtok的话,这个变量的值就会被另一个线程不定期的进行修改.
4.mybash.c
#include<assert.h>
#include<wait.h>
#include<sys/types.h>
#include<pwd.h>
#include<sys/utsname.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define ARG_MAX 10
#define CHAR_MAX 128
char* get_cmd(char* buff,char* myargv[])
{if(buff==NULL||myargv==NULL){perror("buff为NULL或myargv为NULL\n");return NULL;}char* s=strtok(buff," ");int i=0;while(s!=NULL){myargv[i++]=s;s=strtok(NULL," ");}return myargv[0];
}
char* getpath()
{char buf[CHAR_MAX];//当前的绝对路径getcwd(buf,CHAR_MAX);char* stdpath=(char*)malloc(CHAR_MAX);//用户家目录uid_t uid=getuid();struct passwd* pw;pw=getpwuid(uid);strcpy(stdpath,pw->pw_dir);int i=0;char* path=(char*)malloc(CHAR_MAX);while(stdpath[i]!='\0'){if(buf[i]!=stdpath[i]){strcpy(path,buf);break;}i++;}if(stdpath[i]=='\0'){strcpy(path,"~");if(strchr(strstr(buf,getlogin()),'/')!=NULL){strcat(path,strchr(strstr(buf,getlogin()),'/'));}}return path;
}
int main(int argc,char* argv[])
{char* myenvp[]={"/home/stu/bash/mybin"};//获取主机名struct utsname uts;uname(&uts);/*char* hostname[128]={0};* if(gethostname(hostname,128)==-1)* {* printf("mybash1.0>>> ");* fflush(stdout);* }*///获取用户iduid_t uid=getuid();char user='$';if(uid==0){user='#';}while(1){printf("\033[1;35m%s@%s\033[0m:\033[1;34m%s\033[0m%c ",getlogin(),uts.nodename/*hostname*/,getpath(),user);//命令提示符fflush(stdout);//刷新缓冲区char buff[CHAR_MAX];fgets(buff,CHAR_MAX,stdin);//从键盘获取命令buff[strlen(buff)-1]='\0';//解析命令char* myargv[ARG_MAX]={0};char* cmd=get_cmd(buff,myargv);//命令//如果是内置命令if(cmd==NULL){continue;}else if(strcmp(cmd,"cd")==0){if(myargv[1]!=NULL){if(chdir(myargv[1])==-1){perror("path error\n");}}else{char path[128]="/home/";strcat(path,getlogin());chdir(path);} }else if(strcmp(cmd,"exit")==0){break;}//如果是普通命令else{pid_t pid=fork();assert(pid!=-1);if(pid==0){char pathname[CHAR_MAX]={0};strcat(strcpy(pathname,"/home/stu/bash/mybin/"),cmd);execv(pathname,myargv);perror("execve error!\n");exit(0);}else{wait(NULL);}}}exit(0);
}
5.拓展
上面的代码我们实现了通过fork+exec的模式,子进程调用了系统的对应命令的可执行文件,并没有完全自己实现命令,而要实现这个功能呢,就需要你将每一个命令都实现出来,这是一个硕大的工程,但也不要灰心,我们可以实现先一些简单的命令练练手,也算是自己实现过了。
这里给大家放一个ls命令的实现
#include <stdio.h>struct dirent *s=NULL;while((s=readdir(pdir))!=NULL){ if(strncmp(s->d_name,".",1)==0){continue;}//printf("%s ",s->d_name);struct stat filestat;stat(s->d_name,&filestat);if(S_ISDIR(filestat.st_mode)){printf("\033[1;34m%s\033[0m ",s->d_name);}else{if(filestat.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)){printf("\033[1;32m%s\033[0m ",s->d_name);}else{printf("%s ",s->d_name);}}} printf("\n");closedir(pdir);exit(0);
}