项目《基于Linux下的mybash命令解释器》(二)

发布于:2025-05-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、使用系统命令的完整代码

#include<stdio.h>
#include<wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<pwd.h>

#define ARG_MAX 10//防止参数不够,可以做到一改全改

char *get_cmd(char *buff,char *myargv[]){//这个函数的作用是分割命令
    if(buff == NULL||myargv == NULL){
        return NULL;//说明用户没有输入命令
    }
    char *s = strtok(buff," ");//第一次调用strtok
    int i = 0;
    while(s!=NULL){//不确定用户输入的命令需要分割几次,只要分割到字符串末尾为'\0',就会结束
        myargv[i++] = s;//保存分割的结果
        s = strtok(NULL," ");//后续调用不用再传buff,因为内部有指针会记录
    }
    return myargv[0];
}

void run_cmd(char *path,char *myargv[]){//这个函数的作用是当输入命令为普通命令时,创建子进程执行
    if(path == NULL || myargv == NULL)
        return;
    pid_t pid = fork();
    if(pid == -1)
        return;
    //下面我们要让父子进程做不一样的事情
    if(pid == 0)//子进程进入if
    {
        execvp(path,myargv);
        //这里选择替换系列时,选带v,这样就可以传数组,选带p的,这样就不需要传环境变量
        perror("execvp error!\n");//一定要记得打印错误信息,这样方便我们明确的知道execvp是否执行成功
       // exit(0);
    }
    else//父进程进入esle
    {
    //这里要记得处理将死进程
        wait(NULL);
    }
}

