亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Linux命令行解釋器的模擬實現(xiàn)過程

 更新時間:2025年04月18日 09:37:05   作者:羑悻的小殺馬特.  
平時使用指令操作linux系統(tǒng)的時候可能會輸入一些不好的指令,這些指令可能會對操作系統(tǒng)內(nèi)核造成影響,所以就有了命令行解釋器這個東西,本文小編給大家介紹了Linux命令行解釋器的模擬實現(xiàn)過程,需要的朋友可以參考下

一、整體框架:

首先我們把這個myshell大致進(jìn)行框架展示出:

我們首先創(chuàng)建數(shù)組cl保存要輸入的字符串;而只要讀取失敗就要一直讀取故我們在獲取,命令行輸入的時候利用了while循環(huán);其次就是如果是內(nèi)建命令;我們就要直接父進(jìn)程執(zhí)行完;無需execute再讓子進(jìn)程執(zhí)行了。

先說一下想法:這里可執(zhí)行程序,把它當(dāng)成真正shell的bash;大部分命令都是通過調(diào)用子進(jìn)程來程序替換完成;有些命令是內(nèi)建的,故需要自己完成;而首先這個程序會繼承原本bash的那張環(huán)境變量表;這里我們模擬實現(xiàn)一下真正的bash的那兩張表:也就是說我們用數(shù)組,通過拷貝原bash的表,改變environ指針來維護(hù)我們的數(shù)組(也就是我們自己的可執(zhí)行程序要調(diào)用的那張環(huán)境變量表) :這里補(bǔ)充一點:對于環(huán)境變量如果我們env命令:它是通過environ指針來進(jìn)行查找打印的;局部打印就不一定了。 后面我們具體實現(xiàn)的時候會有所體現(xiàn),之后我們道來。

然后下面就是一步步對這些拆開的函數(shù)進(jìn)行實現(xiàn)了。

二、初始化myshell的環(huán)境變量表和命令行參數(shù)表:

這里我們自己開了兩個數(shù)組來模擬這兩張表;也就是拷貝父bash的那兩種表拷貝過來(簡單模擬一下)這倆張表的內(nèi)容就可以作為我們后面程序替換執(zhí)行命令要傳遞的參數(shù)等。

void initenv(){
    memset(env, 0, sizeof(env));
    envs= 0;
 
    for(int i=0;environ[i];i++){
        env[i]=(char*)malloc(strlen(environ[i])+1);//這里模擬的也可以不開空間,直接棧上
        strcpy(env[i], environ[i]);
        envs++;
    }
        env[envs] = NULL;
 
    
    // for(int i = 0; env[i]; i++)
   // {
   //     putenv(env[i]);
   // }
    environ = env;//用自己生成的env表
}

這里我們的命令行參數(shù)表暫時不需要填充,但是需要把環(huán)境變量表由bash那里拷貝過來;并改變了environ指針指向,也就是說等我們執(zhí)行env操作的時候它就會打印我們的這個env數(shù)組了;比如后序我們用putenv等命令的話,它就會通過environ指針對我們的這個數(shù)組進(jìn)行一些增加/覆蓋環(huán)境變量的操作了。 

三、命令行提示行的打?。?/h2>

我們讓它格式輸出這樣的格式:

#define FT "my simulate shell:%s@%s %s# "http://snprintf的format最大值

首先我們對比一下真正的命令解釋器:

我們此刻需要替換掉%s的就是通過環(huán)境變量找到USER,HOSTHOME ,PWD了:

const char* getuser(){
    const char*u=getenv("USER");
    return u==NULL?"NONE":u;
}
 
const char* gethostname(){
    const char*h=getenv("HOSTNAME");
    return h==NULL?"NONE":h;
}
 
const char* getpwd(){
  const char*p=getenv("PWD");
   return p==NULL?"NONE":p;
}

