使用dup2 sys调用重定向输出的奇怪问题

时间:2017-04-03 21:05:51

标签: c unix redirect

我在使用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 =为什么我能在这里输入?

很抱歉这篇长篇文章。程序按预期写入文件。

感谢任何人提供的任何帮助。

1 个答案:

答案 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的调用。