C++ 将posix pipe()和dup()与C++;重定向I/O问题

C++ 将posix pipe()和dup()与C++;重定向I/O问题,c++,shell,posix,pipe,C++,Shell,Posix,Pipe,为了处理I/O重定向,我不得不修改我为之前的家庭作业编写的一个简单shell,而我在让管道正常工作方面遇到了麻烦。似乎在复制了单独进程中的文件描述符之后,当我写入和读取stdout和stdin时,管道工作,但是如果我使用printf、fprintf、get、fgets等来尝试查看输出是否显示在管道中,即使stdin和stdout的文件描述符显然是管道的副本,它也会转到控制台(我不知道这是否是正确的说法,但我认为重点很清楚) 我有99.9%的把握,至少在普通C语言中,我在做所有应该做的事情——比如

为了处理I/O重定向,我不得不修改我为之前的家庭作业编写的一个简单shell,而我在让管道正常工作方面遇到了麻烦。似乎在复制了单独进程中的文件描述符之后,当我写入和读取stdout和stdin时,管道工作,但是如果我使用printf、fprintf、get、fgets等来尝试查看输出是否显示在管道中,即使stdin和stdout的文件描述符显然是管道的副本,它也会转到控制台(我不知道这是否是正确的说法,但我认为重点很清楚)

我有99.9%的把握,至少在普通C语言中,我在做所有应该做的事情——比如在dup()之后适当地关闭所有文件描述符——并且文件I/O工作正常,所以这似乎是一个我不知道也找不到任何信息的细节问题。我花了一天的大部分时间在尝试不同的事情,在过去的几个小时里,我在谷歌上搜索,试图找出是否可以将cin和cout重定向到管道上,看看这是否能解决问题,但在这一点上,这似乎比它的价值更麻烦

既然cin和cout应该与stdio同步,那么仅仅通过重定向stdin和stdout就可以了吗?我认为应该这样做,特别是因为这些命令可能是用C编写的,所以它们会使用stdio。但是,如果我尝试“cat[file1][file2]| sort”这样的命令,它会将cat[file1][file2]的结果打印到命令行,而sort不会获得任何输入,因此没有输出。很明显,cout和cin也不受dup()的影响,所以我把二和二放在一起,得出了这个结论 这是我代码的一个略为缩短的版本,减去了所有的错误检查和诸如此类的事情,我相信我处理得很好。我可以发布完整的代码,如果它来了,但它是很多,所以我将从这个开始

我重写了函数,以便父进程为每个命令派生一个子进程,并根据需要将它们与管道连接,然后等待子进程死亡。同样,文件描述符0和1上的写入和读取工作(即写入管道和从管道读取),文件指针stdin和stdout上的stdio不工作(不写入管道)

非常感谢,这让我很难受

更新:我没有为每个不同的命令更改字符串cmd,所以它似乎不起作用,因为管道只是转到同一个命令,所以最终输出是相同的。。。对不起,我的沉默,但谢谢你,因为我发现了斯特拉斯的问题

