LINUX SHELL 設計實現

flaty 12年前發布 | 42K 次閱讀 Linux 自個寫的一個簡單的LINUX SHELL

沒啥可說的,直接貼原碼:
LINUX的作業。嘿。

設計并實現一個Linux下的的交互式shell命令解釋器:MyShell;以模擬unix/linux系統下的完善的bash shell的功能。不需要具備非常完備的shell功能。主要有如下功能:

<!--[if !supportLists]-->l        <!--[endif]-->初始化環境,主要是默認搜索路徑、history循環數組、作業列表

<!--[if !supportLists]-->l        <!--[endif]-->打印命令提示符;

<!--[if !supportLists]-->l        <!--[endif]-->接受、識別和分析命令提示符后輸入的命令行,并創建子程序,在子程序中將其轉換為相對的系統調用以調用內核功能實現任務;

<!--[if !supportLists]-->l        <!--[endif]-->能實現內部命令,包括:exit(退出shell)、historycdjobsbgfg

<!--[if !supportLists]-->l        <!--[endif]-->能夠執行外部命令,并到搜索路徑中去查找外部命令對應的執行文件;命令均可帶參數;

<!--[if !supportLists]-->l        <!--[endif]-->支持&|<>四個特殊字符及其所代表的功能;

<!--[if !supportLists]-->l        <!--[endif]-->支持前、后臺作業控制,包括fg/bg/jobs以及ctrl+c/ctrl+z(掛起、中止和繼續運行)。

#include <stdio.h>

include <unistd.h>

include <stdlib.h>

include <sys/wait.h>

include <string.h>

include <signal.h>

include <sys/types.h>

include<unistd.h>

include<stdio.h>

include<stdlib.h>

include<fcntl.h>

include<string.h>

include <errno.h>

define MAXLEN 128 //命令最大長度

define HIS_LEN 10 //歷史記錄最大長度

typedef struct history //歷史記錄結點

{

int i;
int start;

int end;

char cmd[HIS_LEN][MAXLEN];

} history;

typedef struct node{ //作業鏈表結點

pid_t pid;



char cmd[100];



char state[10];



struct node *next;



}node;

/**聲明全局變量*/

node head,end; //jobs 鏈表

history his; //history 隊列

char buf[MAXLEN]; //保存輸入命令

char *path[10]; //保存多個環境變量.--pATH

char path_cmd[50]; //保存輸入命令的絕對路

int path_sum; //PATH環境變量個數。

char work_path[100]; //保存當前工作路徑

int jobs_count=0; //當前工作數量

pid_t pid; //子進程PID

char *argv[10]; //根據空格分解命令的字符指針數組

int cmd_count=0; //歷史記錄個數

int stop_flag=0;

//-------------------------------------聲明函數----------------------------------------

void print_work_path(); // 打印SHELL 提示符

void print_string(char ); // 打印字符串 int read_commands(char ); // 讀取命令

void init_envi(); // 初始化環境

void analyse_cmd(char**); // 分析命令,并根據空格分解之

int cmd_addpath(char *); // 補全命令

void my_cd(char* ); // CD 命令

void my_history(char *); // HISTORY 命令

void save_history(); // 保存輸入命令歷史

void my_jobs(); // JOBS查看

void my_fg(char ch,sigset_t son_set); // FG命令

void my_bg(char *); // BG命令

void add_node(int,char,char ); // 增加作業結點命令

void father_sig_hander(int ); // 父進程信號處理函數

void reDirect(char buf); // 管道重定向 char instru_divide(char ch); void aru_divide(char instru, char **ch);

//-----------------main-----------------

