c语言实现简单的shell

c语言实现简单的shell


记录一下上课讲的实现简单的shell(含有单个管道)

/*************************************************************************
	> File Name: myshell.c
	> Author: Kris_Wqy
	> Mail:
	> Created Time: Mon 30 Aug 2021 08:55:15 PM CST
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>

#define CNT 10
#define LEN 1024

//去除命令中多余的空格
char *trim(char *str){
    int head = 0;
    int tail = strlen(str) - 1;
    //isspace函数,判断是否是空格
    while(isspace(str[head]))
        head++;
    while(isspace(str[tail]))
        str[tail--] = 0;//用\0替代空格
    return str+head;
}

//执行命令
void runcmd(char *buff){
    //子进程
    int redfd = -1;
    //strsrt函数,返回字符串中首次出现字符的地址
    if(strstr(buff, "<")){
        //设置为输入重定向
        redfd = 0;
    }
    if(strstr(buff, ">")){
        //设置为输出重定向
        redfd = 1;
    }

    char *cmd = NULL;
    //判断是否有重定向符号
    if(redfd != -1){
        //strtok函数,用来分隔(<, >)前
        char *token = strtok(buff, "<>");
        //重定向前符号前为命令
        cmd = token;
        //分隔重定向符号后面
        token = strtok(NULL, "<>");
        //清理重定向符号后面的命令中多余的空格
        token = trim(token);

        int fd;
        if(redfd){
            //设置输出重定向的文件描述符
            fd = open(token, O_RDWR | O_CREAT | O_TRUNC, 0644);
        }else{
            //设置输入重定向的文件描述符
            fd = open(token, O_RDWR);
        }

        if(fd < 0){
            perror(token);
            exit(1);
        }
        //输出重定向,redfd指向fd
        dup2(fd, redfd);
    }else{//直接就是命令
        cmd = buff;
    }

    //解析命令
    int i = 0;
    char *argarr[20];//指针数组
    char *tk = strtok(cmd, " ");//以空格切割命令
    while(tk){
        argarr[i++] = tk;//把第一个命令放入指针数组
        tk = strtok(NULL, " ");//继续切割命令
    }
    argarr[i] = tk;//空作为参数结束标志

    execvp(argarr[0], argarr);//输入参数和,对应的数组
    perror(argarr[0]);//执行失败
    exit(1);
}

int main(){
    char buff[LEN];

    while(1){
        printf("$");
        fgets(buff, LEN, stdin);//从标准输入中读取LEN-1长度个数据,存入buff,以\n结束
        buff[strlen(buff) - 1] = 0;//0就是'\0',需要把buff最后一位置为O
        //printf("cmd:[%s]\n", buff);

        //退出命令
        if(!strcmp(buff, "exit")){
            //printf("exit~\n");
            break;
        }
        
		//利用管道符号|切分命令
        int i = 0;
        char *cmdarr[CNT];
        char *tk = strtok(buff, "|");//切割原始buff里存放的命令
		//cmdarr存放命令
        while(tk){
            cmdarr[i++] = tk;
            tk = strtok(NULL, "|");
        }
        cmdarr[i] = tk;

        //没有管道的情况
        if(i == 1){
            pid_t pid = fork();
            if(pid < 0){
                perror("fork");
                exit(1);
            }
            if(pid){
                wait(NULL);
                continue;
            }
            runcmd(cmdarr[0]);
        }
		
        //有管道的情况
        //创建管道
        int pp[2];
        if(pipe(pp) < 0){
            perror("pipe");
            exit(1);
        }

        //创建第一个子进程
        pid_t pid = fork();
        if(pid < 0){
            perror("fork");
            exit(1);
        }
        if(pid == 0){
            //写端赋给标准输出,1为标准输出,pp[1]为写端
            dup2(pp[1], 1);
            //把管道的读写端和子进程的3,4(文件描述符)关闭
            close(pp[0]);
            close(pp[1]);
            //执行管道前的命令
            runcmd(cmdarr[0]);
        }

        //创建第二个子进程
        pid = fork();
        if(pid < 0){
            perror("fork");
            exit(1);
        }
        if(pid == 0){
            //读端赋给标准输入,0为标准输入,pp[0]为读端
            dup2(pp[0], 0);
            //把管道的读写端和子进程的3,4(文件描述符)关闭
            close(pp[0]);
            close(pp[1]);
            //执行管道后的命令
            runcmd(cmdarr[1]);
        }
		//shell进程关闭管道的读端和写端
        close(pp[0]);
        close(pp[1]);
        //wait等待子进程结束
        wait(NULL);
        wait(NULL);
    }

    return 0;
}

演示效果:
在这里插入图片描述


版权声明:本文为huaijiahuo_wqy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。