int call_execv( string cmd, vector<string> &argv, int argc,
    vector<int> &redirect)
{
    int result = 0, pid, /* some other declarations */;
    bool file_in, file_out, pipe_in, pipe_out;
    queue<int*> pipes; // never has more than 2 pipes

    // parse, fork, exec, & loop if there's a pipe until no more pipes
    do
    {
        /* some declarations for variables used in parsing */
        file_in = file_out = pipe_in = pipe_out = false;

        // parse the next command and set some flags
        while( /* there's more redirection */ )
        {
            string symbol = /* next redirection symbol */

            if( symbol == ">" )
            {
                /* set flags, get filename, etc */
            }   
            else if( symbol == "<" )
            {
                /* set flags, get filename, etc */
            }
            else if( pipe_out = (symbol == "|") )
            {
                    /* set flags, and... */
                int tempPipes[2];               
                pipes.push( pipe(tempPipes) );
                break;
            }
        }

        /* ... set some more flags ... */

        // fork child
        pid = fork();
        if( pid == 0 )  // child
        {
            /* if pipe_in and pipe_out set, there are two pipes in queue. 
            the old pipes read  is dup'd to stdin, and the new pipes
            write is dup'd to stdout, other two FD's are closed */

            /* if only pipe_in or pipe_out, there is one pipe in queue.
            the unused end is closed in whichever if statement evaluates */

            /* if neither pipe_in or pipe_out is set, no pipe in queue */

            // redirect stdout
            if( pipe_out ){
                // close newest pipes read end
                close( pipes.back()[P_READ] );

                // dup the newest pipes write end
                dup2( pipes.back()[P_WRITE], STDOUT_FILENO );

                // close newest pipes write end
                close( pipes.back()[P_WRITE] );
            }
            else if( file_out ) 
                freopen(outfile.c_str(), "w", stdout);

            // redirect stdin
            if( pipe_in ){
                close( pipes.front()[P_WRITE] );
                dup2( pipes.front()[P_READ], STDIN_FILENO );
                close( pipes.front()[P_READ] );
            }
            else if ( file_in ) 
                freopen(infile.c_str(), "r", stdin);


            // create argument list and exec
            char **arglist = make_arglist( argv, start, end );
            execv( cmd.c_str(), arglist );

            cout << "Execution failed." << endl;    
            exit(-1); // this only executes is execv fails

        }   // end child

        /* close the newest pipes write end because child is writing to it.
           the older pipes write end is closed already */
        if( pipe_out ) 
            close( pipes.back()[P_WRITE] );

        // remove pipes that have been read from front of queue
        if( init_count > 0 )    
        {
            close( pipes.front()[P_READ] ); // close FD first
            pipes.pop();                    // pop from queue
        }   

    } while ( pipe_out );

    // wait for each child process to die

    return result;
}
int-call\u-execv(字符串cmd、向量和argv、int-argc、,
向量和重定向)
{
int result=0,pid,/*一些其他声明*/;
输入文件,输出文件,输入文件,输出文件;
队列管道;//从来没有超过2个管道
//如果有管道,则执行parse、fork、exec和loop,直到没有更多管道为止
做
{
/*解析中使用的变量的一些声明*/
文件输入=文件输出=管道输入=管道输出=错误;
//解析下一个命令并设置一些标志
而(/*还有更多重定向*/)
{
字符串符号=/*下一个重定向符号*/
如果(符号==“>”)
{
/*设置标志、获取文件名等*/
}   

否则,如果(symbol==”无论出现什么问题,您都不会检查任何返回值。您如何知道
pipe()或
dup2()是
命令成功?您是否验证了
stdout
stdin
是否确实指向
execv
前面的管道?
execv
是否保留您提供的文件描述符?不确定,以下是
execve
文档中的相应段落:

  • 默认情况下,文件描述符在execve()中保持打开状态。在exec上标记为关闭的文件描述符将关闭;请参阅fcntl(2)中对FD_CLOEXEC的描述。(如果 文件描述符已关闭,这将导致释放通过此进程在基础文件上获得的所有记录锁。有关详细信息,请参阅fcntl(2) 如果文件描述符0、1和2在成功执行execve()后关闭,则进程将获得特权,因为每个 在执行的文件上设置任务位,然后系统可能会为每个文件描述符打开一个未指定的文件。作为一般原则,没有可移植程序, 无论是否具有特权,都可以假定这三个文件描述符将在execve()中保持关闭状态
您应该添加更多的调试输出,看看真正发生了什么。您是否在程序中使用了
strace-f
(跟踪子项)?

以下内容:

queue<int*> pipes; // never has more than 2 pipes
// ...
int tempPipes[2];               
pipes.push( pipe(tempPipes) );

我在最初的帖子中说,“这是我的代码的一个略为缩短的版本,减去所有的错误检查和类似的事情,我相信我处理得很好。”我检查管道上的返回值,fork(即使是-1),所有的DUP和FRE都打开了。我正在尝试strace并注意到一些潜在的问题,因此感谢您的建议,我以前不知道。此外,我相当确定execv保留了文件描述符,因为文件I/O工作正常,我尝试设置FD_CLOEXEC标志,以查看它做了什么,并且在关闭时它会抱怨,这是正确的不正常。哦,还有一些事情。我尝试使用一个文件(使用tmpnam()生成一个唯一的文件名)让子进程以管道的形式写入,然后关闭,然后从管道中读取。我相当确定它的逻辑(就像为单独的“管道”使用单独的文件一样)很好,但我想知道刷新和其他东西是否会有问题,这可能解释了为什么不起作用?另外,要明确的是,文件I/O与freopen一起工作,管道到文件与freopen不起作用,这与管道和dup.Ano不起作用
struct PipeFds
{
    int fds[2];
};

std::queue<PipeFds> pipes;
PipeFds p;
pipe(p.fds); // check the return value
pipes.push(p);