Lniux——myshell.cc

目录

一、mysell.cc代码

二、注意:

三、调试——cgdb的操作

四、初学者易混淆的函数


一、mysell.cc代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include<sys/wait.h>
using namespace std;

//我的系统自定义的环境变量表
const int envnum=64;
char* genv[envnum];

//全局命令行参数表
const int gargnum=64;
char* gargv[gargnum];
int gargc;

//全局当前工作路径
const int basesize=1024;
//有没有PWD一定要区分开
//有PWD=,存入genv
char my_pwd[basesize];
//没有PWD=,得最后路径名
char pwd[basesize];

//上一命令错误返回值
int lastcode=0;

//得到user
string GetUser(){
    string name=getenv("USER");
    return name.empty()?"None":name;
}

//得到hostname
string GetHostName(){
    string hostname=getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}

//得到pwd
string GetPwd(){
    //getcwp不依赖环境变量的正确性
    if(getcwd(pwd,sizeof(pwd))==nullptr)
    //为空就不写入genv
        return "None";

    snprintf(my_pwd,basesize,"PWD=%s",pwd);

    //注意在环境变量pwd可能为旧值(像cd后这种情况)要更新
    //my_pwd出作用域之后栈内存会被销毁
    //putenv(my_pwd);
    //setenv自动处理内存管理
    setenv("PWD",pwd,1);//1表示覆盖旧值
    return pwd;
}

//得到最后文件夹名
string LastDir(){
    string curr=GetPwd();
    if(curr=="/"||curr=="None")
        return curr;

    size_t pos=curr.rfind('/');
    if(pos==string::npos)
        return curr;
    return curr.substr(pos+1);

}
    
//打印指令行
void PrintCommand(){
    
    printf("[%s@%s %s]#",
            GetUser().c_str(),GetHostName().c_str(),LastDir().c_str());
    fflush(stdout);
}

//得到命令行:存在局部变量command_buffer[size]
bool GetCommand(char* command_buffer,int size){
    char* result=fgets(command_buffer,size,stdin);
    if(!result)
        return false;

    //把\n换成0
    command_buffer[strlen(command_buffer)-1]=0;
    if(strlen(command_buffer)==0)
        return false;
    return true;
}

//语法解析命令行:分段存入全局的命令行变量表
void ParseCommand(char* command_buffer){
    memset(gargv,0,gargnum);
    gargc=0;
    
    gargv[gargc++]=strtok(command_buffer," ");
    while((bool)(gargv[gargc++]=strtok(nullptr," ")));
    --gargc;
}

//初始化自己的全局环境变量表
void InitEnv(){
    extern char** environ;
    int index=0;
    while(environ[index]&&index<envnum-1){
        //先扩容再拷贝
        genv[index]=(char*)malloc(strlen(environ[index])+1);
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index]=nullptr;
}
//销毁自己的环境变量表
void DeletEnv(){
    for(int i=0;genv[i];i++){
        free(genv[i]);
    }
}
//添加环境变量
void AddEnv(const char* env){
    int index=0;
    while(genv[index]&&index<envnum-1){
        index++;
    }
    if(index==envnum-1){
        perror("环境变量已满\n");
    }

    genv[index]=(char*)malloc(strlen(env)+1);
    strncpy(genv[index],env,strlen(env)+1);
    genv[++index]=nullptr;
}

void ChDir(const string& dir){
    if(chdir(dir.c_str())!=0){
        perror("chdir failed\n");
    }

    //pwd和my_pwd都要更新
    if(getcwd(pwd,sizeof(pwd))==nullptr){
        perror("getcwd failed");
        return;
    }
    snprintf(my_pwd,sizeof(my_pwd),"PWD=%s",pwd);

    int index=0;
    while(strncmp(genv[index],"PWD=",4)!=0&&index<envnum-1){
        index++;
    }

    if(index==envnum-1){
        perror("环境变量已满\n");
    }
    else if(genv[index]==nullptr){
        genv[index+1]=nullptr;
    }
    else//是之前malloc过的要free掉
        free(genv[index]);

    genv[index]=(char*)malloc(strlen(my_pwd)+1);
    strncpy(genv[index],my_pwd,strlen(my_pwd)+1);
}

