在我的 C 程序中使用 execvp 函数执行时 grep 挂起

grep hangs when executed using execvp function in my C program

我用 C 写了一个 bash 像 Linux shell

(Link 到我的存储库:https://github.com/oneiro-naut/wish .文件src/execute.c里面有execute_pipeline函数)

我在执行生成一些输出的内置命令时遇到问题。不产生任何输出的内置函数如 cdexit 工作得很好。外部命令运行顺利。 Shell 支持管道结构和 I/O 重定向。问题是当我尝试 运行 一个内部命令(它只是打印一条消息)时。当我使用管道将内置命令的输出重定向到 grep 命令时,grep 子进程挂起,我认为这是因为 grep 在从管道读取时从未遇到流(或 EOF)的结尾。

注意:当管道中只有外部命令时,这不会发生。(我已经注意没有(外部)命令在下一个命令尚未开始之前完成,因此它不会挂在那里)

这是我的问题的简化版本 `

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

int main()
{

int pipe_fd[2] = {-1,-1};
int EXIT_STAT = 0;

pipe(pipe_fd);//creating pipe


char* argv[]={"grep","Radiohead",NULL};

int std_out_dup = dup(1);
dup2(pipe_fd[1],1); // redirecting stdout to parent pipe's read end
//close(pipe_fd[0]);//closing parent pipe's write end 

printf("Alice in Chains\n");
printf("Radiohead\n");
printf("Dinosaur Jr.\n");
printf("Pixies\n");
printf("Soundgarden\n");
printf("Porcupine Tree\n");
printf("Pain of Salvation\n");


pid_t pid = fork();
if(pid == 0)//child process
{
dup2(pipe_fd[0],0); //redirecting grep's stdin to parent pipe's write end
close(pipe_fd[1]);
execvp(argv[0],argv);//executing grep command
perror("\n");
exit(-1);
}


else if(pid > 0)//parent process
{

    int i;//closing pipes for parent
    for(i=0;i<2;i++)
        close(pipe_fd[i]);

    do{ // waiting for grep to exit
        waitpid(pid,&EXIT_STAT,WUNTRACED);
        //write(,"\x4",1);
    }while(!WIFEXITED(EXIT_STAT)&&!WIFSIGNALED(EXIT_STAT));
}
else ;//fork error

dup2(std_out_dup,1); // restore stdout fd for parent process



return 0;

}

`
如果我尝试 运行 它而不打印任何内容,程序就会挂起。 但是按 ^D 会终止产生输出的程序:

Alice in Chains Radiohead Dinosaur Jr. Pixies Soundgarden Porcupine Tree Pain of Salvation

所以 grep 似乎无法无限期地读取,因为它没有接收到任何 EOF 信号。 请指出我的错误我真的很困惑。

编辑:现在即使按 ^D 也不会终止,所以我必须只按 ^C 程序。

修正后的程序:

int main()
{

    int pipe_fd[2] = {-1,-1};
    int EXIT_STAT = 0;

    pipe(pipe_fd);//creating pipe


    char* argv[]={"grep","Radiohead",NULL};

    int std_out_dup = dup(1);
    //printf("%d \n",std_out_dup);
    dup2(pipe_fd[1],1); // redirecting stdout to parent pipe's read end
    //close(pipe_fd[0]);//closing parent pipe's write end 

    printf("Alice in Chains\n");
    printf("Radiohead\n");
    printf("Dinosaur Jr.\n");
    printf("Pixies\n");
    printf("Soundgarden\n");
    printf("Porcupine Tree\n");
    printf("Pain of Salvation\n");
    fflush(stdout);

    pid_t pid = fork();
    if(pid == 0)//child process
    {
    close(pipe_fd[1]);
    dup2(pipe_fd[0],0); //redirecting grep's stdin to parent pipe's write end
    dup2(std_out_dup,1); // restore stdout fd for parent process
    execvp(argv[0],argv);//executing grep command
    perror("\n");
    exit(-1);
    }


    else if(pid > 0)//parent process
    {

        int i;//closing pipes for parent

        for(i=0;i<2;i++)
           close(pipe_fd[i]);

        dup2(std_out_dup,1); // restore stdout fd for parent process

        do{ // waiting for grep to exit
            waitpid(pid,&EXIT_STAT,WUNTRACED);
        }while(!WIFEXITED(EXIT_STAT)&&!WIFSIGNALED(EXIT_STAT));
      }
          else ;//fork error



       return 0;
    }

你应该

1.恢复子进程中的stdout;否则 grep 子进程将写入管道的写入端,即它的标准输入。

if(pid == 0)//child process ... {

之后添加一个 dup2(std_out_dup,1)

2. 在所有这些 .... printf("Pain of Salvation\n"); printfs 之后添加一个 fflush(stdout);否则流将被刷新两次。

3.关闭父子管道的所有写端;在父级的 wait 循环之前添加 dup2(std_out_dup,1)close(1)