main (){

int is_bg;      //后臺命令標記,后臺=0,否則為1。







init_envi();    //初始化環境



//-------------信號處理---------------





    sigset_t son_set;
            //設置父進程信號處理函數
    struct sigaction father_pact;
    father_pact.sa_handler=father_sig_hander;   



    sigemptyset(&son_set);          //設置子進程屏蔽信號集


    sigaddset(&son_set,SIGTSTP);

    sigaddset(&son_set,SIGINT);



//-------------接收命令---------------

while(1){

    sigprocmask(SIG_BLOCK,&son_set,NULL);
    //pid=0;
    stop_flag=0;
    waitpid(-1,NULL,WNOHANG);       //回收僵尸進程




    signal(SIGINT,father_sig_hander);
//為父進程安裝信號處理函數      
    sigaction(SIGTSTP,&father_pact,NULL);           




    is_bg=0;    


    print_work_path();      //打印命令提示符



    if (!read_commands(buf))    //不處理空格等無用命令, 并去除命令前后空格!

        continue;



    save_history(his);  //保存命令歷史




    int flag=cmd_match(buf);
    if(flag==1){



//----------管道重定向相關---------

        reDirect(buf);
        continue;

    }       

    else if(flag==2){   

        is_bg=1; 


    }





    analyse_cmd(argv);//解析命令,根據空格分開。




//----------------------------執行內部命令------------------------

            if(!strcmp("exit",argv[0])&&argv[1]==NULL)

            {   puts("goodbye!");exit(0);}

            else if(!strcmp("cd",argv[0])){         //------cd

                my_cd(argv[1]);

                continue;

            }

            else if(!strcmp("history",argv[0])){    //-------history

                my_history(argv[1]);

                continue;

            }

            else if(!strcmp("jobs",argv[0])){       //--------jobs

                my_jobs();

                continue;

            }

            else if(!strcmp("fg",argv[0])){         //--------fg

                my_fg(argv[1],&son_set);

                continue;

            }

            else if(!strcmp("bg",argv[0])){         //--------bg

                my_bg(argv[1]);

                continue;

            }




    //------------根據pATH補全外部命令,包括當前路徑---------

            if(cmd_addpath(argv[0])==0){



                strcpy(path_cmd,argv[0]);   //如果在PATH沒找到,把命令視為絕對路徑,直接執行!

                                            //

            }



//------------------執行外部命令------------------------





            if((pid=fork())<0)

                perror("execute fail!!");



    //-------------子進程--------------

            else if(pid==0)

            {   


                if(execv(path_cmd,argv)==-1)                        

                        puts("cmd not found!!");





            }



    //--------------父進程 ----------------

            else

            {   

                sigprocmask(SIG_UNBLOCK,&son_set,NULL);

                if(is_bg==1)

                    add_node(pid,argv[0],"running");

                else{ 



                    waitpid(pid,NULL,0);



                }

            }



        }//while





}

//-------------main 結束--------------------------

//--------------初始化環境-----------------------

void init_envi(){

FILE *fp;

char ch,temp[100],*p;

int i=0;

his.end=his.start=0;
jobs_count=0;

stop_flag=0;

head=end=NULL;

if((fp=fopen("./test_profile","r"))==0)

    {

         perror("init path failure!! exit!!");

        exit(1);

 }

while((ch=fgetc(fp))!=EOF)  temp[i++]=ch;


    temp[i]=0;  


    i=0;

while(temp[i]&&temp[i]!='=') ++i;


strcpy(temp,temp+i+1);      


char *delim = ":";

p=strtok(temp, delim);


path[0]=(char *)malloc(strlen(p)+1);


strcpy(path[0],p);


i=1;

while((p=strtok( NULL, delim))!=NULL) 

  {     path[i]=(char *)malloc(strlen(p)+1);


        strcpy(path[i],p);      


        i++;


    }


    path_sum=i-1;


}

//------------命令匹配--------------------------------------------------------

int cmd_match(char *buf){

char *ch;

if(strchr( buf, '|')!=NULL||strchr( buf, '<')!=NULL||strchr( buf, '>')!=NULL)

    return 1;

else if((ch=strchr( buf, '&'))!=NULL)

{   *ch=0 ; return 2;}



}

//------------打印命令提示--------------------------------

void print_work_path() {

char * user = getlogin();

getcwd(work_path,sizeof(work_path));


printf("<");

print_string(user);

printf("@ ");

print_string(work_path);

printf(">$");

}

//------------讀取輸入命令------------------------

int read_commands(char * buf)

{

int i=0;char ch;


//printf("%s",buf);

//gets(buf);


ch=getchar();


if(ch=='\n') return 0;

buf[i++]=ch;
while((ch=getchar())!='\n') buf[i++]=ch;
buf[i]=0;

//puts(buf); i=0;

while(buf[i]==' '||buf[i]=='\t'&&buf[i]) ++i;

if(buf[i]==0) 
    return 0;



else {
    i=0;

    while(buf[i]==' '||buf[i]=='\t') ++i;//去除前面空格

    strcpy(buf,buf+i);      

// int j=strlen(buf)-1; // while(buf[j]==' '||buf[j]=='\t') j--; // buf[j+1]=0; //puts(buf); //printf("%d",strlen(buf)); return 1;

}

}

