使用C实现模拟实现一个shell解释器(一)

来源:互联网 发布:南方寝饰淘宝旗舰店 编辑:程序博客网 时间:2024/06/05 08:22

项目介绍

一直在想,linux的命令解释器是如何工作的,一直想着自己实现一个命令解释器。后来随着自己的学习以及对linux中终端的使用,对命令解释器有了一个简单的学习和了解。并且对其实现,从一开始的很简单,到后面有一些忌惮。命令解释器,顾名思义,就是对用户输入的命令,进行分析,然后按照用户的想法运行相应的命令,然后再将运行结果成现在用户眼中。

在了解了lex和yacc的相关知识之后,我觉着可以使用lex和yacc更加简单的实现命令解释器,但是后来在开发过程中,我觉着还是一步一步走,从最简单的使用C来解析用户输入的命令以及相关参数,先实现一个简单版本的命令解释器,然后再使用lex和yacc进行优化,使命令解释器能够解析更加复杂的命令,这基本上就是我的思路。

命令介绍

在我们现在使用的linux的终端中,也就是在POSIX标准下,命令分为内部命令和外部命令,当命令解释器遇到内部命令的之后,便直接运行内部命令,也就是说,内部命令是我们自己的实现的。而外部命令,当命令解释器遇到外部命令的时候,直接从系统中找到相应的应用程序,然后开启一个子线程来执行该程序。

下面是我们知道的相应的内部命令:

  • exit : 退出该shell程序
  • cd : 改变当前的工作目录
  • echo : 回显命令后面的字符串
  • export : 设置环境变量
  • history : 显示历史命令

当前实现一

下面我们来看一下,我现在的实现,后面这个代码会逐步跟进,同时在后面的文章中,将会讲解命令以及参数的相应的POSIX标准,我们的开发也会按照标准来进行。

#ifndef _COMS_H_#define _COMS_H_/** * The Struct is for storing the splitting command * the user input and the num of the command and its command * * @params coms The command and its args * @params tnum The nums of command and its args * @author hongbochen * @date 2016-08-26 * */typedef struct com{  char* coms[50];  int tnum;} Com;char* getCom(Com* cm);Com* subCom(char *com);void dealCom(Com* cm);#endif

这里的coms.h用来命令数组,来保存相应的命令以及其参数,同时将该数组的个数也保存到该结构体中。同时定义了几个函数,分别是分割命令函数,处理命令函数,获取命令函数。这里的获取命令函数的意思是:将用户输入的命令和函数进行分割,然后保存到coms数组中,获取命令函数的作用就是获取数组中的第一个元素,即为用户的命令,因为其他的元素都是相应的命令参数。

下面我们来看一下这些函数的实现:

#include "coms.h"#include <stdio.h>#include <stdlib.h>#include <unistd.h>char* getCom(Com* cm){  if(cm->tnum < 1){    return NULL;  }else{    return cm->coms[0];  }}/** * 解析用户输入的命令 * @param com 用户输入的命令字符串 * @author hongbochen * @date 2016-08-24 * @note In this version, We just split the string using blank enter code here*/Com* subCom(char *com){  //char* comss[50];  char *token = strtok(com, " ");  int nums = 0;  // 分配空间  Com* bc = (Com *)malloc(sizeof(Com));  while(token != NULL)  {    bc->coms[nums] = (char *)malloc(sizeof(char) * 20);    strcpy(bc->coms[nums], token);    // go to the next token    token = strtok(NULL, " ");    nums++;  }  bc->tnum = nums;  //return comss;  return bc;}/* * 处理被解析出来的命令 * @param cm 解析出来的命令结构 * @author hongbochen * @date 2016-08-26 */void dealCom(Com* cm){  char* fm;  if((fm = getCom(cm)) != NULL){    printf("%s\n", fm);  }}

这里的实现比较简单,也有相应的注释,处理命令函数还没有完全实现,下一篇文章将会实现。

下面我们看一下比较重要的main函数:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <readline/readline.h>#include <readline/history.h>#include <string.h>#include "coms.h"/* Use this to judge whether the program is running or not */int isRun = 1;/* * @author hongbochen * @date 2016-08-24 * @des This is a simple shell. * */int main(int argc,char *argv[]){    /* The prompt of start */    char prompt[200];    /* The command the user input */    char* line;    // The variable of this prog    int i = 0;    /* Run the app in loop */    while(isRun){      getcwd(prompt,sizeof(prompt));      strcat(prompt,"$ ");      if(!(line = readline(prompt)))        break;      /*      if(strcmp(line, "exit") == 0){        isRun = 0;      }else if(strcmp(line, "echo") == 0){        printf("%s\n", line);      }      */      // get the split command the user input      Com* cms = subCom(line);      dealCom(cms);    }}

在这里这个函数不在赘述,但是有一个事情数需要说明的是,由于我们使用了readline库,所以在编译之前需要安装readline库,同时在编译的时候,编译指令需要指定readline库。

gcc main.c coms.c -o main -lreadline

下面我们使用./main来运行一下该程序:

这里写图片描述

2 0
原创粉丝点击