目前我们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);
} 
                


