但是這樣我們會發(fā)現(xiàn)因為我們維護(hù)的這張環(huán)境變量表,未添加其他修改功能,這里需要我們手動修改;這樣pwd就不會變了(當(dāng)換目錄的時候):因此我們手動維護(hù)一下:

下面是我們要添加的全局變量(因為導(dǎo)入environ維護(hù)的二維數(shù)組應(yīng)該是地址;故給它整成全局): 

//這里獲得環(huán)境變量和其他上面不同;因為當(dāng)我們通過chdir改變當(dāng)前目錄的時候它在環(huán)境變量中的記錄(真正的bash實現(xiàn)了)而我們沒有實現(xiàn),因此我們
//可以通過getcwd每次調(diào)完新的目錄開始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時候變化
const  char *getpwd(){
        char *pwd = getcwd(cwd, sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        putenv(cwdenv);//put進(jìn)去的是環(huán)境變量的地址也就是一個指針指向的是那段量,因此指針要么是全局要么在堆上
    }
    return  pwd==NULL?"NONE":pwd;
}

普及一下用到的getcwd:

參數(shù):放入的數(shù)組;最大字節(jié)數(shù);成功返回這段pwd失敗就是NULL。

這里我們再做一個小優(yōu)化;也就是把路徑變短一下就乘當(dāng)下目錄:

std::string convert(const char *pwd){
    std::string s=pwd;
  auto pos=s.rfind('/');
  if(pos==std::string::npos)return "error";
  if(pos==s.size()-1)return "/";
  else return s.substr(pos+1);
 
 
  
}

因此我們從末尾給它分割了一下:最后調(diào)用它返回的string對象的c_str接口就好;

這里順便說一下;因為后面很多都用到這一點:就是經(jīng)常操作的時候把char串變成string然后調(diào)用它的c_str()為了方便;以及后面很多要注意作用域:因此考慮了全局設(shè)計。 

再下面就是命令行打印了:

void ptcmdprompt(){
    char p[CS]={0};
    snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() );
    printf("%s",p);
    //無\n及時刷新標(biāo)準(zhǔn)輸出緩沖區(qū)
    fflush(stdout);
}

下面展示下效果: 

四、獲取命令參數(shù):

這里邏輯比較簡單就是把我們輸入的字符讀入然后把后面的\n去掉:

bool gtcmdline(char *p,int size){
  char *j=fgets(p,size,stdin);
 // printf("參數(shù)是:%d",size);
  if(j==NULL)return false;
  //printf("%s\n",p);
  p[strlen(p)-1]=0;//干掉\n
  if(strlen(p)==0) return false;
  else return true;
}

這里用到了fgets:

也就是:從流中獲得字符最多是size個到串s中;讀取成功返回這個s;否則返回NULL。

五、重定向判斷:

這里我們封裝的是redirect函數(shù)來完成;簡單說就是讓它檢查我們輸入的cl中是否有> < >>等重定向標(biāo)識符;然后根據(jù)左右分別是命令,文件等給它分離開了;并給對應(yīng)的文件重定向(dup2一下):

預(yù)處理:首先利用標(biāo)識來枚舉一下重定向狀態(tài):輸出,輸入,還是追加:

下面就說一下細(xì)節(jié)處理:

這里值得關(guān)注的是:我們從數(shù)組末尾開始找

標(biāo)識符的;這樣然后利用覆蓋0的操作來完成前方命令的截斷:判斷順序:<  >  >>;其次就是它可能文件前面存在空格;故我們再構(gòu)建一個去除空格函數(shù)。

?
void eliminatesp(char cl[],int &fg){
    while(isspace(cl[fg])) fg++;
    
}
void redirect(char cl[]){
      status=NOPUT_RE;
      int start=0;
      int end=strlen(cl)-1;
      while(start<end){
          if(cl[end]=='<'){
              cl[end++] = 0;
            eliminatesp(cl, end);
            status = INPUT_RE;
            filename = cl+end;
            break;
          }
          else if(cl[end]=='>'){
              if(cl[end-1]=='>'){
                 status=APPPUT_RE;
                 cl[end-1]=0;
              }
              else{
                  status=OUTPUT_RE;
                  cl[end]=0;
              }
             end++;
             eliminatesp(cl,end);
              filename = cl+end;
                 break;
          }
          else{
              end--;
          }
      }
    
}
 
