关闭标准输出和标准输入文件描述符后重新打开它们

37
我正在编写一个函数,根据给定的参数,将stdout重定向到文件或从文件中读取stdin。为了实现这一点,我关闭与stdout或stdin关联的文件描述符,这样当我打开文件时,它会在我刚刚关闭的描述符下打开。这个方法是有效的,但问题是一旦完成这个操作,我需要将stdout和stdin恢复到它们真正应该是的状态。
对于stdout,我可以使用open("/dev/tty",O_WRONLY);,但我不确定为什么这样可以,更重要的是,我不知道stdin的等效语句。
所以,对于stdout,我有以下解决方案:
close(1);
if (creat(filePath, O_RDWR) == -1)
{
    exit(1);
}

和标准输入

close(0);
if (open(filePath, O_RDONLY) == -1)
{
    exit(1);
}

O_RDWR 用于 open 函数的 flags 参数,而非 creat 函数的 mode 参数。 - Werner Henze
3个回答

46

您应该使用dup()和dup2()来克隆文件描述符。

int stdin_copy = dup(0);
int stdout_copy = dup(1);
close(0);
close(1);

int file1 = open(...);
int file2 = open(...);

< do your work. file1 and file2 must be 0 and 1, because open always returns lowest unused fd >

close(file1);
close(file2);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);

然而,有一个小细节需要注意(来自dup的man文档):

  

这两个描述符不共享文件描述符标志(close-on-exec标志)。 复制描述符的close-on-exec标志(FD_CLOEXEC;请参见fcntl(2))已关闭。

如果这是一个问题,您可能需要恢复close-on-exec标志,可能使用dup3()而不是dup2()以避免竞争条件。

此外,请注意如果您的程序是多线程的,则其他线程可能会意外写入/读取重新映射的stdin / stdout。


2
如果您是多线程的话,还会有其他问题 - 如果另一个线程在您的 close(0)(或 close(1))和相应的 open(...) 之间打开了一个文件,则其文件将成为标准输入(或标准输出)。最好使用 dup2()(或 dup3)来强制文件描述符为0(或1),而不是依赖于“最低未使用”的行为。 - psmears
有没有不克隆它的方法(假设我们不能使用dup或dup2)? - milad

21
我认为您可以在重定向之前“保存”描述符:

int save_in, save_out;

save_in = dup(STDIN_FILENO);
save_out = dup(STDOUT_FILENO);

之后,您可以使用 dup2 来恢复它们:
/* Time passes, STDIN_FILENO isn't what it used to be. */
dup2(save_in, STDIN_FILENO);

在这个例子中,我没有进行任何错误检查 - 你应该加上。


请提供要翻译的英文文本。 - Michael Ambrus

0
你可以创建一个子进程,并仅在子进程内设置重定向。然后等待子进程终止,继续在父进程中工作。这样,您就不必担心撤消重定向了。
只需查找使用fork()和wait()的代码示例即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接