//检查并执行内接命令
bool CheckAndBuiltCommand(){
    //cd为内接命令是因为要改变的是当前进程的路径
    if(strcmp(gargv[0],"cd")==0){
        if(gargc==2){
            ChDir(gargv[1]);
        }
        else{
            lastcode=1;
        }
        return true;
    }

    else if(strcmp(gargv[0],"export")==0){
        if(gargc==2){
            AddEnv(gargv[1]);
        }
        else{
            lastcode=2;
        }
            return true;
    }

    else if(strcmp(gargv[0],"env")==0){
        for(int i=0;genv[i];i++){
            printf("%s\n",genv[i]);
        }
        return true;
    }

    else if(strcmp(gargv[0],"echo")==0){
		for(int i=1;i<gargc;i++){
            if(gargv[i][0]=='$'){
               if(gargv[i][1]=='?')
                    printf("%d\n",lastcode);
				else{
					char* var=getenv(gargv[i]+1);
					printf("%s%s",(var?var:" "),(i<gargc-1?" ":""));
				}
             }
             else{
                 printf("%s",gargv[i]);
             }
        }
        return true;

     }
    return false;
}
//外接命令
bool ExecuteCommand(){
    pid_t id=fork();
    if(id<0){
        perror("fork failed!");
        return false;
    }
    else if(id==0){
        execvpe(gargv[0],gargv,genv);
        exit(1);
    }
    else{
        int status=0;
        pid_t chid=waitpid(id,&status,0);
        if(chid<0){
            perror("waitpid failed");
            return false;
        }
        else if(chid>0){
            if(WIFEXITED(status)){
                lastcode=WEXITSTATUS(status);
            }
            else{
                lastcode=100;
            }
            return true;
        }
    }

    return false;
}

int main(){
    char command_buffer[basesize];
    InitEnv();
    while(1){
        PrintCommand();
        if(!GetCommand(command_buffer,basesize))
            continue;

        ParseCommand(command_buffer);
        if(CheckAndBuiltCommand())
            continue;

        ExecuteCommand();

    }
    DeletEnv();
    return 0;
}

二、注意


    1.分清楚my_pwd和pwd的区别
        my_pwd有“PWD=”,用来存入genv
        pwd没有,是从getcwd()中得到的

        getcwd()函数不依赖环境变量的正确性
        getenv()依赖
        -->pwd由getcwd()得到,用来跟新my_pwd和genv


    2.自己的ChDir内接命令(改变现在进程的地址)也要调用chdir()改变地址
        注意此时环境变量的地址还没有改变,
        所以要依次更新pwd,my_pwd,genv,环境变量


    3.更新环境变量时putenv() vs. setenv()
        不推荐putenv(const char* s),需要手动管理字符串生命周期
        推荐setenv("name",value,1),自动管理


三、调试——cgdb的操作


    1.char* result=fgets(command_buffer,size,stdin);
      当输入流字符未满要关闭时,此处操作是r stdin<input.txt
      其中input.txt是提前写好的输入文件

    2.调试子进程
      set follow-fork-mode [parent/child]     默认调试父进程,子进程继续运行……

      set detach-on-fork on 默认只调试父进程
      set detach-on-fork off 同时调试两个进程,被调试的进程暂停,另一个进程处于挂起状态

      info inferiors 显示所有[被调试]进程
        在detach-on-fork on的情况下不调试子进程
      inferior num 切换到指定num编号的进程


四、初学者易混淆的函数


    1.<cstring> strcmp VS. strncmp(const char* s1,const char* s2,size_t num),比较前num个
                strcpy     strncpy(const char* dest,const char*scr,size_t num),不含\0,自己注意手动加
      <cstdio>  sprintf    snprintf(缓冲区,最大安全长度,格式,字符)         ,自动添加\0

    2.<cstdlib> setenv VS. putenv
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值