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

时间:2021-05-09 17:06:50

项目介绍

一直在想,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来运行一下该程序:

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