使用C的| wc不工作。

时间:2021-11-25 13:31:46

I'have written a C program which uses multiple piping to simulate shell. The problem is I can run most of the commands like ls | cat etc, but I'm unable to use ls | wc. Is there any case when wc doesn't work?

我已经编写了一个C程序,它使用多个管道来模拟shell。问题是我可以运行大多数的命令,比如ls | cat等等,但是我不能使用ls | wc。当wc不工作时,有什么情况吗?

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);
    }
}

It seems that the programs from /usr/bin are not being executed if they are not the first command in the pipeline.

如果在管道中不是第一个命令,那么/usr/bin的程序似乎没有被执行。

2 个解决方案

#1


1  

Here's a very simple program created from your code — guessing how pipes might be created and simplifying the command argv handling a bit:

下面是一个非常简单的程序,它创建于您的代码—猜测如何创建管道并简化命令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;
}

When GCC 4.7.1 (on Mac OS X 10.7.4) compiles it, it warns:

当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]

When I run it, I get the output:

当我运行它时,我得到输出:

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

Since the parent in the code does not wait for the children to finish, the prompt appears before the error message from wc, but the diagnostic numbers printed show that there are all sorts of problems (and the compiler was able to spot some of the problems).

由于代码中的父进程不等待孩子完成,所以提示符出现在wc的错误消息之前,但是诊断数字显示存在各种各样的问题(并且编译器能够发现一些问题)。

Note that there is no need to check the return value from any of the exec*() family of functions. If they are successful, they do not return; if they return, they failed. There was also no need for the closes before calling _exit(0); since the system will close them anyway. Also, when you fail to exec something, it is courteous to print a message indicating what you failed to execute and to exit with a non-zero exit status.

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

So, as Michał Górny says, a major part of your problem is that your pipe handling code is at least mysterious because you didn't show it and probably erroneous.

所以,MichałGorny说,你的问题是你管的主要部分处理代码至少是神秘的,因为你并没有显示出这一点,可能错误。

I'm also tolerably sure you don't have enough close() calls in your code. As a guideline, in each process that has pipes open in it that is going to be part of a pipeline, all the file descriptors returned by the pipe() system call should be closed before any given child process uses an exec*() function. Not closing pipes can lead to processes hanging because the write end of a pipe is open. If the process that has the write end open is the process that's trying to read from the read end of the pipe, then it isn't going to find any data to read.

我还可以相当肯定地确定您的代码中没有足够的close()调用。作为一个指导方针,在每个流程中都有管道打开,这将成为管道的一部分,在任何给定的子进程使用exec*()函数之前,管道()系统调用返回的所有文件描述符都应该被关闭。不关闭管道会导致进程挂起,因为管道的写端是打开的。如果有写端打开的进程是试图从管道的读端读取的进程,那么它就不会找到要读取的任何数据。

#2


1  

You are connecting the pipes incorrectly.

你把管道连接错了。

This logic:

这个逻辑:

int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;

doesn't work — the result of modulo will always be 0 or 1, so prev_pipe and current_pipe will be either 0 or 2...

不工作——模块的结果总是0或1,所以prev_pipe和current_pipe将是0或2…

Well, unless I'm missing some hidden concept because you didn't paste any code creating the pipes.

除非我遗漏了一些隐藏的概念,因为你没有粘贴任何创建管道的代码。

#1


1  

Here's a very simple program created from your code — guessing how pipes might be created and simplifying the command argv handling a bit:

下面是一个非常简单的程序,它创建于您的代码—猜测如何创建管道并简化命令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;
}

When GCC 4.7.1 (on Mac OS X 10.7.4) compiles it, it warns:

当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]

When I run it, I get the output:

当我运行它时,我得到输出:

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

Since the parent in the code does not wait for the children to finish, the prompt appears before the error message from wc, but the diagnostic numbers printed show that there are all sorts of problems (and the compiler was able to spot some of the problems).

由于代码中的父进程不等待孩子完成,所以提示符出现在wc的错误消息之前,但是诊断数字显示存在各种各样的问题(并且编译器能够发现一些问题)。

Note that there is no need to check the return value from any of the exec*() family of functions. If they are successful, they do not return; if they return, they failed. There was also no need for the closes before calling _exit(0); since the system will close them anyway. Also, when you fail to exec something, it is courteous to print a message indicating what you failed to execute and to exit with a non-zero exit status.

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

So, as Michał Górny says, a major part of your problem is that your pipe handling code is at least mysterious because you didn't show it and probably erroneous.

所以,MichałGorny说,你的问题是你管的主要部分处理代码至少是神秘的,因为你并没有显示出这一点,可能错误。

I'm also tolerably sure you don't have enough close() calls in your code. As a guideline, in each process that has pipes open in it that is going to be part of a pipeline, all the file descriptors returned by the pipe() system call should be closed before any given child process uses an exec*() function. Not closing pipes can lead to processes hanging because the write end of a pipe is open. If the process that has the write end open is the process that's trying to read from the read end of the pipe, then it isn't going to find any data to read.

我还可以相当肯定地确定您的代码中没有足够的close()调用。作为一个指导方针,在每个流程中都有管道打开,这将成为管道的一部分,在任何给定的子进程使用exec*()函数之前,管道()系统调用返回的所有文件描述符都应该被关闭。不关闭管道会导致进程挂起,因为管道的写端是打开的。如果有写端打开的进程是试图从管道的读端读取的进程,那么它就不会找到要读取的任何数据。

#2


1  

You are connecting the pipes incorrectly.

你把管道连接错了。

This logic:

这个逻辑:

int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;

doesn't work — the result of modulo will always be 0 or 1, so prev_pipe and current_pipe will be either 0 or 2...

不工作——模块的结果总是0或1,所以prev_pipe和current_pipe将是0或2…

Well, unless I'm missing some hidden concept because you didn't paste any code creating the pipes.

除非我遗漏了一些隐藏的概念,因为你没有粘贴任何创建管道的代码。