c多个管道管道和文件描述符问题

时间:2014-11-01 15:03:41

标签: c pipe stdout stdin

你好,我在使用烟斗时有点迷失

我必须开发一个类似程序的shell,所以我有一个flex程序已经工作,表达式是这个函数的源代码

修改

正如戴夫所指出的那样 我确实声明了函数内部的管道,但我仍然有同样的问题 所以这里有一个功能的更新我已经被困在它上一段时间了 看起来我没有正好关闭管道因为第二个叉子总是挂在waitpid()

但我确实尝试关闭主进程和所有孩子的所有分叉,但第二个分叉仍然悬挂


   int execute(Expression *e , int wait, int fdin,int fdout,int fderror){
    int status;
    pid_t childPID;
    int fd;
    int pp[2];

    switch (e->type) {
        case SIMPLE:
            childPID = fork();
            if(childPID >= 0) //fork was successful
            {
                if(childPID == 0) //child process
                {
                    if(fdin != 0){
                        dup2(fdin,0);
                        close(fdin);
                        if(fdin > 2){
                            close(fdin +1); 
                        }
                    }
                    if(fdout != 0){
                        dup2(fdout,1);
                        close(fdout);
                        if(fdout > 3){
                            close(fdout -1);
                        }
                    }
                    if(fderror != 2){
                        dup2(fderror,2);
                        close(fderror);
                    }
                    status = execvp(e->arguments[0], &e->arguments[0]);
                    perror(e->arguments[0]);
                    exit(1);
                }
                else//parent process
                {
                    if(fdin > 2){
                        close(fdin);    
                        close(fdin +1); 
                    }
                    if(fdout > 3){
                        close(fdout);
                        close(fdout -1);    
                    }

                    if(wait == 1){
                        printf("%s\n","going to wait" );
                        waitpid(childPID, &status, 0);
                    }
                    putchar('\n');
                    break;
                }
            }
            else// fork failed 
            {
                perror("fork");
            }
            break;
        case SEQUENCE:
            execute(e->gauche,1,fdin,fdout,fderror);
            execute(e->droite,1,fdin,fdout,fderror);
            break;
        case SEQUENCE_ET:
            execute(e->gauche,0,fdin,fdout,fderror);
            execute(e->droite,1,fdin,fdout,fderror);
            break;
        case SEQUENCE_OU:
            execute(e->gauche,0,fdin,fdout,fderror);
            execute(e->droite,1,fdin,fdout,fderror);
            break;
        case BG:
            execute(e->gauche,0,fdin,fdout,fderror);
            break;
        case PIPE:
            if(pipe(pp) < 0){
                perror("pipe");
                exit(1);
            }
            execute(e->gauche,0,fdin,pp[1],fderror);
            execute(e->droite,1,pp[0],fdout,fderror);
            break;
        case REDIRECTION_I:
            fd = open(e->arguments[0],O_RDONLY, 0666);
            execute(e->gauche,1,fd,fdout,fderror);
            break;
        case REDIRECTION_O:
            fd = open(e->arguments[0],O_CREAT | O_RDWR, 0666);
            execute(e->gauche,1,fdin,fd,fderror);
            break;
        case REDIRECTION_A:
            fd = open(e->arguments[0], O_TRUNC | O_CREAT | O_RDWR, 0666);
            execute(e->gauche,1,fdin,fd,fderror);
            break;
        case REDIRECTION_E:
            fd = open(e->arguments[0], O_CREAT | O_RDWR, 0666);
            execute(e->gauche,1,fdin,fdout,fd);
            break;
        case REDIRECTION_EO:
            fd = open(e->arguments[0], O_CREAT | O_RDWR, 0666);
            execute(e->gauche,1,fdin,fd,fd);
            break;
        default:
            return 0;
            break;

    }
    return 0;


}

我的问题是这对于像

这样的简单命令很有用
ls | grep a

os

ls > test

但是如果我搞砸了那就完全错了

例证

ls | grep a > test 

我得到了终端而不是文件

或者如果我使用

ls | grep a | grep c 

管道变得一团糟我猜我需要更多的管道来做它但我不知道如何动态创建管道

感谢所有

编辑