//----------------------解析命令--------------------

void analyse_cmd(char **argv){

int i=1;

char *delim = "' ','\t'";

argv[0]=strtok(buf, delim);

//puts(argv[0]);

while((argv[i]=strtok( NULL, delim))!=NULL) 

        i++;



argv[i]=NULL;





}

//---------------------補全命令-------------------

int cmd_addpath(char * argv){

int i=0;

//char 

//puts("!!!!");

//printf("%d\n",path_sum); strcpy(path_cmd,work_path);//查找當前路徑并補全命令

strcat(path_cmd,"/");

    strcat(path_cmd,argv);

//puts(path_cmd);

if(!access(path_cmd,F_OK))

    return 1;
while(i<path_sum){       //查找pATH并補全命令

    strcpy(path_cmd,path[i]);

    strcat(path_cmd,"/");





    strcat(path_cmd,argv);

//puts(path_cmd);

    if(access(path_cmd,F_OK)==0)

        return 1;
    i++;

}




return 0;

}

//---------------常用函數--------------

void print_string(char *p){

printf("%s",p);



}

//---------------內部命令--------------------------------------------

//------------save history------------------

void save_history(){

cmd_count++;

his.start%=HIS_LEN;

his.end%=HIS_LEN;                           

strcpy(his.cmd[his.end++],buf);


if((his.end)%HIS_LEN==his.start)


    his.start++;    


}

//---------------------cd-------------------------

void my_cd(char * para){

if(para==NULL)

        chdir("/root");


    else if(para!=NULL&&chdir(para)==-1)


        perror("dir not exist!!n");




}

//----------------------history----------------------------

void my_history(char * argv) {

int i=0,len,j;

if(cmd_count<HIS_LEN) len=cmd_count;

else    len=HIS_LEN;


j=len;

if(argv==NULL){


    for(i=his.start;i<his.start+len;i++)

    printf("%d \t%s\n",j--,his.cmd[i%HIS_LEN]);


}



}

//--------------------作業控制相關-------------------------------

void my_jobs()

{ int i=1;

if(head==NULL) {printf("no jobs!!\n");return;}
node *p=head;

while(p->next!=NULL)

{

    printf("[%d]- %d  %s \t%s\n",i++,p->pid,p->state,p->cmd);

    p=p->next;

}

printf("[%d]+ %d  %s  \t%s\n",i,p->pid,p->state,p->cmd);

}

//-------------------------fg-----------------------

void my_fg(char ch,sigset_t son_set){

sigprocmask(SIG_UNBLOCK,son_set,NULL);

if(head==NULL){                 //無作業
    printf("no  job!!\n");
    return;

}
if(ch==NULL){           //
    puts("para missing!");
}
else {              //作業放到前臺運行

    int i=atoi(ch),j=1;


    node *p=head,*q;



if(i>2){ while(p->next!=NULL&&j<i-1) { p=p->next; j++;}

        if(p->next==NULL) {
            printf("no such job!!\n");
            return;
        }

    }else {

        if(i==2&&p->next==NULL)
            {printf("no such job!!\n");return;}

    }

    if(i==1){
            if(head==end){

                //printf("only one!\n");

                p=head;     

                pid=p->pid;printf("%d\t\t%s\n",pid,p->cmd);
                free(p);
                head=end=NULL;
            }else{
            //

printf("i==1!\n");
q=p->next; pid=head->pid;printf("%d\t\t%s\n",pid,head->cmd); free(head); head=q;} }
else{

    //printf("other\n");
        q=p->next;
        pid=q->pid;
        p->next=q->next;
        if(q->next==NULL) end=p;//如果是最后一個結點
        printf("%d\t\t%s\n",pid,q->cmd);
        free(q);

    }

    jobs_count--;
    kill(pid,SIGCONT);
    waitpid(pid,NULL,0);



}





}

//-------------------------bg---------------------

void my_bg(char *ch){

if(head==NULL){                 //無作業

    printf("no  job!!\n");

    return;



}

else if(ch==NULL){              //把作業放到后臺

    puts("para missing!");

}

else{                           //根據作業號放至后臺運行。



    int i=atoi(ch),j=1;

    node *p=head;

    while(p->next!=NULL&&j++<i)   p=p->next; 
    if(p==NULL) {
        printf("no such job!!\n");
        return;
    }
    pid=p->pid;

    kill (pid,SIGCONT);
    strcpy(p->state,"running");



}




}

