欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

一、整體框架:

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

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

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

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

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

這里我們自己開(kāi)了兩個(gè)數(shù)組來(lái)模擬這兩張表;也就是拷貝父bash的那兩種表拷貝過(guò)來(lái)(簡(jiǎn)單模擬一下)這倆張表的內(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);//這里模擬的也可以不開(kāi)空間,直接棧上
        strcpy(env[i], environ[i]);
        envs++;
    }
        env[envs] = NULL;
 
    
    // for(int i = 0; env[i]; i++)
   // {
   //     putenv(env[i]);
   // }
    environ = env;//用自己生成的env表
}

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

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

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

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

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

我們此刻需要替換掉%s的就是通過(guò)環(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;
}

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

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

//這里獲得環(huán)境變量和其他上面不同;因?yàn)楫?dāng)我們通過(guò)chdir改變當(dāng)前目錄的時(shí)候它在環(huán)境變量中的記錄(真正的bash實(shí)現(xiàn)了)而我們沒(méi)有實(shí)現(xiàn),因此我們
//可以通過(guò)getcwd每次調(diào)完新的目錄開(kāi)始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時(shí)候變化
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)境變量的地址也就是一個(gè)指針指向的是那段量,因此指針要么是全局要么在堆上
    }
    return  pwd==NULL?"NONE":pwd;
}

普及一下用到的getcwd:

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

這里我們?cè)僮鲆粋€(gè)小優(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對(duì)象的c_str接口就好;

這里順便說(shuō)一下;因?yàn)楹竺婧芏喽加玫竭@一點(diǎn):就是經(jīng)常操作的時(shí)候把char串變成string然后調(diào)用它的c_str()為了方便;以及后面很多要注意作用域:因此考慮了全局設(shè)計(jì)。 

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

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

下面展示下效果: 

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

這里邏輯比較簡(jiǎn)單就是把我們輸入的字符讀入然后把后面的\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個(gè)到串s中;讀取成功返回這個(gè)s;否則返回NULL。

五、重定向判斷:

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

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

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

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

標(biāo)識(shí)符的;這樣然后利用覆蓋0的操作來(lái)完成前方命令的截?cái)啵号袛囗樞颍?lt;  >  >>;其次就是它可能文件前面存在空格;故我們?cè)贅?gòu)建一個(gè)去除空格函數(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)程來(lái)執(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無(wú)重定向操作

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

六、語(yǔ)義分析:

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

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

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

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

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

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

//內(nèi)建命令:和execute執(zhí)行是分開(kāi)的
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;
}

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

7.1 cd:

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

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

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

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

7.1.1 cd :

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

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

保存原先目錄位置然后改變?cè)俑采wenv對(duì)應(yīng)pwd即可:

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

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

std::string where = argv[1];

效果展示:

7.1.2 cd -:

 if(where=="-") //上一個(gè)工作目錄
        {   // std::cout<<lastpwd<<std::endl;
 
            chdir(lastpwd.c_str());//這里的lastpwd是我們?cè)谛虑袚Q目錄更改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>

對(duì)echo $?我們規(guī)定只要走了子進(jìn)程就會(huì)返回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ù)組多開(kāi)一個(gè)空間然后把我們要導(dǎo)入的串記錄一下完成深拷貝(注意最后一個(gè)置空):

   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)沒(méi)了;符合我們的預(yù)期。 

7.4 alias:

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

查看別名表:

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

使用效果:

刪除別名(unalias+別名):

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

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

全局變量:

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

hash_cp是命令行分析過(guò)程的對(duì)hash表內(nèi)取得值的一個(gè)深拷貝;反之strtok函數(shù)破壞了;導(dǎo)致再次使用這個(gè)別名就會(huì)出現(xiàn)找原名時(shí)候被破壞的結(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;
 
   
       }

對(duì)語(yǔ)義分析部分修改:

  argc=0;
   std::string cc=c;
 
 //  淺拷貝:
 //   if(_alias[cc]!="")  c = &((_alias[cc])[0]);
 
 //   借助string的深拷貝賦值完成對(duì)hash內(nèi)數(shù)據(jù)的深拷貝:
   if(_alias[cc]!=""){
       hash_cp =_alias[cc];
       c=&(hash_cp[0]);
 
   }  
  
      argv[argc++]=strtok(c,sp);//此處c這個(gè)指針將會(huì)隨之變化最后分割結(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無(wú)重定向操作
      
   execvp(argv[0],argv);
   exit(1);
  }
  int status=0;
  pid_t id=waitpid(pid,&status,0);
  if(id > 0)
    {
        lastcode = WEXITSTATUS(status);//對(duì)于這里規(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;
//對(duì)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);//這里模擬的也可以不開(kāi)空間,直接棧上
        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)境變量和其他上面不同;因?yàn)楫?dāng)我們通過(guò)chdir改變當(dāng)前目錄的時(shí)候它在環(huán)境變量中的記錄(真正的bash實(shí)現(xiàn)了)而我們沒(méi)有實(shí)現(xiàn),因此我們
//可以通過(guò)getcwd每次調(diào)完新的目錄開(kāi)始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時(shí)候變化
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)境變量的地址也就是一個(gè)指針指向的是那段量,因此指針要么是全局要么在堆上
    }
    return  pwd==NULL?"NONE":pwd;
}
//const char *getpwd(){
//    // 調(diào)用了這個(gè),在Getpwd中
//    return Getpwd();
//}
//打印命令行提示符:把pwd的最后一個(gè)名稱得到
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);
    //無(wú)\n及時(shí)刷新標(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ù)分出來(lái)方便后序傳給要調(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的深拷貝賦值完成對(duì)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這個(gè)指針將會(huì)隨之變化最后分割結(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)
    //直接返回到家目錄,但是此時(shí)沒(méi)有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記  借助string完成了否則對(duì)返回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=="-") //上一個(gè)工作目錄
        {   // std::cout<<lastpwd<<std::endl;
 
            chdir(lastpwd.c_str());//這里的lastpwd是我們?cè)谛虑袚Q目錄更改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==".."){} 上級(jí)目錄
         
        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無(wú)重定向操作
      
   execvp(argv[0],argv);
   exit(1);
  }
  int status=0;
  pid_t id=waitpid(pid,&status,0);
  if(id > 0)
    {
        lastcode = WEXITSTATUS(status);//對(duì)于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0
    }
    return 0;
   
 
}
//內(nèi)建命令:和execute執(zhí)行是分開(kāi)的
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ù)這個(gè)串拆解到argv里:
        cmdparse(cl);
     //判斷是否是內(nèi)建命令由bash自己完成(這里模擬的是main自己執(zhí)行)
        if(checkinkeycmd()) {
           // lastpwd();
            continue;
        } 
        execute();
    }
    //銷毀表所開(kāi)辟的空間
    destroy();
}

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

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

相關(guān)文章

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

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

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

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

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

    linux解決ping通但端口不通的問(wèn)題

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

    詳解Linux安裝教程

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

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

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

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

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

    apache性能測(cè)試工具ab使用詳解

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

    Linux快速生成大文件方式

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

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

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

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

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

最新評(píng)論