来源:
公众号【编程珠玑】
作者:
守望先生
ID:
shouwangxiansheng
Linux中不可错过的信息宝库
如何理解 Linux shell中“2>&1”?
Linux下你还知道这些特殊文件?
如何查看linux中文件打开情况?
关注公众号【编程珠玑】,获取更多Linux/C/C++/数据结构与算法/计算机基础/工具等原创技术文章。
后台免费获取经典电子书和视频资源
最新内容可点击阅读原文。
$ program > result.txt
这样printf的输出就存储在result.txt中了。
相关内容可以参考《
如何理解Linux shell中“2>&1”
》。
当然了,如果你
既想打印在终端,又想保存在文件
,还可以使用tee命令:
program | tee result.txt
注:
program为你运行的程序。
不过文本介绍了不是通过命令行的方式,而是通过代码实现。
写文件
你可能会想,那不用printf,直接将打印写入到文件不就可以了? 类似于下面这样://来源:公众号编程珠玑 网站:https://www.yanbinghu.com#includeint main(void){ FILE *fp = fopen("log.txt","w+"); if(NULL == fp) { printf("open failed\n"); return -1; } int a = 10; fprintf(fp,"test content %d\n",a); fclose(fp); return 0;}
不过这需要将原先的printf改用fprintf,修改了最原始的代码。但是本文并不是说明如何实现一个logging功能,而是如何将printf的原始打印保存在文件中。
重定向
实际上,我们的程序在运行起来后,都会有三个文件描述符:- 0 标准输入
- 1 标准输出
- 2 标准错误
//test.c#include#include int main(void){ sleep(20);//为了避免立即退出 return 0;}
假设编译出来的程序为test:
$ gcc -o test test.c$ ./test &$ ls -l /proc/`pidof test`/fd
这里关于proc文件系统可以参考《
Linux中不可错过的信息宝库
》,pidof test用于获取test进程id,其fd目录可以看到打开的文件描述符,关于文件打开可以参考《
查看文件打开的多种方式
》。
其输出结果如下(可左右滑动查看完整内容):
lrwx------ 1 root root 64 Nov 16 16:26 0 -> /dev/pts/0lrwx------ 1 root root 64 Nov 16 16:26 1 -> /dev/pts/0lrwx------ 1 root root 64 Nov 16 16:26 2 -> /dev/pts/0
看到了吗,0,1,2都重定向到了/dev/pts/0,其实就是当前终端(参考《
Linux下你还知道这些特殊文件?
》):
$ tty/dev/pts/0
所以如果我们要将printf的打印保存到文件中,实际上就让它重定向到这个文件就可以了。
这里我们用到freopen函数:
FILE *freopen(const char *path, const char *mode, FILE *stream);
参数说明:
- path:需要重定向到的文件名或文件路径。
- mode:代表文件访问权限的字符串。例如,"r"表示“只读访问”、"w"表示“只写访问”、"a"表示“追加写入”。
- stream:需要被重定向的文件流。
//来源:公众号【编程珠玑】#include#include int main(void){ FILE *fp = freopen("test.log","w",stdout); if(NULL == fp) { printf("reopen failed\n"); return -1; } printf("bianchengzhuji\n"); printf("shouwangxiansheng\n"); sleep(20);//便于观察 fclose(fp); return 0;}
重新编译后运行查看其打开的文件描述符:
lrwx------ 1 root root 64 Nov 16 16:34 0 -> /dev/pts/0lrwx------ 1 root root 64 Nov 16 16:34 1 -> /data/workspaces/test.loglrwx------ 1 root root 64 Nov 16 16:34 2 -> /dev/pts/0
看到了吗,它现在重定向到test.log了,并且终端也没有printf的打印。
随后我们也在文件test.log中看到了下面的内容:
bianchengzhuji
有人可能会有下面的疑问:
- 怎么恢复?
int ttyname_r(int fd, char *buf, size_t buflen);
又可以重新定向到/dev/pts/0了:
//来源:公众号【编程珠玑】#include#include int main(void){ char ttyName[128] = {0}; ttyname_r(1,ttyName,128);//1为标准输出 FILE *fp = freopen("test.log","w+",stdout); if(NULL == fp) { printf("reopen failed\n"); return -1; } printf("bianchengzhuji\n"); printf("shouwangxiansheng\n"); sleep(20); freopen(ttyName,"w+",stdout); printf("std out to %s\n",ttyName); fclose(fp); return 0;}
最终运行会发现两个结果:
- std out to 打印到终端
- 打开的文件描述符1被重定向到/dev/pts/0
int dup2(int oldfd, int newfd);
它是用来复制文件描述符的,会使得newfd成为oldfd的副本.所以与上面看到不同的是,标准输出和往fd写入的内容,都会存储在文件test.log中:
//来源:公众号【编程珠玑】#include#include #include #include #include int main(void){ int fd = open("test.log",O_WRONLY|O_CREAT); dup2(fd,1);//1代表标准输出 printf("bianchengzhuji\n"); printf("shouwangxiansheng\n"); sleep(20); close(fd); return 0;}
观察的结果:
lrwx------ 1 root root 64 Nov 17 17:03 0 -> /dev/pts/0l-wx------ 1 root root 64 Nov 17 17:03 1 -> /data/workspaces/test.loglrwx------ 1 root root 64 Nov 17 17:03 2 -> /dev/pts/0l-wx------ 1 root root 64 Nov 17 17:03 3 -> /data/workspaces/test.log
这种情况适合于将标准输出的内容和其他写文件的内容一并保存到文件中。
如何关闭printf打印
实际上非常简单,进程启动后,只需要关闭文件描述符1(标准输出),2(标准错误)即可。什么情况下会需要呢?有些后台进程有自己的日志记录方式,而不想让printf的信息打印在终端,因此可能会关闭。总结
文本旨在通过将printf的打印保存在文件中来 介绍重定向,以及0,1,2文件描述符 。如果你不想保留标准输出,可以将其重定向到/dev/null,如果想保留,且单独保留到特定文件,可以使用freopen,如果想保留,且和其他内容保留到同一文件,使用dup2。如果一行代码都不想动,使用命令行重定向。如果你完全不关心,当我啥都没说。本文相关内容见 相关精彩推荐 。![80e3e87c73518f8b7958595b6e097e6f.png](https://i-blog.csdnimg.cn/blog_migrate/4dfdedd7916ff2075d4d1c16ff331b00.jpeg)
相关精彩推荐
![62324d46a8d2ae260e4bb51fd029591f.png](https://i-blog.csdnimg.cn/blog_migrate/c6beccaea2036f688f07744829c58c62.jpeg)
![b5506aa1c599f741c0e5119dc2bf3c6f.png](https://i-blog.csdnimg.cn/blog_migrate/55b61fdbc85342eb2cc6557073941d18.jpeg)