如何优雅地将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 ...
随机推荐
- 微信小程序实现tabs选项卡
选项卡在我们的日常开发中,使用的还是蛮多的,但是微信小程序中却没有直接提供选项卡组件,不过我们可以变通通过 scroll-view 和 swiper 组件来实现一个选项卡的功能. 需求: 实现一个选项 ...
- zuul过滤器filter 的编写
通过上一节(zuul的各种配置)的学习,我们学会了zuul路由的各种配置,这一节我们来实现一下zuul的过滤器功能.那么为什么需要用到zuul的过滤器呢?我们知道zuul是我们实现外部系统统一访问的入 ...
- python +spatialite + window 解决方案(https://www.jianshu.com/p/5bc7d8b7b429)
运行环境在windows 10 64bit.先将python安装完成.然后,到 spatilite官网 找到MS(即Microsoft)版本,下载64位的mod_spatialite,将其先解压到目标 ...
- 开发笔记-----Ajax 基础使用
一.GET 方式的用法: 1 <!--html --> 2 <div class="layui-form"> 3 <div class="l ...
- 基于SpringBoot项目MyBatis分页插件实现分页总结
前言 在使用Mybatis时,最头痛的就是写分页了,需要先写一个查询count的select语句,然后再写一个真正分页查询的语句,当查询条件多了之后,会发现真的不想花双倍的时间写 count 和 se ...
- RabbitMQ多消费者顺序性消费消息实现
最近起了个项目消息中心,用来中转各个系统中产生的消息,用到的是RabbitMQ,由于UAT环境.生产环境每台消费者服务都是多台,有些消息要求按顺序消费,所以需要采取一定的措施保证消息的顺序消费,下面讲 ...
- harbor安装高可用
harbor架构 下载地址https://github.com/goharbor/harbor/ 高可用架构 解压压缩包 tar -xvf harbor-offline-installer-v1.10 ...
- .Net Core微服务——网关(2):ocelot集成consul
有consul基础的都知道,consul可以发现新增的服务,剔除掉无效的服务,赋予应用自动伸缩的能力.而ocelot如果集成了consul,那ocelot也能拥有这些能力,还可以自主选择负载均衡策略, ...
- Linux wget 命令 使用总结
简介 wget命令用来从指定的URL下载文件.wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性,如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕.如果是服务器 ...
- Django 中间件 详细总结
一.什么是中间件 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨慎实用,用不好会影 ...