?

下面我們把獲得了重定向左邊的命令和右邊的文件下面就是利用dup2完成重定向操作了:

這里由于不是內(nèi)建命令;故我們還是放在子進(jìn)程來執(zhí)行:

 if(status==INPUT_RE){
          int fd=open(filename.c_str(),O_RDONLY);
          if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
      }
 
      else if(status==OUTPUT_RE){
           int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
      }
          else if(status==APPPUT_RE){
          int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
 
          }
      else { }//NOPUT_RE無重定向操作

故我們只需當(dāng)分開始fork后對我們標(biāo)志的進(jìn)行判斷即可;但是當(dāng)子進(jìn)程完成重定向執(zhí)行完退出后我們又要對status這個狀態(tài)給它重置一下。

六、語義分析:

簡單來說就是利用我們的strtok函數(shù)完成對空格的分割;然后把它填入到我們自己創(chuàng)建的argv數(shù)組中;注:這里最后也要補(bǔ)上NULL;注意好邊界處理:

bool  cmdparse(char *c){
    argc=0;
   std::string cc=c;
 
 //  淺拷貝:
 //   if(_alias[cc]!="")  c = &((_alias[cc])[0]);
 
 //   借助string的深拷貝賦值完成對hash內(nèi)數(shù)據(jù)的深拷貝:
   if(_alias[cc]!=""){
       hash_cp =_alias[cc];
       c=&(hash_cp[0]);
 
   }  
  argv[argc++]=strtok(c,sp);//此處c這個指針將會隨之變化最后分割結(jié)束為null
   while(argv[argc++]=strtok(NULL,sp)){}
     argc--;
 
   return true;
}

這里我們先暫時忽略對alias重命名的內(nèi)建命令的設(shè)計(后面會談到) 。

七、 內(nèi)建命令判斷:

下面我們把整體框架展示一下:

當(dāng)我們在main函數(shù)主體內(nèi)分析是不是內(nèi)建命令;如果是內(nèi)建命令那么就直接由main這個進(jìn)程執(zhí)行完然后直接開始下一層循環(huán),就不往下走了;否則就走我們的execute函數(shù)。

//內(nèi)建命令:和execute執(zhí)行是分開的
bool checkinkeycmd()
{
    std::string cmd = argv[0];
    if(cmd == "cd")
    {
       // printf("cd進(jìn)入!\n");
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
     Echo();
        return true;
    }
    else if(cmd == "export")
    {
        //
    }
    else if(cmd == "alias"&&argc>=2)
    {
     //
    }
 
    return false;
}

下面我們分四個部分來對相關(guān)內(nèi)建命令進(jìn)行單獨處理: 

7.1 cd:

這里cd其實就是change directory;它完成的操作其實就是幫我們改變目錄;但是我們另外讓它把我們對應(yīng)的環(huán)境變量表也給改變;其實就要操作我們所維護(hù)的那個env數(shù)組了。

下面我們就不對應(yīng)把cd 的相關(guān)都實現(xiàn)一遍;大概實現(xiàn)常用的這幾個:

注意:這里我們?yōu)榱丝梢詫崿F(xiàn)cd -:也就是會定義好變量保存上一次訪問的目錄;方便回去;故當(dāng)每次chdir都會保存一下;并改變env表中的pwd

這里我們用的是string;也就是利用了它可以被const類型的字符串初始化;也可以通過c_str完成對應(yīng)轉(zhuǎn)換。 

7.1.1 cd :

這里單純的cd也就是只有命令無參數(shù)此時argc=1;故直接跳到家目錄:

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

保存原先目錄位置然后改變再覆蓋env對應(yīng)pwd即可:

