环境:

Ubuntu14-4   内核 4.4.0-135

vim编辑器 7.4 

gcc  4.8.4 

1.1 知识点

  • Shell 的基本概念
  • 进程控制相关的系统调用的使用(如 fork,exec函数族)

整理框架:

1.命令解释器首先是一个死循环。

2.打印一个命令提示符。

3.取得命令行输入放在数组里面,要求命令带参数。可以getc()、fgets()、scanf()等。

4.如果用fgets()的话,取得的字符串包括最后输入的换行符,故要去掉命令字符串末尾的“/n”,变成“/0”。

5.用字符串切割函数将得到的命令行以空格切割存储在字符串数组中。

6.创建一个子进程,调用exec执行命令,将切割得到的命令以及参数传入。

7.父进程调用waitpid()等待子进程的退出,然后进入下一次循环。

bash终端部分:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/utsname.h>
#include<pwd.h>
#define STRLEN 128

//命令行提示符
void PrintfFlag()
{
    struct passwd *pw=getpwuid(getuid());//得到当前用户的名称的结构体指针pw
    assert(pw!=NULL);
    struct utsname un;//得到主机标识
    uname(&un);
    //	assert(uname!=NULL);
    char shortname[STRLEN]={0};
    memmove(shortname,un.nodename,9);
    char path[STRLEN]={0};//得到当前工作路径
    getcwd(path,STRLEN-1);
    char *p=path;
    if(strcmp(path,"/")==0)
    {
        p="/";
    }
    //	else if(strcmp(p,getenv("HOME"))==0)

    else if(strcmp(p,pw->pw_dir)==0)
    {
        p="~";
    }
    else
    {
        p=path+strlen(path);
        while(*p--!='/');
        p+=2;
    }

    char flag='$';//得到当前用户状态
    if(getuid()==0)
    {
        flag='#';
    }
    printf("[%s@%s %s]%c ",pw->pw_name,shortname,p,flag);

}

void CutCmd(char *cmd,char *argv[])
{
    int count=0;
    char *p=strtok(cmd," ");      //以空格分割cmd,将得到的字符串存储在argv字符串数组中
    while(p!=NULL)
    {
        argv[count++]=p;
        if(count==STRLEN)
        {
            break;
        }
        p=strtok(NULL," ");
    }
}

void CdCmd(char *path)
{
    static char PrePath[STRLEN]={0};      //静态区存储上一次目录
    struct passwd*pw=NULL;
    if(strcmp(path,"~")==0)
    {
        pw=getpwuid(getuid());
        assert(pw!=NULL);
        path=pw->pw_dir;
    }
    else if(strcmp(path,"-")==0)
    {
        if(strlen(PrePath)==0)
        {
            printf("OLDPWD not set\n");
            return ;
        }
        path=PrePath;
    }
    char NowPath[STRLEN]={0};
    getcwd(NowPath,STRLEN-1);      //获得当前工作目录
    if(-1==chdir(path))            //工作目录转移
    {
        printf("bash: cd: %s: No such file or director\n",path);
    }
    else
    {
        strcpy(PrePath,NowPath);
    }
}
int main()
{
    while(1)
    {
        PrintfFlag();
        char cmd[STRLEN] = {0};
        fgets(cmd,STRLEN,stdin);//      输入命令
        cmd[strlen(cmd) - 1] = 0;
        if(strlen(cmd) == 0)
        {
            continue;
        }
        if(strcmp(cmd,"exit") == 0)
        {
            exit(0);
        }
        char *argv[STRLEN] = {0};
        CutCmd(cmd,argv);   //比如:argv[0]="ls",argv[1]="-al",argv[2]=getcwd();
        if(strcmp(argv[0],"cd") == 0)
        {
            CdCmd(argv[1]);
            continue;
        }
        pid_t pid=fork();
        assert(pid != -1);
        if(pid == 0)
        {
            char path[STRLEN] = {"/home/zhao/Desktop/shell/bin/"};
            if(strstr(argv[0],"/") != NULL)//防止带路经命令引起路径错误
            {
                memset(path,0,STRLEN);
            }
            strcat(path,argv[0]);
            execv(path,argv);
            printf("bash: %s: command not found\n",argv[0]);
            exit(0);      //当exec函数调用失败时,子进程能够安全推出
        }
        else
        {
            wait(NULL);
        }


    }
    return 0;
}


 

 

Logo

更多推荐