Linux ——shell的模拟实现

用户和操作系统如何沟通交流,操作系统是不信任用户的,所以提供了一系列的系统接口让用户去调用。shell也是一个应用程序,它提供了界面,并且用户可以输入指令就能够和操作系统打交道。本文,实现一个简单的shell,当然我们不实现提供界面呀的一些复杂操作。我们只要求,输入指令,然后执行指令这样的功能。


(1) 我们知道shell执行完一个指令后,会立马刷新出,然后还能输入指令,所以我们先实现一个不断循环输出的my_shell。

#include
int main()
{
  while(1)
  {
    // 打印提示符
    printf("[who@myhostname mydir]#");
    fflush(stdout);    
  }
}

(2) 获取指令
指令是一个字符串,我们默认其长度为128,这个可以利用宏定义,也方便以后修改。利用fgets(),可以获取指令,注意我们获取的指令中含有’\n’,所以我们要去掉末尾的’\n’。

#include
#include
#include
#include
#define NUM 128
int main()
{
  char command[NUM];
  while(1)
  {
    //清理s中的数据
    command[0]=0;
    //打印提示框
    printf("[who@myhostname mydir]#");
    fflush(stdout);
    //获取指令字符串
    fgets(command,NUM,stdin);
    command[strlen(command)-1]='\0';
     }
}

(3) 解析指令
我们获取到了指令,还得解析指令,我们必须拿到这个字符串数组中的每一个字符串。
比如: ls -a -l,存放到了字符串数组中,我们需要把每个字符串,都单拎出来。变成:‘ls’,‘-a’,‘-l’。
所以会用到strtok()这个字符串分解函数,命令行上字符串是以’ '分开的。分解命令字符串后,保存到一个新的指针数组中。指针数组的大小可以定义宏为64。

#include
#include
#include
#include
#define NUM 128
#define comd_NUM 64
int main()
{
  char command[NUM];
  while(1)
  {
    char * arv[comd_NUM]={NULL};
    s[0]=0;
    // 
    printf("[who@myhostname mydir]#");
    fflush(stdout);
    //
    fgets(command,NUM,stdin);
    command[strlen(command)-1]='\0';
    //分解字符串,保存到arv中
    const char* tal=" ";
    arv[0]=strtok(command,tal);
    int i=1;
    while(arv[i]=strtok(NULL,tal))
    {
       i++;
    }
     }
}

(4) 执行指令
指令我们已经取出来了,接下来就应该是执行命令,我们创建一个子进程去执行指令,父进程等待子进程,拿出退出信息。这里用到了进程替换以及进程等待,不懂得小伙伴,可以看我之前写的博客。

#include 
#include 
#include 
#include 
#include 

#define NUM 128
#define CMD_NUM 64

int main()
{
    char command[NUM];
    while(1)
    {
        char *argv[CMD_NUM] = { NULL };
        //1. 打印提示符
        command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
        printf("[who@myhostname mydir]# ");
        fflush(stdout);
        //2. 获取命令字符串
        fgets(command, NUM, stdin);
        command[strlen(command) - 1] = '\0'; //"ls\n\0"
        //printf("echo: %s\n", command);

        //"ls -a -b -c\0";
        //3. 解析命令字符串, char *argv[];
        //strtok();
        const char *sep = " ";
        argv[0] = strtok(command, sep);
        int i = 1;
        while(argv[i] = strtok(NULL, sep)){
            i++;
        }
        }

        //4. 执行第三方命令
        if(fork() == 0){
            //child
            execvp(argv[0], argv);
            exit(1);
        }

        int status = 0;
        waitpid(-1, &status, 0);
        printf("exit code: %d\n", (status >> 8)&0xFF);
    }
}

现在我们来看看运行效果:
Linux ——shell的模拟实现_第1张图片
但是还有一个问题没有解决:
我们来运行一些 cd指令。
Linux ——shell的模拟实现_第2张图片
可以看到,路径并没有回退,这是什么原因?我们得指令一直用的是子进程来执行,回退路径应该是父进程去执行,需要父进程执行的命令为内键命令,比如:cd。
所以我们需要在加一个判断,是否为”cd“指令。

#include 
#include 
#include 
#include 
#include 

#define NUM 128
#define CMD_NUM 64

int main()
{
    char command[NUM];
    for( ; ; ){
        char *argv[CMD_NUM] = { NULL };
        //1. 打印提示符
        command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
        printf("[who@myhostname mydir]# ");
        fflush(stdout);
        //2. 获取命令字符串
        fgets(command, NUM, stdin);
        command[strlen(command) - 1] = '\0'; //"ls\n\0"
        //printf("echo: %s\n", command);

        //"ls -a -b -c\0";
        //3. 解析命令字符串, char *argv[];
        //strtok();
        const char *sep = " ";
        argv[0] = strtok(command, sep);
        int i = 1;
        while(argv[i] = strtok(NULL, sep)){
            i++;
        }

        //4.检测命令是否是需要shell本身执行的,内建命令
           if(strcmp(argv[0], "cd") == 0){
           if(argv[1] != NULL) 
           chdir(argv[1]);
            continue;
        }

        //5. 执行第三方命令
        if(fork() == 0){
            //child
            execvp(argv[0], argv);
            exit(1);
        }

        int status = 0;
        waitpid(-1, &status, 0);
        printf("exit code: %d\n", (status >> 8)&0xFF);

    }
}

chdir(),是更改路径的函数。


以上就是我们shell的模拟实现

你可能感兴趣的:(Linux学习,linux,服务器,运维)