我已经编写了一个C程序,该程序使用多个管道模拟外壳。问题是我可以运行大多数命令,例如ls | cat等,但是我无法使用ls | wcwc不能正常工作吗?

int pipefd[4];
int p1 = pipe(pipefd);          // Open pipe 1
int p2 = pipe(pipefd + 2);      // Open pipe 2

pid_t pid;

for(i = 0; i < n_commands; i++)
{
    fflush(stdout);
    pid = fork();

    if(pid == 0)
    {
        int command_no = i;
        int prev_pipe = ((command_no - 1) % 2) * 2;
        int current_pipe = (command_no % 2) * 2;

        // If current command is the first command, close the
        // read end, else read from the last command's pipe
        if(command_no == 0)
        {
            close(pipefd[0]);
        }
        else
        {
            dup2(pipefd[prev_pipe], 0);
            close(pipefd[current_pipe]);
        }

        // If current command is the last command, close the
        // write end, else write to the pipe
        if(command_no == n_commands - 1)
        {
            close(pipefd[current_pipe + 1]);
        }
        else
        {
            dup2(pipefd[current_pipe + 1], 1);
        }

        int p = execvp(tokens[cmd_pos[command_no]], tokens + cmd_pos[command_no]);

        close(pipefd[current_pipe]);
        close(pipefd[prev_pipe]);
        close(pipefd[prev_pipe + 1]);
        close(pipefd[current_pipe + 1]);

        _exit(0);
    }
}


如果/usr/bin中的程序不是管道中的第一个命令,则似乎未在执行。

最佳答案

这是从您的代码创建的一个非常简单的程序-猜测如何创建管道并简化命令argv的处理:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static char *argv_ls[] = { "ls", 0 };
static char *argv_wc[] = { "wc", 0 };
static char **cmds[]   = { argv_ls, argv_wc };

int main(void)
{
    int n_commands = 2;
    int pipefd[2];

    pipe(&pipefd[0]);   // Error check!

    fflush(stdout);
    for (int i = 0; i < n_commands; i++)
    {
        int pid = fork();

        if (pid == 0)
        {
            int command_no = i;
            int prev_pipe = ((command_no - 1) % 2) * 2;
            int current_pipe = (command_no % 2) * 2;
            printf("cmd %d: prev pipe %d, curr pipe %d\n", i, prev_pipe, current_pipe);
            fflush(stdout);

            // If current command is the first command, close the
            // read end, else read from the last command's pipe
            if (command_no == 0)
            {
                close(pipefd[0]);
            }
            else
            {
                dup2(pipefd[prev_pipe], 0);
                close(pipefd[current_pipe]);  // Line 40
            }

            // If current command is the last command, close the
            // write end, else write to the pipe
            if (command_no == n_commands - 1)
                close(pipefd[current_pipe + 1]);  // Line 46
            else
                dup2(pipefd[current_pipe + 1], 1);

            execvp(cmds[i][0], cmds[i]);
            fprintf(stderr, "Failed to exec: %s (%d: %s)\n", cmds[i][0], errno, strerror(errno));
            _exit(1);
        }
    }

    return 0;
}


当GCC 4.7.1(在Mac OS X 10.7.4上)进行编译时,它会发出警告:

pipes-12133858.c: In function ‘main’:
pipes-12133858.c:40:22: warning: array subscript is above array bounds [-Warray-bounds]
pipes-12133858.c:46:22: warning: array subscript is above array bounds [-Warray-bounds]


运行它时,我得到输出:

Isis JL: pipes-12133858
cmd 0: prev pipe -2, curr pipe 0
cmd 1: prev pipe 0, curr pipe 2
Isis JL: wc: stdin: read: Bad file descriptor


由于代码中的父级不等待子级完成,因此提示出现在wc的错误消息之前,但是打印的诊断数字表明存在各种问题(并且编译器能够发现一些问题。问题)。

注意,不需要检查任何exec*()函数族的返回值。如果成功,他们将不会返回。如果他们回来,他们就会失败。在调用_exit(0);之前也不需要关闭,因为系统仍然会关闭它们。同样,当您无法执行某项操作时,打印一条消息指示您执行失败的内容并以非零退出状态退出也是很礼貌的。

因此,正如Michał Górny所说,问题的主要部分是您的管道处理代码至少是神秘的,因为您没有显示它并且可能是错误的。

我还可以容忍您的代码中没有足够的close()调用。原则上,在每个将打开管道且将成为管道一部分的进程中,在任何给定的子进程使用pipe()函数之前,应关闭exec*()系统调用返回的所有文件描述符。不关闭管道会导致进程挂起,因为管道的写端是打开的。如果打开写端的进程是尝试从管道的读端读取的进程,则它将不会找到任何要读取的数据。

关于c - ls |使用C的wc无法正常工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12133858/

10-13 07:34