if(argc == 1)
    //直接返回到家目錄,但是此時沒有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記  借助string完成了否則對返回const多次覆蓋保存較為麻煩
    {
        std::string home = gethome();
        if(home.empty()) return false;
       std::string tmp=getpwd();
                lastpwd=tmp;
 
        chdir(home.c_str());
           getpwd();
            return true;
    }

下面我們保存第二個參數(shù):

std::string where = argv[1];

效果展示:

7.1.2 cd -:

 if(where=="-") //上一個工作目錄
        {   // std::cout<<lastpwd<<std::endl;
 
            chdir(lastpwd.c_str());//這里的lastpwd是我們在新切換目錄更改env前記錄的;故是先前的pwd
              getpwd();
 
        }

效果展示:

7.1.3 cd / :

 else if(where=="/"){
             std::string tmp=getpwd();
                lastpwd=tmp;
                chdir("/");
                getpwd();
 
            }

效果展示:

7.1.4 cd ~:

這里分為普通用戶還是root:普通用戶是家目錄而root就是登機(jī)目錄了:

if(!strcmp(getuser(),"root")){ 
                 std::string tmp=getpwd();
                lastpwd=tmp;
 
                chdir("/root");
                getpwd();
            }
            else {
               std::string home = gethome();
               std::string tmp=getpwd();
                lastpwd=tmp;
 
                  chdir(home.c_str());
                  getpwd();
            }

效果展示: 

7.1.5 cd +dirname:

    else
        {     std::string tmp=getpwd();
                lastpwd=tmp;
            // std::cout<<lastpwd<<std::endl;
             chdir(where.c_str());
             getpwd();
        }

演示效果:

7.2 echo:

這里我們分為 echo $?;echo $+環(huán)境變量:

全局變量lastcode保存上次的子進(jìn)程退出碼;方便下一次打?。?/p>

對echo $?我們規(guī)定只要走了子進(jìn)程就會返回1;比如內(nèi)建命令等就返回0。 

void Echo(){
//echo $? echo $PATH
    if(argc==2){
   std::string func=argv[1];
   if(func=="$?"){
        std::cout << lastcode << std::endl;
         lastcode = 0;
   }
   else if(func[0]=='$'){
       std::string envname = func.substr(1);
        const char * envvalue= getenv(envname.c_str());
            if(envvalue)
                std::cout << envvalue << std::endl;
   }
     else
        {
            std::cout << func << std::endl;
        }
     
}
}

演示效果:

7.3 export:

這里我們只需在我們維護(hù)的env數(shù)組多開一個空間然后把我們要導(dǎo)入的串記錄一下完成深拷貝(注意最后一個置空):

   void Export(){
      env_str=argv[1];
        env[envs]=(char*)calloc(strlen(argv[1])+1,1);
            for(int i=0;i<env_str.size();i++)env[envs][i]=env_str[i];
            envs++;
            env[envs]=NULL;
}

效果展示: 

當(dāng)我們退出后重新進(jìn)入:

發(fā)現(xiàn)沒了;符合我們的預(yù)期。 

7.4 alias:

首先我們先認(rèn)識一下linux中的alias也就是起別名:

查看別名表:

 起別名(alias 別名=原名):

使用效果:

刪除別名(unalias+別名):

這里比如我們的ll就是alias起別名來的;下面我們就來模擬實現(xiàn)一下(簡單版本的alias)。 

這里用到了映射,故我們采用了哈希表;

全局變量:

cur,pre是分別是別名和原名 ;

hash_cp是命令行分析過程的對hash表內(nèi)取得值的一個深拷貝;反之strtok函數(shù)破壞了;導(dǎo)致再次使用這個別名就會出現(xiàn)找原名時候被破壞的結(jié)果。

封裝Alias函數(shù):

void Alias(){
 
     std::string sec=argv[1];
      auto pos=sec.find('=');
        cur=sec.substr(0,pos);
       pre=sec.substr(pos+1,std::string::npos);
       for(int i=2;i<argc;i++){
           pre+=" ";
           pre+=argv[i];
       }
      _alias[cur]=pre;
 
   
       }

