【问题标题】:Recursive piping in Unix again Part 2再次在 Unix 中递归管道第 2 部分
【发布时间】:2018-12-04 08:00:34
【问题描述】:

我知道这个主题已经发布了很多,但我希望将其引向不同的方向。我想编写一个具有 shell 管道功能的程序,即 cmd1 |命令2 | cmd3 管道和重定向。我已阅读 thisthisthis 等页面以供参考。这些解决方案非常适合“水平流水线”,但我想“垂直”实现它。为了使我的外壳垂直,每个“命令”进程必须有一个不同的父进程(上一个命令)。因此,每个要执行的命令都是从前一个命令产生的。我遇到的问题是,当我在孩子中递归(而不是像示例中的父级)时,程序执行得很好,但随后挂起,我必须按回车键重新提示我的 shell。我很好奇为什么这是不同的以及如何解决这个问题。

static void exec_pipeline(size_t pos, int in_fd) {
    // Invalid Pipe has issues
    if (newargv[pipe_commands[pos+1]] == NULL)
        report_error_and_exit("Invalid pipe");
/* last command, pipe_command conatins indices of commands to execute */
    if (pipe_commands[pos + 1] == 0) { 
        redirect(in_fd, STDIN_FILENO); /* read from in_fd, write to STDOUT */
        execvp(newargv[pipe_commands[pos]], newargv + pipe_commands[pos]);
        report_error_and_exit("execvp last command");
    }
    else { /* $ <in_fd cmds[pos] >fd[1] | <fd[0] cmds[pos+1] ... */
        int fd[2]; /* output pipe */
        if (pipe(fd) == -1)
            report_error_and_exit("pipe");
        switch(fork()) {
            case -1:
                report_error_and_exit("fork");
            case 0: /* parent: execute the rest of the commands */
                CHK(close(fd[1])); /* unused */
                CHK(close(in_fd)); /* unused */
                exec_pipeline(pos + 1, fd[0]); /* execute the rest */
            default: /* child: execute current command `cmds[pos]` */
                child = 1;
                CHK(close(fd[0])); /* unused */
                redirect(in_fd, STDIN_FILENO);  /* read from in_fd */
                redirect(fd[1], STDOUT_FILENO); /* write to fd[1] */
                execvp(newargv[pipe_commands[pos]], newargv + pipe_commands[pos]);
                report_error_and_exit("execvp");

        }
    }
}
void report_error_and_exit(const char *msg) {
    perror(msg);
    (child ? _exit : exit)(EXIT_FAILURE);
}

/* move oldfd to newfd */
void redirect(int oldfd, int newfd) {
    if (oldfd != newfd) {
        if (dup2(oldfd, newfd) != -1)
            CHK(close(oldfd));
        else
            report_error_and_exit("dup2");
    }
}

CHK 很像 assert,定义在一个名为 CHK.h 的文件中,如果您好奇,它看起来像这样:

  do {if((x) == -1)\
   {fprintf(stderr,"In file %s, on line %d:\n",__FILE__,__LINE__);\
    fprintf(stderr,"errno = %d\n",errno);\
    perror("Exiting because");\
    exit(1);\
   }\
 } while(0)

【问题讨论】:

  • 更新:问题是我如何等待进程完成,我目前正在研究解决方案。

标签: c shell recursion pipe file-descriptor


【解决方案1】:

想通了!所以问题是,在垂直实现中,最后一个要执行的命令是'cmd3',它应该是'cmd1'。不是一个完美的解决方案,但有效。基本上只是翻转了建造管道的方向。但是,此解决方案仍然存在一些与传入输入有关的文件描述符问题。

static void exec_pipe(size_t pos, int in_fd) {
    // Invalid Pipe has issues
    if (newargv[pipe_commands[pos+1]] == NULL) {
        report_error_and_exit("Invalid pipe");
    }
    if (pipe_commands[pos + 1] == 0) { /* last command */
        redirect(in_fd, STDOUT_FILENO); /* read from in_fd, write to STDOUT */
        execvp(newargv[pipe_commands[PIPE_FLAG-pos]], newargv + pipe_commands[PIPE_FLAG-pos]);
        report_error_and_exit("execvp last command");
    } else { /* $ <in_fd cmds[pos] >fd[1] | <fd[0] cmds[pos+1] ... */
        int fd[2]; /* output pipe */
        if (pipe(fd) == -1)
            report_error_and_exit("pipe");
        switch(fork()) {
            case -1:
                report_error_and_exit("fork");
            case 0: /* child: execute the rest of the commands */
                CHK(close(fd[0])); /* unused */
                CHK(close(in_fd)); /* unused */
                exec_pipe(pos + 1, fd[1]); /* execute the rest */
            default: /* parent: execute current command `cmds[pos]` */
                child = 1;
                CHK(close(fd[1])); /* unused */
                redirect(in_fd, STDOUT_FILENO);  /* read from in_fd */
                redirect(fd[0], STDIN_FILENO); /* write to fd[0] */
                execvp(newargv[pipe_commands[PIPE_FLAG-pos]], newargv + pipe_commands[PIPE_FLAG-pos]);
                report_error_and_exit("execvp");
        }
    }
}

void pipeHelper() {
    pid_t dad;
    (void) fflush(stdin);
    (void) fflush(stdout);

    // Check for successful fork
    if ((dad = fork()) < 0)
        report_error_and_exit("fork");
    else if (dad == 0) {
        // Catch input redirect
        // Copy input file description to standard in descriptor then close
        if (IN_FLAG == 1)
            redirect(input_file_description, STDIN_FILENO);
        // Catch output redirect
        // Copy output file description to standard out descriptor then close
        if (OUT_FLAG == 1)
            redirect(output_file_description, STDOUT_FILENO);
        // Catch input redirect
        if (HEREIS_FLAG > 0 && BAIL_FLAG == 0)
            redirect(hereis_file_description, STDIN_FILENO);
        // Redirect to /dev/null
        if (AMP_FLAG != 0 && IN_FLAG == 0)
            redirect(devnull_file_description, STDIN_FILENO);
        exec_pipe(0, STDIN_FILENO);
        //exec_pipeline(0, STDIN_FILENO);
    } else {
        if (AMP_FLAG != 0) {
            (void) printf("%s [%d]\n", newargv[0], dad);
            AMP_FLAG = 0;
        } else {
            for (;;) {
                pid_t pid;
                pid = wait(NULL);
                if (pid == dad)
                    break;
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-28
    • 2010-12-14
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多