#include<iostream>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<sys/wait.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;

const int shellnum =32;
const char * file_all = {"shell-all.txt"};
const char * file_half = {"shell-half.txt"};
//把输入转换为exec函数组可用的参数
void cmd2argv(char **, char *);
//没有 | 的情况
void cmd_no_grep(char **, char *);
//需要管道的情况
void cmd_with_grep(char **, char *, int );
//用来完成管道
bool fileReverse();
//输出到屏幕
void file2Srcn();
int main()
{
       char cmd_line[1024];
       char * arg[shellnum];
       char *cmd_grep = NULL;
        //int out_save = dup(STDOUT_FILENO); 
        //int in_save = dup(STDIN_FILENO);
       int time=0;
       memset(cmd_line, 0, sizeof(cmd_line));
       cout<<">>";
       while(fgets(cmd_line, sizeof(cmd_line), stdin))
       {
               /*
                        如果有管道的话,相当于是把A的结果作为B的输入
                        这里的file_all就是用来存放A的结果
                        所以每次输入命令,需要清空这个文件
               */
                int fd = open(file_all, O_TRUNC | O_CREAT, 0644);
                close(fd);
               cmd_line[strlen(cmd_line)-1] = '\0';
               //判断是否有管道
               if(strchr(cmd_line, '|'))
               {
                        cmd_grep = strtok(cmd_line, "|");
                        //cout<<"cmd:"<<cmd_grep<<endl;
                       while(cmd_grep)
                       {
                               cmd_with_grep(arg, cmd_grep,time++);
                               cmd_grep = strtok(NULL, "|");
                       }
                int fd = open(file_all,  O_RDONLY, 0644);
                file2Srcn();
                close(fd);
               }
                else
                {
                        cmd_no_grep(arg, cmd_line);
                }
                cout<<"\n>>";
       }
}

void cmd2argv(char **argv, char *cmd)
{
        int cnt=0,bg=0,end=0;
        while(cmd[bg] != '\0')
        {
                //去除命令前面的空格
                while(cmd[bg] == ' ')
                        bg++;
                end = bg;
                //确定命令的结尾
               while(cmd[end] != ' ' && cmd[end] != '\0')
                        end++;
                /*
                        下面这个判断时因为下面这种情况我这段代码会有bug
                        ls -l  (l后面还有两个空格)
                        这是程序会成功拆开ls和-l 但是此时由于-l后面不是\0
                        所以while循环还会继续,但是这种情况最后 bg == end
                        所以用下面这个判断进行避免  
                */
                if(bg != end)
                {
                        argv[cnt] = new char[end-bg+1];
                        strncpy(argv[cnt],  cmd+bg, end-bg);
                        //cout<<":"<<argv[cnt]<<":"<<endl;
                        cnt++;
                        bg = end+1;
                        end++;
                }
                
        }
        argv[cnt] =NULL;
}

void cmd_no_grep(char ** arg, char *cmd_line)
{
        cmd2argv(arg, cmd_line);
        pid_t pid = fork();
        if(0 == pid)
        {
                execvp(arg[0], arg);
        }
        else
        {
                wait(NULL);
        }
}


void cmd_with_grep(char ** arg, char *cmd_line, int time)
{
        int fd = open(file_all,O_WRONLY | O_CREAT ,0644);
        int out_save = dup(STDOUT_FILENO); 
        int in_save = dup(STDIN_FILENO);
        /*
        A|B 对于A,它的输入是标准输入,输出的话重定向到文件shell-half.txt
        */
        if(time == 0)
        {
               cmd2argv(arg, cmd_line);
               dup2(fd, STDOUT_FILENO);
               pid_t pid = fork();
               if(0 == pid)
                {
                        execvp(arg[0], arg);
                }
                else
                {
                        sleep(0.1);
                        wait(NULL);
                        close(fd);
                }
        }
        else
        {
                /*
                        A|B 对于B来说,输入输出都得重定向
                        输入 file_all.txt
                        输出 file_half.txt
                        所以我们再最下面调用fileReverse()函数把half.txt拷贝到all.txt
                        (这里这么做是因为,我们在使用exec函数的时候要保证all.txt是我们想要的输入)
                */
               int fd_in = open(file_all,O_RDWR  ,0644);
               int fd_half = open(file_half,O_RDWR | O_CREAT | O_TRUNC, 0644);
               cmd2argv(arg, cmd_line);
               dup2(fd_in, STDIN_FILENO);
               dup2(fd_half, STDOUT_FILENO);
               pid_t pid = fork();
               if(0 == pid)
                {
                        if(execvp(arg[0], arg) < 0)
                        {
                                perror("error:");
                        }
                        
                }
                else
                {
                        sleep(0.1);
                        wait(NULL);
                        close(fd_in);
                        close(fd_half);
                        bool res = fileReverse();
                }
                 
        }
        dup2(out_save, STDOUT_FILENO);
        dup2(in_save, STDIN_FILENO);
        close(fd);
}

bool fileReverse()
{
        int fd_read = open(file_half,O_RDONLY);
        if(-1 == fd_read)
        {
                perror("open");
                exit(1);
      }
      int fd_write = open(file_all,O_CREAT | O_WRONLY | O_TRUNC, 0664);
      if(-1 == fd_write)
      {
          perror("create");
          exit(1);
      }
        char *str = new char[1024];
        int count = read(fd_read, str, sizeof(str));
        if(-1 == count)
        {
                perror("read");
                return false;
        }
        while(count)
        {
        write(fd_write, str, count);
        count = read(fd_read, str, sizeof(str));
        }
        return true;
}

void file2Srcn()
{
        int fd_read = open(file_all,O_RDONLY);
	if(-1 == fd_read)
	{
		perror("open");
		exit(1);
	}
	char *str = new char[1024];
	int count = read(fd_read, str, sizeof(str));
	if(-1 == count)
	{
		perror("read");
		exit(1);
	}
	while(count)
	{
		write(STDOUT_FILENO, str, count);
		count = read(fd_read, str, sizeof(str));
	}
}

目前支持shell解析以及管道,但是重定向这种还没实现

实现方法不是最优,很蠢,大家发现问题欢迎下方留言讨论