//--------------------往作業鏈表增加結點------------------

void add_node(int pid,char argv,char state)

{

node *p;

jobs_count++;

p=(node *)malloc(sizeof(node));

p->pid=pid;

if (stop_flag==0)

    strcat(argv," &");

strcpy(p->cmd,argv);

strcpy(p->state,state);


p->next=NULL;




printf("[%d] %d  %s  %s\n",jobs_count,p->pid,p->state,p->cmd);



if(head==NULL){

    head=p;end=p;

}else {

    end->next=p;

    end=end->next;

}





}

//-------------信號處理函數-------------------------------------------

void father_sig_hander(int p){

if(p==SIGINT)

{   
    kill(pid,SIGTERM);                  

}

else if(p==SIGTSTP)

{

    stop_flag=1;

//printf("%d\n",pid);

    kill(pid,SIGSTOP);

    add_node(pid,strrchr(path_cmd,'/')+1,"stop");

}

}

//--------------------管道重定向----------------------------//

//-------------找出管道符位置并返回-------------------------/ char instru_divide(char ch){ char argu1, argu2; char token[] = {"<",">","|"}; int i = 0, len; len = strlen(ch); for(;i<3;i++){ argu1 = strtok(ch,token[i]); if(strlen(argu1)<len){ break; } else{ continue; } } return token[i]; }

//---------------求取execvp函數第二個參數-------------/ void aru_divide(char instru, char **ch){ int i = 0; ch[i] = strtok(instru," "); while(ch[i]&&i<8){ i++; ch[i] = strtok(NULL," "); } ch[i] = (char )0;//最后一個參數后面指針賦值NULL }

void reDirect(char str){ char instru1 , instru2 , token , ch[50]; int pipe_fd[2]; int fid; strcpy(ch,str); token = instru_divide(str);//token 為管道符 |、 >、或 < instru1 = strtok(str,token);//處理前管道符前一個命令 instru2 = strstr(ch,token)+1;//處理前管道符后一個命令 char prog1[8],prog2[8];//定義指針數組存儲execvp函數第二個參數 aru_divide(instru1,prog1); aru_divide(instru2,prog2); pid_t pid[2]; if(strcmp(token,"|")==0){ if(pipe(pipe_fd) < 0){ perror ("pipe failed"); exit (errno);
}

    pid[0] = fork();
    if(pid[0]<0){
        perror("fork error");
        exit(1);
    }

    else if(pid[0]==0){
         close(pipe_fd[0]);
        dup2(pipe_fd[1], 1);
        close(pipe_fd[1]);
        execvp(prog1[0], prog1);
    }

    else{
    pid[1] = fork();
        if(pid[1]<0){
              perror("fork error");
              exit(1);
          }
        else if(pid[1]==0){
            close(pipe_fd[1]);
            dup2(pipe_fd[0],0);
            close(pipe_fd[0]);
            if(execvp(prog2[0], prog2)){
                close(pipe_fd[0]);
                close(pipe_fd[1]);
                return;
            }
        }
        close(pipe_fd[0]);
        close(pipe_fd[1]);
        waitpid(pid[0],NULL,0);
        waitpid(pid[1],NULL,0);

  }
  return;
}

if(strcmp(token,">")==0){ if((fid = open(prog2[0],O_CREAT|O_RDWR|O_TRUNC))==-1){ perror("open error"); exit(1); } pid[0] = fork(); if(pid[0]<0){ perror("fork error"); exit(1); } else if(pid[0]==0){ dup2(fid,1); close(fid); execvp(prog1[0], prog1); } else{ waitpid(pid[0],NULL,0); } return; } if(strcmp(token,"<")==0){ if(access(prog2[0],F_OK)){ perror("file not found"); exit(1); } pid[0] = fork(); if(pid[0]<0){ perror("fork error"); exit(1); } else if(pid[0]==0){ fid = open(prog2[0],O_RDWR); dup2(fid,0); close(fid); if(execvp(prog1[0], prog1)){ puts("rwewrw"); } } else{ waitpid(pid[0],NULL,0); } return; } }</pre>

 本文由用戶 flaty 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!