我在使用dup2系统调用将STDOUT重定向到文件时遇到了一个奇怪的问题。
我正在使用我在这里找到的2个功能: In C how do you redirect stdin/stdout/stderr to files when making an execvp() or similar call?
下面是我编写的一个简单程序,用于在出错后测试函数。 程序按预期工作,并将输入写入文件。
int fd;
fpos_t pos;
int main(){
while(1){
char input[100];
printf("Please enter text: ");
gets(input);
printf("\nString = %s\n", input);
switchStdout("test.txt");
puts("THIS TEXT SHOULD REDIRECT\n");
printf("String(file) = %s\n", input);
revertStdout();
puts("This should come before the gets() ??\n");
}
return 0;
}
void switchStdout(const char *newStream)
{
fflush(stdout);
fgetpos(stdout, &pos);
fd = dup(fileno(stdout));
freopen(newStream, "w", stdout);
return;
}
void revertStdout()
{
fflush(stdout);
dup2(fd, fileno(stdout));
close(fd);
clearerr(stdout);
fsetpos(stdout, &pos);
}
调用revertStdout()函数后,程序似乎挂起。
我意识到,实际上,程序在打印之前已经调用了gets()“这应该在gets()之前发生?”
输入文字后,程序会打印跳过的行。
这是我用粗体输入的终端输出:
请输入文字: Hello !!!!
String = Hello !!!!
为什么我能在这里打字?
之前
这应该在gets()??请输入文字:
String =为什么我能在这里输入?
很抱歉这篇长篇文章。程序按预期写入文件。
感谢任何人提供的任何帮助。
答案 0 :(得分:0)
基本上,当你在标准图书馆后面偷偷溜走时会发生什么。
引用man setbuf
:
通常所有文件都是块缓冲的。在文件上发生第一次I / O操作时,将调用
malloc(3)
,并获取缓冲区。如果流引用终端(正如stdout
通常那样),则它是行缓冲的。
所以,你的stdout
开始引用你的终端,并且是行缓冲的。然后你freopen
引用一个文件,所以(作为重新打开流的一部分),它变成块缓冲。然后你dup2
fd,所以现在再次引用终端,但它仍然是同一个流;标准库无法知道您已达到stdout
的内容并改变了它所指的内容。所以它保持块缓冲,导致意外行为。
在第一次输出后更改流的缓冲会导致未定义的行为,但是如果有一些C库实现,则缓冲区为空(因为它将在调用fflush
之后)。你不应该指望这种行为。但是,您可以在freopen
之后立即再次stdout
dup2
流,将NULL第一个参数传递给freopen
,从而为标准库提供重新初始化的机会。 / p>
对于它的价值,fflush
中的switchStdout
来电是不必要的,因为freopen
有效地关闭了流。如果您将freopen
添加到revertStdout
,则会重置错误和文件结束指示符,因此您可以移除对clearerr
的调用。