2013-07-26 21 views
0

我目前正试图理解dup2和C管道的合并,但是即使最简单的程序似乎也不行。已经在阅读示例代码时,我很困惑他们什么时候关闭管道的末端以及输出应该在哪里打印。使用dup2和pipe时,必须订购什么东西?

有时write end is closed,即使应该产生一行后面的输出应该进入管道。在其他例子中,the unused end is closed(这对我更有意义)。

然后,我不明白dup2何时应该执行。我想它应该变成之前我想要重定向的输出,但我觉得我今天也看到了不同的感觉。

所以最后我想出了这个小测试,每行都有printffflush,没有任何东西通过管道重定向。为什么?我究竟做错了什么?

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

int main(void) { 
    int out_pipe[2]; 
    char *output[101]; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello"); 
    fflush(stdout); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello"); 
    fflush(stdout); 

    close(out_pipe[1]); 
    printf("Hello"); 
    fflush(stdout); 

    read(out_pipe[0], output, 100); 
    close(out_pipe[0]); 

    printf("PIPE: %s", output); 
    fflush(stdout); 

    return 0; 
} 

回答

4
  1. 结束您printf()消息用换行; fflush()仍然是一个好主意,因为您即将更改标准输出的位置,但如果程序的标准输出传送到终端通常不是必需的。如果标准输出正在进行文件处理,并且fflush()未到位,那么您将得到三份"Hello\n"写入管道的副本。

  2. 当您将标准输出更改为管道时,您的消息确实被写入管道。

  3. 关闭写入文件描述符时,不会遇到任何问题。然后你写第二个Hello到管道。您需要这个fflush()以确保标准I/O包实际上已将其缓冲数据写入管道。

  4. 然后您从管道读入缓冲区output。您应该检查您读取的字节数,因为字符串不会以null结尾。您应该读取10个字节(当消息中没有任何换行符时)。

  5. 然后用PIPE:前缀再次写入管道。

要修复,请将消息写入标准错误。

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

int main(void) 
{ 
    int out_pipe[2]; 
    char output[101]; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello\n"); 
    fflush(stdout); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello\n"); 
    fflush(stdout); 

    close(out_pipe[1]); 
    printf("Hello\n"); 
    fflush(stdout); 

    int n = read(out_pipe[0], output, sizeof(output)); 
    close(out_pipe[0]); 

    fprintf(stderr, "PIPE: %.*s\n", n, output); 

    return 0; 
} 

注意,我改变的output定义从char *数组的char一个简单的数组。有了变化,我得到的输出:

$ ./pipe3 
Hello 
PIPE: Hello 
Hello 

$ 

那是因为我包含在写入管道消息换行,以及在标准错误结束的格式字符串。


有没有一种可能,以 “重新启用” 标准输出?

是的;使用dup2()之前简单地保持标准输出原始的文件描述符的副本,然后恢复副本,一旦你与管道完成。

我已经删除了两个主要fflush()电话,和样品输出演示端和输出文件之间的差异:

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

int main(void) 
{ 
    int out_pipe[2]; 
    char output[101]; 
    int old_stdout; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello\n"); 

    old_stdout = dup(STDOUT_FILENO); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello\n"); 

    close(out_pipe[1]); 
    printf("Hello\n"); 
    fflush(stdout); 

    int n = read(out_pipe[0], output, sizeof(output)); 
    close(out_pipe[0]); 

    dup2(old_stdout, STDOUT_FILENO); 
    printf("PIPE: %d <<%.*s>>\n", n, n, output); 

    return 0; 
} 

样品输出:

$ ./pipe3Hello 
PIPE: 12 <<Hello 
Hello 
>> 
$./pipe3 > output 
'pipe3' is up to date. 
$ cat output 
PIPE: 18 <<Hello 
Hello 
Hello 
>> 
$ 

如果去除剩余的fflush() ,程序挂起。有没有在管(因为标准I/O并没有刷新其缓冲区,因为它不是完整的,输出不是终端更多),但管道是对写开放,所以内核认为输入可能会出现在它上面—如果只有管道打开的程序未在管道的读取端等待输入显示。该计划本身已陷入僵局。

+0

是啊,这工作。是否有可能“重新启用”stdout?其实我试图在使用ncurses的时候存储一些stdout消息(它会忽略这些消息,因为它使用stdout本身)。所以我想把这些消息发送给一个字符串,然后用'stradd()'将它们发布到ncurses。 – Aufziehvogel

+0

我刚想到的一个想法是分叉,然后等待stdout和管道回到原始程序。我认为这应该起作用,因为原始线程仍然具有标准输出,不是吗?在那里,我可以从管道的读端读取它。 – Aufziehvogel

+0

如果你使用'fork()',你有两个进程(每个进程只有一个控制线程),而不是你的注释所暗示的一个进程中的两个线程。是的,您可以在创建管道之后进行分叉,让子进行I/O重定向并将管道中的信息写入父级,然后让父级读取该数据并使用它。谨慎对待诅咒,虽然—,它听起来很可疑,好像你可能会给它半无法消化的数据来显示,这可能破坏它对屏幕上什么位置的理解。 –

相关问题