自主shell命令行解释器

发布于:2025-05-11 ⋅ 阅读:(24) ⋅ 点赞:(0)

目标

  • 能处理普通命令
  • 能处理内建命令

实现原理

用下面的时间轴来表示时间发生次序。时间从左向右。shell由标识为sh的方块,它随着时间从左向右移动。
shell从用户读入字符串“ls”。shell建立一个新的进程,然后等待进程中运行ls程序并等待进程结束。
在这里插入图片描述
然后shell读取新的一行输入,建立一个新的进程在这个进程中运行程序,等待进程结束。
所以写一个shell,需要循环一下过程

  1. 获取命令行
  2. 解析命令行
  3. 建立子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait)

源码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "


//下面是shell定义的全局数据
// 1.命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;

// 2.环境变量表
#define MAX_ENV 100
char* g_env[MAX_ENV];
int g_envs = 0;

char cwd[1024];
char cwdenv[1024];


// last code exit
int lastcode = 0;



const char* GetUserName()
{
    const char* name=getenv("USER");
    return name == NULL ? "None" : name;
}

const char* GetHostName()
{
    const char* host=getenv("HOSTNAME");
    return host == NULL ? "None" : host;
}

const char* GetPwd()
{
    //const char* pwd=getenv("PWD");
    const char* pwd = getcwd(cwd,sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

const char* GetHome()
{
    const char* home=getenv("HOME");
    return home == NULL ? "None" : home;
}

const char* GetOldpwd()
{
    const char* oldpwd=getenv("OLDPWD");
    return oldpwd == NULL ? "None" : oldpwd;
}

// 处理目录
std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if(dir == SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos) return pwd;
    return dir.substr(pos+1);
}

void InitEnv()
{
    extern char** environ;
    memset(g_env,0,sizeof(g_env));
    g_envs = 0;

    //简化操作,从父进程获得环境变量
    for(int i = 0; environ[i]; i++)
    {
        g_env[i]=(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs] = NULL;

    //2.导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

void MakeCommandLine(char cmd_prompt[], int size)
{
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}

void PrintfCommandPrompt()
{
    char command[COMMAND_SIZE];
    MakeCommandLine(command,COMMAND_SIZE);
    printf("%s",command);
    fflush(stdout);
}

// ls -a -l => "ls -a -l"字符串
bool GetCommandLine(char* out, int size)
{
    char* c = fgets(out, size, stdin);
    if(c==NULL) return false;
    out[strlen(out)-1]=0; // 清理\n
    if(strlen(out) <= 0) return false;
    return true;
}


// 命令行分析 "ls -a -l" => "ls" "-a" "-l"
bool CommandParse(char* commandline)
{
#define SEP " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline, SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));
    g_argc--;
    return g_argc > 0 ? true :false;
}

// command
bool cd()
{
    //cd
    if(g_argc == 1)
    {
        std::string home = GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else 
    {
        std::string where = g_argv[1];
        //cd ~
        if(where == "~")
        {
            std::string home = GetHome();
            chdir(home.c_str());
        }
        //cd -
        if(where == "-")
        {
            std::string oldpwd = GetOldpwd();
            chdir(oldpwd.c_str());
        }
        
        chdir(where.c_str());
    
    }

    return true;
}
void PrintEnv()
{
    extern char** environ;
    for(int i = 0; environ[i];++i)
    {
        printf("%-2d->%s\n",i,environ[i]);
    }
}

bool CheckAndExcuteBuildin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        cd();
        return true;
    }
    if(cmd == "test")
    {
        PrintEnv();
        return true;
    }
    return false;
}

void Excute()
{
    pid_t id = fork();

    if(id == 0) // child
    {
        execvp(g_argv[0], g_argv);
        exit(1);
    }
    // father
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
        lastcode=WEXITSTATUS(status);        
    }
}



void test_GetCommandLine(char* str)
{
    printf("%s\n", str);
}
void test_CommandParse()
{
    for(int i = 0; i < g_argc; ++i)
        std::cout << g_argv[i] << " " ;
}
void test_InitEnv()
{
    for(int i = 0; g_env[i]; i++)
    {
        printf("env[%2d]->%s\n",i,g_env[i]);
    }
    printf("envs: %d\n",g_envs);
}


int main()
{
    InitEnv();
    while(true)
    {
        // 1. 输出命令行提示符  [jfs@superg-alicloud myshell]$
        PrintfCommandPrompt();
        
        // 2. 获取用户输入的命令 ls -a -l
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline, sizeof(commandline))) 
            continue;
        //test_GetCommandLine(commandline);
        
        // 3. 命令行分析,"ls -a -l" => "ls" "-a" "-l"
        if(!CommandParse(commandline))
            continue;
        //test_CommandParse();
        
        // 4.检测并处理内建命令
        if(CheckAndExcuteBuildin())
            continue;


        // 5. 执行命令
        Excute();
    }
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到