對語義分析部分修改:

  argc=0;
   std::string cc=c;
 
 //  淺拷貝:
 //   if(_alias[cc]!="")  c = &((_alias[cc])[0]);
 
 //   借助string的深拷貝賦值完成對hash內(nèi)數(shù)據(jù)的深拷貝:
   if(_alias[cc]!=""){
       hash_cp =_alias[cc];
       c=&(hash_cp[0]);
 
   }  
  
      argv[argc++]=strtok(c,sp);//此處c這個指針將會隨之變化最后分割結(jié)束為null
   while(argv[argc++]=strtok(NULL,sp)){}
     argc--;
  
   return true;

效果展示:

八、子進(jìn)程執(zhí)行操作:

main函數(shù)的進(jìn)程fork后讓子進(jìn)程得到相關(guān)指令和參數(shù)并用exec系列函數(shù)進(jìn)行程序替換(這里選用的execvp):

然后父進(jìn)程阻塞等待回收資源和相關(guān)信息:

int  execute(){
  pid_t pid=fork();
  if(pid==0){
    //printf("argv[0]: %s\n", argv[0]);
    
      if(status==INPUT_RE){
          int fd=open(filename.c_str(),O_RDONLY);
          if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
      }
 
      else if(status==OUTPUT_RE){
           int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
      }
          else if(status==APPPUT_RE){
          int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
 
          }
      else { }//NOPUT_RE無重定向操作
      
   execvp(argv[0],argv);
   exit(1);
  }
  int status=0;
  pid_t id=waitpid(pid,&status,0);
  if(id > 0)
    {
        lastcode = WEXITSTATUS(status);//對于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0
    }
    return 0;
   
 
}

九、myshell代碼匯總:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<unordered_map>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
extern char**environ;
#define CS 1024//命令行提示符最大值
#define FT "my simulate shell:%s@%s %s# "http://snprintf的format最大值
#define sp " "http://space
#define MC 128//命令行參數(shù)最大值
 
 
 
//這里模擬了bash的兩張表,而不是main直接繼承下(改變environ指針)而是重新布置了一下數(shù)組,讓environ指針指向我們所布置的數(shù)組。
// 1. 命令行參數(shù)表
#define MAXARGC 128
char *argv[MAXARGC];
int argc = 0;
 
// 2. 環(huán)境變量表
#define MAX_ENVS 100
char *env[MAX_ENVS];
int envs = 0;
 
char cwd[1024];
char cwdenv[1029];
 //char *lastpwd=(char*)calloc(1024,1);
std::string lastpwd;
int lastcode=0;
 
//char export_env[1024];
std::string env_str;
//對alias的適用:
std::unordered_map<std::string,std::string>_alias;
std::string cur,pre;
std::string hash_cp;
 
//重定向:
std::string filename;
#define NOPUT_RE 0
#define INPUT_RE 1
#define OUTPUT_RE 2
#define APPPUT_RE 3
int status;//重定向方式
 
void initenv(){
    memset(env, 0, sizeof(env));
    envs= 0;
 
    for(int i=0;environ[i];i++){
        env[i]=(char*)malloc(strlen(environ[i])+1);//這里模擬的也可以不開空間,直接棧上
        strcpy(env[i], environ[i]);
        envs++;
    }
        env[envs] = NULL;
 
    
    // for(int i = 0; env[i]; i++)
   // {
   //     putenv(env[i]);
   // }
    environ = env;//用自己生成的env表
}
 
 //獲取一些環(huán)境變量:
const char* getuser(){
    const char*u=getenv("USER");
    return u==NULL?"NONE":u;
}
 
const char* gethostname(){
    const char*h=getenv("HOSTNAME");
    return h==NULL?"NONE":h;
}
 
