Linux系统编程--sh模拟器

首先设置参数
最多命令参数 MAX_CMD
和每个命令的最大长度 MAX_LEN

#define MAX_LEN 32
#define MAX_CMD 20
#define QUIT "quit"

使用一个二维数组来保存从终端得到的命令,并且使用两个int数组(start,end)来对每组命令进行划分(按照 “|”)。其中在cmd中使用’\0’来作为命令的结束。
在这里插入图片描述
首先分配内存,因为函数传参是值语义,所以需要将其地址传入。

void initilize(char **buf, char ***cmd, int **start, int **end){
   *buf = (char*)malloc(sizeof(char) * 1024);
   *cmd = (char**)malloc(sizeof(char*) * MAX_CMD);
   *start = (int*)malloc(sizeof(int) * MAX_CMD);
   *end = (int*)malloc(sizeof(int) * MAX_CMD);
   int i = 0;
   for(; i < MAX_CMD; ++i){
       *((*cmd) + i) = (char*)malloc(sizeof(char) * MAX_LEN);
   }
}

获取命令。首先将终端数据读取到buf中,在使用strtok函数,以空格为分隔符对buf进行分割,得到命令。

int getCom(char* buf, char **cmd){
    char *s;
    int i;
    write(STDOUT_FILENO, "yyd # ", 6);
    //bzero(buf, 1024);
    i = read(STDIN_FILENO, buf, 1024);
    buf[i - 1] = '\0';
    i = 0;
    s = strtok(buf, " ");
    while(s){
        if(!strcmp(s, QUIT)) return -1;
        strcpy(*(cmd + (i++)), s);
        s = strtok(NULL, " ");
    }
    **(cmd + i) = '\0';
    return 0;
}

对得到的命令按照“|”进行拆分。每一部分使用start和end来保存

int partCom(char **cmd, int *start, int *end){
    int i = 0;
    int nums = 1;
    *(start++) = 0;
    while(**(cmd + i) != '\0'){
     //   printf("cmd %s \n",*(cmd + i));
        if(!strcmp(*(cmd + i), "|")){
            *(end++) = i - 1;
            *(start++) = ++i;
            nums++;
        }else{
            i++;
            //printf("fuck %d\n", i);
        }
    }
    *(end++) =  i - 1;
    *(start) = *(end) = -1;
    return nums;
}

执行。其中nums是命令组的个数。首先判断是一个命令还是多个命令。多个需要使用递归创建管道。
如果是一个的话。先将命令通过start和end从cmd中获得保存到tmp中,在获取pathname。然后检查是否命令中包含重定向(check_redirect),如果返回-1则说明没有,可以直接execvp。如果有则交给rediret_handle处理。

void execute(char **cmd, int *start, int *end, int nums){//cmd nums 
  if(nums == 1){
  	char *tmp[MAX_CMD];
  	char pathname[MAX_LEN];
  	int i = 0;
  	int type;
  	int s = *start;
  	int e = *end;
  	if(s > e) return;
  	while(s != e) tmp[i++] = *(cmd + s++);
  	
  	tmp[i] = *(cmd + *end);
  	tmp[++i] = NULL;
 	if(fork()){
   		waitpid(-1, NULL, 0);
   		return;
  	}else{
  		strcpy(pathname, "/bin/");
   		strcat(pathname, *(cmd + *start));
   		if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
		redirect_handle(tmp, type, i);
   		_exit(0);
   	}
  }
  if(fork()){
            waitpid(-1, NULL, 0);
            return;
  }else{
            re_execute(cmd, start + nums - 1, end + nums - 1, 1, nums);
            _exit(0);
  }
}

一共六种重定向,其中i表示第几个字符串是重定向标志。

int check_redirect(char *list[], int *i){// > < >> << >& &<
    char **s = list;
    int type = -1;
    *i = -1;
    while(*s){
        if(!strcmp(*s, ">")) type = 0;
        else if(!strcmp(*s, "<")) type = 1;
        else if(!strcmp(*s, ">>")) type = 2;
        else if(!strcmp(*s, ">>")) type = 3;
        else if(!strcmp(*s, ">&")) type = 4;
        else if(!strcmp(*s, "&<")) type = 5;
        if(type != -1){
            (*i)++;
            return type;
        }
        (*i)++;
        s++;
    }
    return type;
}
void (*redirect_ptr[])(char *list[], int p) = {redirect_0, redirect_1, redirect_2,
                 redirect_3, redirect_4, redirect_5};
