目前我们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);
* }
*/
//获取用户id
uid_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);
}