//const char* getpwd(){
//    const char*p=getenv("PWD");
//    return p==NULL?"NONE":p;
//}
const char *gethome()
{
    const char *home = getenv("HOME");
    return home == NULL ? "" : home;
}
//這里獲得環(huán)境變量和其他上面不同;因為當(dāng)我們通過chdir改變當(dāng)前目錄的時候它在環(huán)境變量中的記錄(真正的bash實現(xiàn)了)而我們沒有實現(xiàn),因此我們
//可以通過getcwd每次調(diào)完新的目錄開始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時候變化
const  char *getpwd(){
        char *pwd = getcwd(cwd, sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        putenv(cwdenv);//put進(jìn)去的是環(huán)境變量的地址也就是一個指針指向的是那段量,因此指針要么是全局要么在堆上
    }
    return  pwd==NULL?"NONE":pwd;
}
//const char *getpwd(){
//    // 調(diào)用了這個,在Getpwd中
//    return Getpwd();
//}
//打印命令行提示符:把pwd的最后一個名稱得到
std::string convert(const char *pwd){
    std::string s=pwd;
  auto pos=s.rfind('/');
  if(pos==std::string::npos)return "error";
  if(pos==s.size()-1)return "/";
  else return s.substr(pos+1);
 
 
  
}
void ptcmdprompt(){
    char p[CS]={0};
    snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() );
    printf("%s",p);
    //無\n及時刷新標(biāo)準(zhǔn)輸出緩沖區(qū)
    fflush(stdout);
}
//獲得命令行參數(shù):
bool gtcmdline(char *p,int size){
  char *j=fgets(p,size,stdin);
 // printf("參數(shù)是:%d",size);
  if(j==NULL)return false;
  //printf("%s\n",p);
  p[strlen(p)-1]=0;//干掉\n
  if(strlen(p)==0) return false;
  else return true;
}
//命令行解釋:把輸入的命令行參數(shù)分出來方便后序傳給要調(diào)用的main的argv
bool  cmdparse(char *c){
   // printf("%s\n",c);
    argc=0;
   std::string cc=c;
 //  std::cout<<"內(nèi)容:"<<_alias[cc]<<std::endl;
 //  淺拷貝:
 //   if(_alias[cc]!="")  c = &((_alias[cc])[0]);
 
 //   借助string的深拷貝賦值完成對hash內(nèi)數(shù)據(jù)的深拷貝:
   if(_alias[cc]!=""){
       hash_cp =_alias[cc];
       c=&(hash_cp[0]);
 
   }  
   //printf("%s\n",c);
      argv[argc++]=strtok(c,sp);//此處c這個指針將會隨之變化最后分割結(jié)束為null
   while(argv[argc++]=strtok(NULL,sp)){}
     argc--;
   //printf("%s\n%s\n",argv[0],argv[1]);
  // printf("%s%s\n",argv[0],argv[1]);
   return true;
}
 
