用fork和exec管道后输出不显示

时间:2017-03-15 21:11:17

标签: c++11 pipe fork exec

我正在尝试创建一个运行一个命令的函数,然后将输出传递给第二个命令并运行它。我在无限循环中运行该函数。问题是,该功能第一次起作用,但之后没有显示任何内容。 例如,当我运行ls | wc -l时,它第一次显示正确的结果,但是当我在那之后运行它时,我没有输出。

这是我的函数(解析在另一个函数中处理):

void system_pipe(std::string command1, std::string command2)
{
    int status;
    int fd[2];
    int fd2[2];
    pipe(fd);

    int pid = fork();
    // Child process.
    if (pid == 0)
    {
        std::shared_ptr<char> temp = string_to_char(command1);
        char *name[] = {"/bin/bash", "-c", temp.get(), NULL};

        close(fd[0]);
        dup2(fd[1], 1);
        execvp(name[0], name);

        exit(EXIT_FAILURE);
    }
    // Parent process.
    else
    {
        std::shared_ptr<char> temp = string_to_char(command2);
        char *name[] = {"/bin/bash", "-c", temp.get(), NULL};   

        close(fd[1]);
        dup2(fd[0], 0);
        waitpid(pid, &status, 0);
        //my_system(command2);

        // Fork and exec a new process here.
        int pid2 = fork();
        if (pid2 == 0)
        {
            execvp(name[0], name);
            exit(EXIT_FAILURE);
        }
        else
        {
            waitpid(pid2, NULL, 0);
        }
    }

    if (status)
        std::cout << "Bad" << std::endl;
}

我这样称呼函数:

while(true)
{
    string line;
    getline(cin, line);
    pair<string, string> commands = parse(line);
    system_pipe(commands.first, commands.second);
}

为什么该功能仅在第一个循环上正常工作?之后发生了什么变化?

1 个答案:

答案 0 :(得分:1)

else
 { 
        std::shared_ptr<char> temp = string_to_char(command2);
        char *name[] = {"/bin/bash", "-c", temp.get(), NULL};   

        close(fd[1]);
        dup2(fd[0], 0);
        waitpid(pid, &status, 0);
        //my_system(command2);

我相信那不是你的意图。必须在孩子中调用dup2。

int pid2 = fork();
if (pid2 == 0)
{
    dup2(fd[0], 0); // here.
    execvp(name[0], name);
    exit(EXIT_FAILURE);
}

第二个问题是您将管道文件保持打开状态。 这不是一个好的编码样本,但它只是为了说明它应该如何工作。

// ( g++ -std=c++11  )
// type `ls | grep file.cxx`
#include <iostream>
#include <string>
#include <cstring>
#include <array> 
#include <memory>

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

void replace_with ( std::string command )
{
    char exec_name [] = "bash" , arg [] = "-c" ;
    char * line [] = { exec_name , arg , &command[ 0 ] , nullptr } ;
    execvp( exec_name , line ) ;
}

void pipeline ( const std::string& command0 , const std::string& command1 )
{
    std::array< int , 2 > pipe_fd ; enum { READ_END , WRITE_END } ; 

    if ( pipe( pipe_fd.data() ) ) throw std::runtime_error( "can't create a pipe" ) ;

    int id = fork() ;
    if ( id < 0 ) { for ( auto each : pipe_fd ) close( each ) ;
                    throw std::runtime_error( "can't create a child" ) ; }

    if ( ! id ) /* child */
    {
        close( pipe_fd[ READ_END ] ) ; 
        dup2( pipe_fd[ WRITE_END ] , STDOUT_FILENO ) ;
        close( pipe_fd[ WRITE_END ] ) ; //

        replace_with( command0 ) ;
    }
    else /* parent */
    {
        close( pipe_fd[ WRITE_END ] ) ;
        waitpid( id , nullptr , 0 ) ;
        int id_second = fork () ;
        if ( id_second > 0 ) waitpid( id_second , nullptr , 0 ) ;
        else if ( ! id_second ) /* child */ 
        {
            dup2( pipe_fd[ READ_END ] , STDIN_FILENO ) ;
            close( pipe_fd[ READ_END ] ) ; //
            replace_with( command1 ) ;
        }

        close( pipe_fd[ READ_END ] ) ;
    }
}

int main () try
{
    while ( true )
    {
        std::string command0 , command1 ;
        getline( std::cin , command0 , '|' ) ;
        getline( std::cin , command1 ) ;
        pipeline( command0 , command1 ) ;
    }

} catch ( const std::runtime_error& e ) 
  { std::cerr << e.what() ; return - 1 ; }