刚才发现在管道修复后我确实尝试了更大数据的命令 例如,而不是ls我在文件上做了猫 这样工作看起来就像命令在任何数据到达管道之前停止

2 个答案:

答案 0 :(得分:1)

您正在为所有管道使用全局变量。当您创建ls | grep a时,您的全局管道工作正常。但是当你有多个管道时,你可以将它们用于不同的目的!解决方案是在函数中提取管道创建代码。当您需要一个新管道时,在那里创建一个并将正确的文件描述符传递给execute的递归调用(这就是您需要fdinfdout参数的原因。< / p>

答案 1 :(得分:1)

所以我想我修好了

我的问题是在最后一个fork启动之前调用wait 在这种情况下,我确实添加了标志f来识别最后一个命令,并在最后一个命令上使用wait pid

这是代码

int execute(Expression *e , int wait, int fdin,int fdout,int fderror, int lastflag){
    int status;
    pid_t childPID;
    int fd;
    int pp[2];


    switch (e->type) {
        case SIMPLE:
            childPID = fork();
            if(childPID >= 0) //fork was successful
            {
                if(childPID == 0) //child process
                {
                    if(fdin != 0){
                        dup2(fdin,0);
                        close(fdin);
                    }
                    if(fdout != 1){
                        dup2(fdout,1);
                        close(fdout);
                    }
                    if(fderror != 2){
                        dup2(fderror,2);
                        close(fderror);
                    }
                    for(int i = 3; i <= lastfd; i++){
                        close(i);
                    }

                    status = execvp(e->arguments[0], &e->arguments[0]);
                    perror(e->arguments[0]);
                    exit(1);
                }
                else//parent process
                {

                    if(wait == 1){
                        for(int i = 3; i <= lastfd; i++){
                            close(i);
                        }
                        printf("%s\n","going to wait" );
                        waitpid(childPID, &status, WNOHANG);
                    }
                    putchar('\n');
                    break;
                }
            }
            else// fork failed 
            {
                perror("fork");
            }
            break;
        case SEQUENCE:
            execute(e->gauche,1,fdin,fdout,fderror,0);
            execute(e->droite,1,fdin,fdout,fderror,0);
            break;
        case SEQUENCE_ET:
            execute(e->gauche,0,fdin,fdout,fderror,0);
            execute(e->droite,1,fdin,fdout,fderror,0);
            break;
        case SEQUENCE_OU:
            execute(e->gauche,0,fdin,fdout,fderror,0);
            execute(e->droite,1,fdin,fdout,fderror,0);
            break;
        case BG:
            execute(e->gauche,0,fdin,fdout,fderror,0);
            break;
        case PIPE:
            if(pipe(pp) < 0){
                perror("pipe");
                exit(1);
            }
            ch_lastfd(pp[1]);
            execute(e->gauche,0,fdin,pp[1],fderror,0);
            if(lastflag == 1){
                execute(e->droite,1,pp[0],fdout,fderror,0);
            }else{
                execute(e->droite,0,pp[0],fdout,fderror,0);
            }
            break;
        case REDIRECTION_I:
            fd = open(e->arguments[0],O_RDONLY, 0666);
            ch_lastfd(fd);
            execute(e->gauche,1,fd,fdout,fderror,0);
            break;
        case REDIRECTION_O:
            fd = open(e->arguments[0],O_CREAT | O_RDWR, 0666);
            ch_lastfd(fd);
            execute(e->gauche,1,fdin,fd,fderror,0);
            break;
        case REDIRECTION_A:
            fd = open(e->arguments[0], O_TRUNC | O_CREAT | O_RDWR, 0666);
            ch_lastfd(fd);
            execute(e->gauche,1,fdin,fd,fderror,0);
            break;
        case REDIRECTION_E:
            fd = open(e->arguments[0], O_CREAT | O_RDWR, 0666);
            ch_lastfd(fd);
            execute(e->gauche,1,fdin,fdout,fd,0);
            break;
        case REDIRECTION_EO:
            fd = open(e->arguments[0], O_CREAT | O_RDWR, 0666);
            ch_lastfd(fd);
            execute(e->gauche,1,fdin,fd,fd,0);
            break;
        default:
            break;

    }
    return 0;


}

可能不是实现它的最佳方式,所以如果有人知道如何让它变得更好我会打开提示感谢