//void lastpwd(){
//   // printf("11111111111111111");
// //  printf(" %s%d\n ",argv[0],argc);
//   if(!strcmp(argv[0],"cd")&&argc==2){
//        // printf("執(zhí)行\(zhòng)n");
//        std::string s("LASTPWD");
//        s+="=";
//        s+=argv[1];
//        //std::cout<<s<<std::endl;
//        // char p[s.size()+1]={0};
//        char* p = (char*)calloc(s.size() + 1, 1);
//        for(int i=0;i<s.size();i++) p[i]=s[i];
//        printf("huanbian:%s\n",p);
//        environ[envs]=(char*)calloc(strlen(p)+1,1);
//        putenv(p);
//   }
//}
bool Cd(){
if(argc == 1)
    //直接返回到家目錄,但是此時沒有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記  借助string完成了否則對返回const多次覆蓋保存較為麻煩
    {
        std::string home = gethome();
        if(home.empty()) return false;
       std::string tmp=getpwd();
                lastpwd=tmp;
 
        chdir(home.c_str());
           getpwd();
            return true;
    }
else{
     std::string where = argv[1];
    // printf("%s\n",argv[1]);
        // cd - / cd ~
        if(where=="-") //上一個工作目錄
        {   // std::cout<<lastpwd<<std::endl;
 
            chdir(lastpwd.c_str());//這里的lastpwd是我們在新切換目錄更改env前記錄的;故是先前的pwd
              getpwd();
 
        }
            
          else if(where=="/"){
             std::string tmp=getpwd();
                lastpwd=tmp;
                chdir("/");
                getpwd();
 
            }
            else if(where=="~")//家目錄
        {
            if(!strcmp(getuser(),"root")){ 
                 std::string tmp=getpwd();
                lastpwd=tmp;
 
                chdir("~");
                getpwd();
            }
            else {
               std::string home = gethome();
               std::string tmp=getpwd();
                lastpwd=tmp;
 
                  chdir(home.c_str());
                  getpwd();
            }
        }
 
 //  else if(where==".."){} 上級目錄
         
        else
        {     std::string tmp=getpwd();
                lastpwd=tmp;
            // std::cout<<lastpwd<<std::endl;
             chdir(where.c_str());
             getpwd();
        }
           return true;
 
       }
 
}
void Echo(){
//echo $? echo $PATH
    if(argc==2){
   std::string func=argv[1];
   if(func=="$?"){
        std::cout << lastcode << std::endl;
         lastcode = 0;
   }
   else if(func[0]=='$'){
       std::string envname = func.substr(1);
        const char * envvalue= getenv(envname.c_str());
            if(envvalue)
                std::cout << envvalue << std::endl;
   }
     else
        {
            std::cout << func << std::endl;
        }
     
}
}
void Export(){
   env_str=argv[1];
        env[envs]=(char*)calloc(strlen(argv[1])+1,1);
            for(int i=0;i<env_str.size();i++)env[envs][i]=env_str[i];
            envs++;
            env[envs]=NULL;
 
}
void Alias(){
 std::string sec=argv[1];
      auto pos=sec.find('=');
        cur=sec.substr(0,pos);
       pre=sec.substr(pos+1,std::string::npos);
       for(int i=2;i<argc;i++){
           pre+=" ";
           pre+=argv[i];
       }
      _alias[cur]=pre;
 
}
//分子進(jìn)程執(zhí)行:
int  execute(){
  pid_t pid=fork();
  if(pid==0){
    //printf("argv[0]: %s\n", argv[0]);
    
      if(status==INPUT_RE){
          int fd=open(filename.c_str(),O_RDONLY);
          if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
      }
 
      else if(status==OUTPUT_RE){
           int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
      }
          else if(status==APPPUT_RE){
          int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
 
          }
      else { }//NOPUT_RE無重定向操作
      
   execvp(argv[0],argv);
   exit(1);
  }
  int status=0;
  pid_t id=waitpid(pid,&status,0);
  if(id > 0)
    {
        lastcode = WEXITSTATUS(status);//對于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0
    }
    return 0;
   
 
}
//內(nèi)建命令:和execute執(zhí)行是分開的
bool checkinkeycmd()
{
    std::string cmd = argv[0];
    if(cmd == "cd")
    {
       // printf("cd進(jìn)入!\n");
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
     Echo();
        return true;
    }
    else if(cmd == "export")
    {
         Export();
    }
    else if(cmd == "alias"&&argc>=2)
    {
       Alias();
    }
 
    return false;
}
 
void eliminatesp(char cl[],int &fg){
    while(isspace(cl[fg])) fg++;
    
}
void redirect(char cl[]){
      status=NOPUT_RE;
      int start=0;
      int end=strlen(cl)-1;
      while(start<end){
          if(cl[end]=='<'){
              cl[end++] = 0;
            eliminatesp(cl, end);
            status = INPUT_RE;
            filename = cl+end;
            break;
          }
          else if(cl[end]=='>'){
              if(cl[end-1]=='>'){
                 status=APPPUT_RE;
                 cl[end-1]=0;
              }
              else{
                  status=OUTPUT_RE;
                  cl[end]=0;
              }
             end++;
             eliminatesp(cl,end);
              filename = cl+end;
                 break;
          }
          else{
              end--;
          }
      }
    
}
void destroy(){
    for(int i=0;env[i];i++){
        free(env[i]);
    }
}
 
