如何优雅地将printf的打印保存在文件中?
我们都知道,一般使用printf的打印都会直接打印在终端,如果想要保存在文件里呢?我想你可能想到的是重定向。例如:
$ program > result.txt
这样printf的输出就存储在result.txt中了。
当然了,如果你既想打印在终端,又想保存在文件,还可以使用tee命令:
program | tee result.txt
注:program为你运行的程序。
不过文本介绍了不是通过命令行的方式,而是通过代码实现。
写文件
你可能会想,那不用printf,直接将打印写入到文件不就可以了?类似于下面这样:
#include<stdio.h>
int main(void)
{
FILE *fp = fopen("log.txt","w+");
if(NULL == fp)
{
printf("open failed
");
return -1;
}
int a = 10;
fprintf(fp,"test content %d
",a);
fclose(fp);
return 0;
}
不过这需要将原先的printf改用fprintf,修改了最原始的代码。但是本文并不是说明如何实现一个logging功能,而是如何将printf的原始打印保存在文件中。
重定向
实际上,我们的程序在运行起来后,都会有三个文件描述符:
0 标准输入
1 标准输出
2 标准错误
一般标准输出都是都直接输出到终端。
我们可以用一个程序简单观察一下:
#include<stdio.h>
#include <unistd.h>
int main(void)
{
sleep(20);//为了避免立即退出
return 0;
}
假设编译出来的程序为test:
$ gcc -o test test.c
$ ./test &
$ ls -l /proc/`pidof test`/fd
pidof test用于获取test进程id,其fd目录可以看到打开的文件描述符, 其输出结果如下:
lrwx------ 1 root root 64 Nov 16 16:26 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov 16 16:26 1 -> /dev/pts/0
lrwx------ 1 root root 64 Nov 16 16:26 2 -> /dev/pts/0
看到了吗,0,1,2都重定向到了/dev/pts/0,其实就是当前终端。
$ tty
/dev/pts/0
所以如果我们要将printf的打印保存到文件中,实际上就让它重定向到这个文件就可以了。这里我们用到freopen函数:
FILE *freopen(const char *path, const char *mode, FILE *stream);
参数说明:
- path:需要重定向到的文件名或文件路径。
- mode:代表文件访问权限的字符串。例如,"r"表示“只读访问”、"w"表示“只写访问”、"a"表示“追加写入”。
- stream:需要被重定向的文件流。
那么要完成前面的任务也就很简单了:
#include<stdio.h>
#include <unistd.h>
int main(void)
{
FILE *fp = freopen("test.log","w",stdout);
if(NULL == fp)
{
printf("reopen failed
");
return -1;
}
printf("bianchengzhuji
");
printf("shouwangxiansheng
");
sleep(20);//便于观察
fclose(fp);
return 0;
}
重新编译后运行查看其打开的文件描述符:
lrwx------ 1 root root 64 Nov 16 16:34 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov 16 16:34 1 -> /data/workspaces/test.log
lrwx------ 1 root root 64 Nov 16 16:34 2 -> /dev/pts/0
看到了吗,它现在重定向到test.log了,并且终端也没有printf的打印。随后我们也在文件test.log中看到了下面的内容:
bianchengzhuji
有人可能会有下面的疑问:
- 怎么恢复?
首先来看怎么恢复,实际上恢复的原理是类似的,既然最开始它从定向到了/dev/pts/0,那么我们只需要重定向回去就可以了,但是在不同的终端,它的tty名字可能不同,因此需要使用ttyname函数获取原先stdout的tty名字:
int ttyname_r(int fd, char *buf, size_t buflen);
又可以重新定向到/dev/pts/0了:
#include<stdio.h>
#include <unistd.h>
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
");
return -1;
}
printf("bianchengzhuji
");
printf("shouwangxiansheng
");
sleep(20);
freopen(ttyName,"w+",stdout);
printf("std out to %s
",ttyName);
fclose(fp);
return 0;
}
最终运行会发现两个结果:
- std out to 打印到终端
- 打开的文件描述符1被重定向到/dev/pts/0
还有没有别的保存方法呢?
除了上面这种方式,还有一种方式是使用dup2:
int dup2(int oldfd, int newfd);
它是用来复制文件描述符的,会使得newfd成为oldfd的副本.所以与上面看到不同的是,标准输出和往fd写入的内容,都会存储在文件test.log中:
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = open("test.log",O_WRONLY|O_CREAT);
dup2(fd,1);//1代表标准输出
printf("bianchengzhuji
");
printf("shouwangxiansheng
");
sleep(20);
close(fd);
return 0;
}
观察的结果:
lrwx------ 1 root root 64 Nov 17 17:03 0 -> /dev/pts/0
l-wx------ 1 root root 64 Nov 17 17:03 1 -> /data/workspaces/test.log
lrwx------ 1 root root 64 Nov 17 17:03 2 -> /dev/pts/0
l-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。
如何优雅地将printf的打印保存在文件中?的更多相关文章
- 将数组打印到txt文件中
用print_r 将数组打印到txt文件中. 1.function save_log($content='', $file='app') { $logDir = './logs'; $now ...
- 利用POI抽取word中的图片并保存在文件中
利用POI抽取word中的图片并保存在文件中 poi.apache.org/hwpf/quick-guide.html 1.抽取word doc中的图片 package parse; import j ...
- C0302 将一个代码块中的内容保存在文件中, 查看一个rpm包是否可以安装
#!/bin/bash # 这个脚本是用来描述和确认是否可以安装一个rpm包 # 在一个文件中保存输出 SUCCESS=0 E_NOARGS=65 if [ -z "$1" ] t ...
- 使用 python 把一个文件生成 C 语言中的数组并保存到头文件中
(一)要做什么 之前有这么一个需求,是要把一个二进制文件里面的数据,转换成 C 代码里面的数组,可以看之前的一篇文章: NUC980 运行 RT-Thread 驱动 SPI 接口 OLED 播放 ba ...
- Python脚本连接数据库读取特定字段保存在文件中
从Script表中取出Description字段作为文件名,并按协议将脚本归位相同的文件夹,取TestScript字段的内容写入文件 import MySQLdb import sys import ...
- RestAssured打印日志到文件中的方法
参考https://stackoverflow.com/questions/14476112/how-to-get-rest-assured-log-into-something-printable- ...
- ios 将Log日志重定向输出到文件中保存
对于真机,日志没法保存,不好分析问题.所以有必要将日志保存到应用的Docunment目录下,并设置成共享文件,这样才能取出分析. 首先是日志输出,分为c的printf和标准的NSLog输出,print ...
- 把打印的内容保存成文件(PDF)
有时候网页的内容和打印的内容会有一些差异,需要把打印的内容倒出来.是有办法的. 1.以谷歌为内核的浏览器示例,按Ctrl+p快捷键打开打印对话框,如图: 2.点击更改按钮,更改打印机,会出现选择目标打 ...
- Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件
Android日志打印类LogUtils,能够定位到类名,方法名以及出现错误的行数并保存日志文件 在开发中,我们常常用打印log的方式来调试我们的应用.在Java中我们常常使用方法System.out ...
随机推荐
- 最短路径算法:弗洛伊德(Floyd-Warshall)算法
一.算法介绍 Floyd-Warshall算法(英语:Floyd-Warshall algorithm),中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存 ...
- Go语言核心36讲(Go语言进阶技术十)--学习笔记
16 | go语句及其执行规则(上) 我们已经知道,通道(也就是 channel)类型的值,可以被用来以通讯的方式共享数据.更具体地说,它一般被用来在不同的 goroutine 之间传递数据.那么 g ...
- ubuntn 一直循环登录界面 (卸载nvidia驱动)
由于在Ubuntu下安装了Nvidia显卡驱动后开机一直处于循环登录界面,密码输入正确也是进不去,然后就决定卸载Nvidia显卡驱动.首先是在能使用tty1登录的情况下,使用 $ sudo apt-g ...
- 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)
问题描述 使用 python websockets 模块作为Socket的服务端,发布到App Service for Linux环境后,发现Docker Container无法启动.错误消息为: 2 ...
- Ubuntu中python的mysql操作
1.在已经安装了python和MySQL数据库的前提下使用pip3 install PyMySQL命令 2. 建立链接: (1)首先使用命令python 进入编程模式,再导入包: import pym ...
- Typecho 反序列化漏洞 分析及复现
0x00 漏洞简介 CVE-2018-18753 漏洞概述: typecho 是一款非常简洁快速博客 CMS,前台 install.php 文件存在反序列化漏洞,通过构造的反序列化字符串注入可以执行任 ...
- {% csrf_token %} 原理和作用 (踩坑必看)
本博客已暂停更新,请转自新博客 https://www.whbwiki.com/320.html 继续阅读 简介 在django中我们需要在templates的form中加入{%csrf_token% ...
- idea使用git更新代码 : update project(git merge、git rebase)
idea使用git更新代码 : 选中想要更新的项目,右键点击 git => repository => pull 这样使用一次后idea会自动建立选中分支的远程跟踪分支,以后可直接点击下图 ...
- Spring中bean的初始化和销毁几种实现方式
Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-metho ...
- 动手写一个简单的Web框架(HelloWorld的实现)
动手写一个简单的Web框架(HelloWorld的实现) 关于python的wsgi问题可以看这篇博客 我就不具体阐述了,简单来说,wsgi标准需要我们提供一个可以被调用的python程序,可以实函数 ...