将printf重定向到fopencookie
在Linux上(Raspbian在Raspberry Pi上),我希望这样,我的C应用程序使用将printf重定向到fopencookie,c,printf,raspbian,glibc,stdio,C,Printf,Raspbian,Glibc,Stdio,在Linux上(Raspbian在Raspberry Pi上),我希望这样,我的C应用程序使用printf打印的任何内容都会在回调中发送回我 (不,我不是说用>一些_file.txt进行shell重定向。我是说一个C程序自己决定将标准输出发送到同一程序中的回调中(因此,printf) (是的,我真的很想这样做。我正在使用OpenGL制作一个全屏程序,并希望使用我自己的渲染代码向该程序中的用户呈现任何printf'd文本。用其他东西替换所有printf调用是不可行的。) 我觉得这应该很容易。这个问
printf
打印的任何内容都会在回调中发送回我
(不,我不是说用>一些_file.txt
进行shell重定向。我是说一个C程序自己决定将标准输出发送到同一程序中的回调中(因此,printf
)
(是的,我真的很想这样做。我正在使用OpenGL制作一个全屏程序,并希望使用我自己的渲染代码向该程序中的用户呈现任何printf
'd文本。用其他东西替换所有printf
调用是不可行的。)
我觉得这应该很容易。这个问题在StackOverflow上已经有了变化,但我发现没有一个是完全相同的
我可以使用fopencookie
获取一个文件*
,该文件最终调用我的回调函数。到目前为止,一切顺利。挑战在于让stdout
和printf
去那里
我不能使用freopen
,因为它需要字符串路径。我想重定向到的文件*
不是文件系统上的文件,而是在运行时存在的
我无法使用dup2
,因为fopencookie
中的文件*
没有文件描述符(fileno
返回-1)
建议我可以简单地将stdout
重新分配到我的新文件*
:“stdin、stdout和stderr是普通变量,您可以像其他任何变量一样进行设置。”。这个几乎可以工作。使用fprintf(stdout,“whatever”)
打印的任何内容都会转到我的回调,具有任何格式说明符的printf
也会转到我的回调。但是,任何对printf
的调用,只要字符串中没有格式说明符,都会转到“原始”标准输出
我怎样才能实现我想做的事
附言:我不在乎便携性。这将仅在我当前的环境中运行
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdarg.h>
#include <alloca.h>
#include <string.h>
static ssize_t my_write_func (void * cookie, const char * buf, size_t size)
{
fprintf (stderr, "my_write_func received %d bytes\n", size);
char * copy = (char*) alloca (size + 1);
assert (copy);
copy[size] = 0;
strncpy (copy, buf, size);
fprintf (stderr, "Text is: \"%s\"\n", copy);
fflush (stderr);
return size;
}
static FILE * create_opencookie ()
{
cookie_io_functions_t funcs;
memset (&funcs, 0, sizeof (funcs));
funcs.write = my_write_func;
FILE * f = fopencookie (NULL, "w", funcs);
assert (f);
return f;
}
int main (int argc, char ** argv)
{
FILE * f = create_opencookie ();
fclose (stdout);
stdout = f;
// These two DO go to my callback:
fprintf (stdout, "This is a long string, fprintf'd to stdout\n");
printf ("Hello world, this is a printf with a digit: %d\n", 123);
// This does not go to my callback.
// If I omit the fclose above then it gets printed to the console.
printf ("Hello world, this is plain printf.\n");
fflush (NULL);
return 0;
}
定义GNU源
#包括
#包括
#包括
#包括
#包括
#包括
静态ssize_t my_write_func(void*cookie,const char*buf,size_t size)
{
fprintf(stderr,“my_write_func接收到%d字节\n”,大小);
char*copy=(char*)alloca(大小+1);
断言(副本);
复制[大小]=0;
strncpy(副本、基本单位、大小);
fprintf(标准格式,“文本为:\%s\”\n”,副本);
fflush(stderr);
返回大小;
}
静态文件*创建\u opencookie()
{
cookie\u io\u函数\u t函数;
memset(&funcs,0,sizeof(funcs));
funcs.write=my_write_func;
文件*f=fopencookie(空,“w”,funcs);
断言(f);
返回f;
}
int main(int argc,字符**argv)
{
FILE*f=create_opencookie();
fclose(stdout);
stdout=f;
//这两个确实会转到我的回调:
fprintf(stdout,“这是一个长字符串,fprintf'd to stdout\n”);
printf(“你好,世界,这是一个数字为:%d\n”的printf,123);
//这不属于我的回拨。
//如果我省略上面的fclose,那么它将被打印到控制台。
printf(“你好,世界,这是纯printf。\n”);
fflush(空);
返回0;
}
这似乎是GLIBC中的一个bug
printf(“简单字符串”)
与printf(“foo%d”,123)
工作方式不同的原因是,GCC将前者转换为put
,并认为它们是等价的
据我所知,它们应该是等价的。声明将
输出放入标准输出
,就像printf
一样
然而,在GLIBCprintf
中,输出到stdout
,但是将输出放到\u IO\u stdout
,这些是不等价的
要解决此错误,可以使用-fno builtin printf
标志进行构建。这防止GCC将printf
转换为put
,并在我的系统上生成:
$ ./a.out
my_write_func received 126 bytes
Text is: "This is a long string, fprintf'd to stdout
Hello world, this is a printf with a digit: 123
Hello world, this is plain printf.
"
这个解决方法当然是不完整的:如果调用put
setvbuf (f, NULL, _IOLBF, 0); // line buffered
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
// this is the original program which you can't change
void print(void) {
printf("Hello, %d\n", 123);
puts("world");
printf("xyz");
}
int p[2];
void *render(void *arg) {
int nread;
char buf[1];
while((nread = read(p[0], buf, sizeof buf)) > 0) {
// process the written data, in this case - make it uppercase and write to stderr
for(int i = 0; i < nread; i++)
buf[i] = toupper(buf[i]);
write(2, buf, nread);
}
return NULL;
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
pipe(p);
dup2(p[1], 1);
close(p[1]);
pthread_t t;
pthread_create(&t, NULL, render, NULL);
print();
close(1);
pthread_join(t, NULL);
}