int main() {
    //自己的環(huán)境變量和命令行參數(shù)表的初始化:
    initenv();
 
    while(1) {
        //命令提示行打?。?
        ptcmdprompt();
 
        char cl[CS]={0};
        //把命令參數(shù)輸入到cl
        while(!gtcmdline(cl,sizeof(cl))){}
 
       redirect(cl);
 
       //把命令參數(shù)這個串拆解到argv里:
        cmdparse(cl);
     //判斷是否是內(nèi)建命令由bash自己完成(這里模擬的是main自己執(zhí)行)
        if(checkinkeycmd()) {
           // lastpwd();
            continue;
        } 
        execute();
    }
    //銷毀表所開辟的空間
    destroy();
}

目前功能比較基本,會不斷補(bǔ)充;感謝支持??! !

以上就是Linux命令行解釋器的模擬實現(xiàn)過程的詳細(xì)內(nèi)容,更多關(guān)于Linux命令行解釋器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 利用Linux防火墻隔離本地欺騙地址的方法詳解

    利用Linux防火墻隔離本地欺騙地址的方法詳解

    防火墻,其實說白了講,就是用于實現(xiàn)Linux下訪問控制的功能的,它分為硬件的或者軟件的防火墻兩種。下面這篇文章主要給大家介紹了關(guān)于如何利用Linux防火墻隔離本地欺騙地址的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2018-05-05
  • Linux中g(shù)it用https連接時不用每次輸入密碼的方法

    Linux中g(shù)it用https連接時不用每次輸入密碼的方法

    這篇文章主要給大家介紹了關(guān)于Linux中g(shù)it使用https連接時不用每次輸入密碼的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • linux解決ping通但端口不通的問題

    linux解決ping通但端口不通的問題

    在本文里我們給大家整理了關(guān)于在linux解決ping通但端口不通的問題的解決方法和步驟,有需要的朋友們參考下。
    2018-09-09
  • 詳解Linux安裝教程

    詳解Linux安裝教程

    這篇文章主要介紹了Linux安裝教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • linux系統(tǒng)下MongoDB單節(jié)點安裝教程

    linux系統(tǒng)下MongoDB單節(jié)點安裝教程

    這篇文章主要給大家介紹了在linux系統(tǒng)下mongo在單節(jié)點安裝的方法教程,文中將實現(xiàn)的方法一步步介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起看看吧。
    2017-10-10
  • Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題

    Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題

    這篇文章主要介紹了Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • apache性能測試工具ab使用詳解

    apache性能測試工具ab使用詳解

    這篇文章主要介紹了apache性能測試工具ab使用詳解,需要的朋友可以參考下
    2015-01-01
  • Linux快速生成大文件方式

    Linux快速生成大文件方式

    這篇文章主要介紹了Linux快速生成大文件方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Linux服務(wù)器掛載新硬盤方式

    Linux服務(wù)器掛載新硬盤方式

    本文詳細(xì)記錄了在一臺主機(jī)上插上一塊8T硬盤后遇到的問題及解決過程,初始問題包括無法重啟和/etc/fstab文件配置錯誤,通過手動掛載、查看文件系統(tǒng)類型以及系統(tǒng)日志,最終確定是插槽順序問題導(dǎo)致硬盤名稱混亂,通過調(diào)整/etc/fstab文件中的掛載分區(qū)和文件系統(tǒng)類型
    2025-02-02
  • Linux文件目錄結(jié)構(gòu)(小白版)

    Linux文件目錄結(jié)構(gòu)(小白版)

    這篇文章主要介紹了Linux文件目錄結(jié)構(gòu)(小白版),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10

最新評論