void redirect_handle(char* list[] , int type, int p){
    redirect_ptr[type](list, p);
}
void redirect_0(char *list[], int p){
    _redirect_help(list, p, 0);
}

IO重定向中>>,<<是往文件末尾添加数据O_APPRND, > < 是写入,并且会截断原文件O_TRUNC。>& &<是像文件描述符中写入。
通过fork()创建子进程,使用管道连接父子进程,传递数据。将对子进程的管道输入端pfd[1]重定向到标准输出1,将父进程的管道输出端pfd[0]定向到标准输入0。
注意,要在父子进程关闭不用的管道描述符,否则会看不到消息的边界,一直阻塞。

void _redirect_help(char *list[], int p, int type){
    char *file;
    char *par[MAX_LEN];
    char *cmd;
    int pos;
    int fd = -1;
    int flag = O_CREAT|O_RDWR|O_TRUNC;
    list[p] = NULL;
    switch(type){
    case 2: flag |= ~O_TRUNC; flag &= O_APPEND; // >>
    case 0: file = list[p + 1]; cmd = list[0]; pos = 0; break; // >
    case 3: flag |= ~O_TRUNC; flag &= O_APPEND; // <<
    case 1: file = list[0]; cmd = list[p + 1]; pos = p + 1; break; // <
    case 4: fd = atoi(list[p + 1]); cmd = list[0]; pos = 0; break; // >&
    case 5: fd = atoi(list[0]); cmd = list[p + 1]; pos = p + 1; break;
    }
    int i = 0;
    while(list[pos] != NULL){
        par[i++] = list[pos++]; 
    }
    par[i] = NULL;
    int pfd[2];
    char buf[1024];
    if(fd == -1) fd = open(file, O_CREAT|O_RDWR|O_TRUNC, 0666);
    pipe(pfd);
    if(fork()){
            int i;
            close(pfd[1]);
            close(0);
            dup2(pfd[0], 0);
            while((i = read(pfd[0], buf, 1024)) > 0 ){
                write(fd, buf, i);    
            }
            close(fd);
    }else{
        close(pfd[0]);
        close(1);//stdout
        dup2(pfd[1], 1);
        execvp(cmd, par);
    }
}

多个管道相连时使用递归创建。
C最先执行,然后P2等待返回,继续执行,然后P1,P0。所以应该将命令倒输入。
在这里插入图片描述
execute中。首先创建一个子进程,在子进程中进行递归处理。其中参数fd是连接进程的管道描述符pfd[1]。

 if(fork()){
            waitpid(-1, NULL, 0);
            return;
  }else{
            re_execute(cmd, start + nums - 1, end + nums - 1, 1, nums);
            _exit(0);
  }
}
void re_execute(char **cmd, int *start, int *end, int fd, int nums){
    char *tmp[MAX_CMD];
    char pathname[MAX_LEN];
    int i = 0;
    int type;
    int status;
    int s = *start;
    int e = *end;
    while(s != e){
        tmp[i++] = *(cmd + s);
        s++;
    }
    tmp[i++] = *(cmd + e);
    tmp[i] = NULL;
    strcpy(pathname, "/bin/");
    strcat(pathname, *(cmd + *start));
    if(fd != 1){
    close(1);
    dup2(fd, 1);
    }
    if(nums == 1){
        if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
        redirect_handle(tmp, type, i);
    }
   int pfd[2];
   pipe(pfd);
   if(fork()){
      close(0);
      close(pfd[1]);
      dup2(pfd[0], 0);
      waitpid(-1, &status, 0);
      if((type = check_redirect(tmp, &i)) == -1) execvp(pathname, tmp);
    }else{
 	close(pfd[0]);
        re_execute(cmd, start - 1, end - 1, pfd[1], nums - 1);
        _exit(0);
    }
}

运行结果
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值