从C重新路由stdin和stdout


67

我想重新打开stdinand stdout(也许stderr在我在的时候)文件句柄,以便将来对printf()orputchar()或or的调用puts()将转到文件,而将来对getc()诸如此类的调用将来自文件。

1)我不想永久失去标准的输入/输出/错误。我可能想稍后在程序中重用它们。

2)我不想打开新的文件句柄,因为这些文件句柄将必须大量传递或全局传递(颤抖)。

3)如果我不能帮忙,我不想使用任何open()fork()其他依赖于系统的功能。

因此,基本上,这样做是否有效:

而且,如果可以,我如何获得stdinback的原始值?我是否必须将其存储在中FILE *并稍后再取回?


1
需要注意的是stdinstdoutstderr是全局变量。
亚历克西斯·威尔克

Answers:


88

为什么要使用freopen()?C89规范在以下部分的尾注之一中给出了答案<stdio.h>

116.的主要用途freopen功能是改变与标准文本流相关联的文件(stderrstdin,或stdout),如那些标识符不必是可修改的左值,由所述返回的值fopen函数可以被分配。

freopen通常被滥用,例如stdin = freopen("newin", "r", stdin);。这不比fclose(stdin); stdin = fopen("newin", "r");。这两个表达式都试图分配给stdin,但不能保证可分配该。

正确的使用方法freopen是忽略分配:freopen("newin", "r", stdin);


2
@AbiusX我不确定它的可移植性,但是在POSIX环境下,您可以使用另一种方法-通过更改基础句柄的含义 dup2()linux.die.net/man/3/fflush。这样一来,您可以在替换STDOUT手柄之前先对其进行复制,然后再进行复制。请记住fflush()。有这样一个例子被用来在这里叉:stackoverflow.com/questions/9405985/...
菲利普·库寿龄

17

我认为您正在寻找类似的东西 freopen()


stdin = freopen(“ newin”,“ r”,stdin)之间有什么区别?和fclose(stdin); stdin = fopen(“ newin”,“ r”); 或没有?
克里斯·卢兹

fopen打开文件对象,而freopen将数据流指向该文件对象。从页面“此功能对于将预定义流(例如stdin,stdout和stderr重定向到特定文件特别有用)”
John T 2009年

1
除了stdin = freopen(“ / dev / tty”,“ r”);以外,还有什么方法可以将stdin / stdout重新打开为其原始值。或者其他的东西?
克里斯·卢茨

1
您将fclose与指向文件的流一起使用。fclose(stdout);
约翰T

1
说明性:语法上分配给stdin,如stdin = fopen(...)或中的stdin = freopen(...),不是可移植的,因为stdin允许为宏。stdio.h可能包含#define stdin __builtin_get_stdin(),当然您不能将其放在作业的LHS中。@ChrisLutz,正确的用法freopen很简单if (freopen("newin", "r", stdin) != NULL) ...---返回值可用于指示打开是否失败,但是通常不需要将其分配给任何东西。
Quuxplusone

10

这是Tim Post方法的修改版本;我使用/ dev / tty而不是/ dev / stdout。我不知道为什么它不适用于stdout(这是/ proc / self / fd / 1的链接):

通过使用/ dev / tty,输出将重定向到启动应用程序的终端。

希望此信息有用。


9

os函数dup2()应该提供您所需要的内容(如果未完全引用您所需要的内容)。

更具体地说,您可以将stdin文件描述符dup2()转换为另一个文件描述符,使用stdin进行其他操作,然后在需要时将其复制回去。

dup()函数复制一个打开的文件描述符。具体来说,它使用F_DUPFD常数命令值(其中第三个参数为0)为fcntl()函数提供的服务提供了备用接口。复制的文件描述符与原始文件共享任何锁。

成功后,dup()返回一个新文件描述符,该文件描述符与原始文件描述符具有以下共同点:

  • 相同的打开文件(或管道)
  • 相同的文件指针(两个文件描述符共享一个文件指针)
  • 相同的访问模式(读,写或读/写)

1
问题明确指出,不过,不要使用与系统有关的功能。
放松

问题明确指出限制开放流/文件描述符的数量
Tim Post

