结束您printf()
消息用换行; fflush()
仍然是一个好主意,因为您即将更改标准输出的位置,但如果程序的标准输出传送到终端通常不是必需的。如果标准输出正在进行文件处理,并且fflush()
未到位,那么您将得到三份"Hello\n"
写入管道的副本。
当您将标准输出更改为管道时,您的消息确实被写入管道。
关闭写入文件描述符时,不会遇到任何问题。然后你写第二个Hello
到管道。您需要这个fflush()
以确保标准I/O包实际上已将其缓冲数据写入管道。
然后您从管道读入缓冲区output
。您应该检查您读取的字节数,因为字符串不会以null结尾。您应该读取10个字节(当消息中没有任何换行符时)。
然后用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并没有刷新其缓冲区,因为它不是完整的,输出不是终端更多),但管道是对写开放,所以内核认为输入可能会出现在它上面—如果只有管道打开的程序未在管道的读取端等待输入显示。该计划本身已陷入僵局。
是啊,这工作。是否有可能“重新启用”stdout?其实我试图在使用ncurses的时候存储一些stdout消息(它会忽略这些消息,因为它使用stdout本身)。所以我想把这些消息发送给一个字符串,然后用'stradd()'将它们发布到ncurses。 – Aufziehvogel
我刚想到的一个想法是分叉,然后等待stdout和管道回到原始程序。我认为这应该起作用,因为原始线程仍然具有标准输出,不是吗?在那里,我可以从管道的读端读取它。 – Aufziehvogel
如果你使用'fork()',你有两个进程(每个进程只有一个控制线程),而不是你的注释所暗示的一个进程中的两个线程。是的,您可以在创建管道之后进行分叉,让子进行I/O重定向并将管道中的信息写入父级,然后让父级读取该数据并使用它。谨慎对待诅咒,虽然—,它听起来很可疑,好像你可能会给它半无法消化的数据来显示,这可能破坏它对屏幕上什么位置的理解。 –