void printf_info(){//打印提示信息,获取用户名,主机名,当前位置,获取用户角色(普通用户还是管理员)
    char *user_str="$";//默认为普通用户
    int user_id = getuid();
    if(user_id == 0){//当uid=0时为root,将$改为#
        user_str = "#";
    }
    struct passwd * ptr = getpwuid(user_id);//得到用户名
    if(ptr == NULL)//如果为NULL,也就是出现问题了,那么打印bash的版本号
    {
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    //获取主机名
    char hostname[128] = {0};
    if(gethostname(hostname,128)==-1){//如果获取主机名失败,打印版本号
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    //获取主机路径
    char dir[256] = {0};
    if(getcwd(dir,256)==NULL){//如果获取路径失败,打印版本号
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    printf("\033[1;32m%s@%s\033[0m  \033[1;34m%s\033[0m%s ",ptr->pw_name,hostname,dir,user_str);
    fflush(stdout);
}

int main()
{
    while(1)
    {
        printf_info();

        char buff[128]={0};
        fgets(buff,128,stdin);
        buff[strlen(buff)-1] = '\0';//去除buff里面的\n,防止exit和exit\n不匹配

        char *myargv[ARG_MAX] = {0};

        char *cmd = get_cmd(buff,myargv);//提取buff里面的命令
        if(cmd == NULL)
        {
            continue;
        }
        else if(strcmp(cmd,"cd")==0){
            if(myargv[1] != NULL){//当cd后面有路径时
                if(chdir(myargv[1])==-1){
                 perror("cd err!\n");
                }
            }
            //当用户只输入cd时,会进入家目录(和系统保持一致)
        }
        else if(strcmp(cmd,"exit")==0){
            break;//若为exit,则退出内部
            exit(0);//可以,但不好
        }
        else{
            //普通命令
            //fork + exec
            run_cmd(cmd,myargv);//也可以直接传myargv
        }
    }
    exit(0);
}

二、使用自己环境变量的完整代码

//mybash.c
#include<stdio.h>
#include<wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<pwd.h>

#define ARG_MAX 10//防止参数不够,可以做到一改全改
#define PATH_BIN "/home/stu/quzijie/class03/test15/mybin/"

char *get_cmd(char *buff,char *myargv[]){//这个函数的作用是分割命令
    if(buff == NULL||myargv == NULL){
        return NULL;//说明用户没有输入命令
    }
    char *s = strtok(buff," ");//第一次调用strtok
    int i = 0;
    while(s!=NULL){//不确定用户输入的命令需要分割几次,只要分割到字符串末尾为'\0',就>会结束
        myargv[i++] = s;//保存分割的结果
        s = strtok(NULL," ");//后续调用不用再传buff,因为内部有指针会记录
    }
    return myargv[0];
}

void run_cmd(char *path,char *myargv[]){//这个函数的作用是当输入命令为普通命令时,创>建子进程执行
    if(path == NULL || myargv == NULL)
        return;
    pid_t pid = fork();
    if(pid == -1)
        return;
    //下面我们要让父子进程做不一样的事情
    if(pid == 0)//子进程进入if
    {
        //使用自己的环境变量mybin
        //在只输入命令的情况下拼接,./和/完整路径这两种情况都不需要拼接
        char pathname[128] = {0};
        if(strncmp(path,"/",1)==0||strncmp(path,"./",2)==0)//不拼接
        {
            strcpy(pathname,path);
        }
        else//拼接,只有命令
        {
            strcpy(pathname,PATH_BIN);//先复制路径
            strcat(pathname,path);//然后和命令拼接
        }
        execv(pathname,myargv);
        perror("execv error!\n");
        exit(0);
    }
    else//父进程进入esle
    {
    //这里要记得处理将死进程
        wait(NULL);
    }
}
void printf_info(){//打印提示信息,获取用户名,主机名,当前位置,获取用户角色(普通用
户还是管理员)
    char *user_str="$";//默认为普通用户
    int user_id = getuid();
    if(user_id == 0){//当uid=0时为root,将$改为#
        user_str = "#";
    }
    struct passwd * ptr = getpwuid(user_id);//得到用户名
    if(ptr == NULL)//如果为NULL,也就是出现问题了,那么打印bash的版本号
    {
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    //获取主机名
    char hostname[128] = {0};
    if(gethostname(hostname,128)==-1){//如果获取主机名失败,打印版本号
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    //获取主机路径
    char dir[256] = {0};
    if(getcwd(dir,256)==NULL){//如果获取路径失败,打印版本号
        printf("mybash1.0>> ");
        fflush(stdout);
        return;
    }
    printf("\033[1;32m%s@%s\033[0m  \033[1;34m%s\033[0m%s ",ptr->pw_name,hostname,dir,user_str);
    fflush(stdout);
}
int main()
{
    while(1)
    {
        printf_info();

        char buff[128]={0};
        fgets(buff,128,stdin);
        buff[strlen(buff)-1] = '\0';//去除buff里面的\n,防止exit和exit\n不匹配

        char *myargv[ARG_MAX] = {0};

        char *cmd = get_cmd(buff,myargv);//提取buff里面的命令
        if(cmd == NULL)
        {
            continue;
        }
        else if(strcmp(cmd,"cd")==0){
            if(myargv[1] != NULL){//当cd后面有路径时
                if(chdir(myargv[1])==-1){
                 perror("cd err!\n");
                }
            }
            //当用户只输入cd时,会进入家目录(和系统保持一致)
        }
        else if(strcmp(cmd,"exit")==0){
            break;//若为exit,则退出内部
            exit(0);//可以,但不好
        }
        else{
            //普通命令
            //fork + exec
            run_cmd(cmd,myargv);//也可以直接传myargv
        }
    }
    exit(0);
}
//mybin目录下的自己实现的命令
//clear.c
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
    printf("\033[2J\033[0;0H");
}
//pwd.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
    char path[256] = {0};
    if(getcwd(path,256) == NULL) 
    {   
        perror("getcwd error!\n");
        exit(1);
    }   
    printf("%s\n",path);
    exit(0);   
}
//ls.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
int main(){
    char path[256] = {0};
    if(getcwd(path,256) == NULL){
        perror("getcwd error!\n");
        exit(1);
    }   
    DIR *pdir = opendir(path);
    if(pdir == NULL)
    {   
        perror("opendir error!\n");
        exit(1);
    }   
    struct dirent *s = NULL;
    while((s=readdir(pdir))!=NULL)
    {   
     if(strncmp(s->d_name,".",1)==0)
     {   
        continue;
     }   
     //是目录文件打印为蓝色,不是目录文件分为两种,普通文件是黑色,可执行文件是绿色
     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);
}


网站公告

今日签到

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