9

这使我的圆头方孔O型表上的针达到峰值,您要完成什么?

编辑:

请记住,对于每个新创建的进程,stdin,stdout和stderr分别是文件描述符0、1和2。freopen()应该保持相同的fd,只需为其分配新的流。

因此,确保此功能确实在做您想要做的一个好方法是:

我相信这是freopen()的预期行为,如您所见,您仍然仅使用三个文件描述符(和关联的流)。

这将覆盖任何Shell重定向,因为Shell不会进行任何重定向。但是,它可能会破坏管道。您可能需要确保为SIGPIPE设置处理程序,以防程序在管道(不是FIFO,管道)的阻塞端发现自己。

因此,。/ your_program --stdout /tmp/stdout.txt --stderr /tmp/stderr.txt应该使用freopen()轻松实现,并保持相同的实际文件描述符。我不明白的是,为什么更改它们后需要将它们放回原处?当然,如果有人通过了这两个选项,他们会希望它持续到程序终止?


我正在尝试为我的程序添加一种方法来重定向它自己的标准输入和输出,以供那些不熟悉Shell重定向的人使用。
克里斯·卢兹

是的,freopen()将成为您的朋友。
蒂姆·波斯特

正如您所描述的那样,这不起作用。我使用了以下代码:printf(“这应该在屏幕上可见!\ n”); freopen(“ testop.txt”,“ w”,stdout);printf(“这应该转到testop!\ n”);fflush(标准输出); fclose(stdout); freopen(“ / dev / stdout”,“ w”,stdout); printf(“这应该再次显示在屏幕上!\ n”);
VectorVictor

@VectorVictor Odd,下班后我将再次运行代码(我肯定在2009年发布此代码时对它进行了测试)。我将使用现代编译器再次进行评估。
蒂姆·波斯特

嗨,蒂姆。我上一篇文章中的评论输入遇到了麻烦。由于某种原因,它不喜欢复制粘贴。想继续列出输出并提供sys详细信息,但在我准备好之前就发送了。前两个指令的作用与预期的一样,但是最后一个指令“然后应该再次显示在屏幕上”却没有。我正在Mac OS X 10.9.5的XCode clang 500.2.79上编译。我什至没有高兴地尝试在/ dev / tty中重新打开stdout。好像重新定义了stdout一样,游戏结束了。
VectorVictor

3

freopen解决了容易的部分。围绕保持老标准输入并不难,如果你没有看过任何东西,如果你愿意使用POSIX系统调用像dupdup2。如果您开始阅读它,那么所有的赌注都关闭了。

也许您可以告诉我们发生此问题的背景?

我会鼓励你坚持,你是愿意放弃旧的情况stdin,并stdout因此可以使用freopen


在上下文中,我认为如果确实需要,我可以放弃旧的stdin / stdout。我正在尝试向程序中添加一个选项,以便程序可以重定向stdin和stdout本身,以供正在运行该程序但不太了解其shell的人员使用。只为它的地狱。
克里斯·卢茨

1
我的学生肯定在I / O重定向方面遇到了麻烦,因此您的目标听起来不错。也许最好使用命令行选项-i和-o命名输入和输出文件?
诺曼·拉姆齐

那或多或少是我的计划(-i和-o已被采用,但这是通用接口),我只需要一种实现它的方法(不必将我的所有printf()更改为fprintf()并传递两种方式处理两个文件句柄)。另外,用于stderr重路由的标志会很好。
克里斯·卢兹

1
从长远来看,传递文件句柄会很高兴,但是如果您有旧代码,则freopen()是您的朋友。
诺曼·拉姆齐

3

同时,有一个C源代码库将为您完成所有这些操作,从而重定向stdout或stderr。但是很酷的部分是,它可以让您为拦截的流分配任意数量的回调函数,然后轻松地将一条消息发送到多个目标,数据库,文本文件等。

最重要的是,创建看起来和行为与stdout和stderr相同的新流变得很简单,您还可以在其中将这些新流重定向到多个位置。

在* oogle上查找U-Streams C库。


0

这是最容易获得,方便且有用的方法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.