Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >如何优雅地将printf的打印保存在文件中?

如何优雅地将printf的打印保存在文件中?

作者头像
编程珠玑
发布于 2019-11-27 07:15:13
发布于 2019-11-27 07:15:13
10.3K01
代码可运行
举报
文章被收录于专栏:编程珠玑编程珠玑
运行总次数:1
代码可运行

我们都知道,一般使用printf的打印都会直接打印在终端,如果想要保存在文件里呢?我想你可能想到的是重定向。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ program > result.txt

这样printf的输出就存储在result.txt中了。相关内容可以参考《如何理解Linux shell中“2>&1”》。

当然了,如果你既想打印在终端,又想保存在文件,还可以使用tee命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
program | tee result.txt

注:program为你运行的程序。

不过文本介绍了不是通过命令行的方式,而是通过代码实现。

写文件

你可能会想,那不用printf,直接将打印写入到文件不就可以了?类似于下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号编程珠玑 网站:https://www.yanbinghu.com
#include<stdio.h>
int 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 标准错误

一般标准输出都是都直接输出到终端。

我们可以用一个程序简单观察一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//test.c
#include<stdio.h>
#include <unistd.h>
int main(void)
{
    sleep(20);//为了避免立即退出
    return 0;
}

假设编译出来的程序为test:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ gcc -o test test.c
$ ./test &
$ ls -l /proc/`pidof test`/fd

这里关于proc文件系统可以参考《Linux中不可错过的信息宝库》,pidof test用于获取test进程id,其fd目录可以看到打开的文件描述符,关于文件打开可以参考《查看文件打开的多种方式》。 其输出结果如下(可左右滑动查看完整内容):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,其实就是当前终端(参考《Linux下你还知道这些特殊文件?》):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ tty
/dev/pts/0

所以如果我们要将printf的打印保存到文件中,实际上就让它重定向到这个文件就可以了。这里我们用到freopen函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
FILE *freopen(const char *path, const char *mode, FILE *stream);

参数说明:

  • path:需要重定向到的文件名或文件路径。
  • mode:代表文件访问权限的字符串。例如,"r"表示“只读访问”、"w"表示“只写访问”、"a"表示“追加写入”。
  • stream:需要被重定向的文件流。

那么要完成前面的任务也就很简单了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】
#include<stdio.h>
#include <unistd.h>
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;
}

重新编译后运行查看其打开的文件描述符:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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中看到了下面的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bianchengzhuji

有人可能会有下面的疑问:

  • 怎么恢复?

首先来看怎么恢复,实际上恢复的原理是类似的,既然最开始它从定向到了/dev/pts/0,那么我们只需要重定向回去就可以了,但是在不同的终端,它的tty名字可能不同,因此需要使用ttyname函数获取原先stdout的tty名字:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int ttyname_r(int fd, char *buf, size_t buflen);

又可以重新定向到/dev/pts/0了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】#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\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

还有没有别的保存方法呢?

除了上面这种方式,还有一种方式是使用dup2:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int dup2(int oldfd, int newfd);

它是用来复制文件描述符的,会使得newfd成为oldfd的副本.所以与上面看到不同的是,标准输出和往fd写入的内容,都会存储在文件test.log中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//来源:公众号【编程珠玑】
#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\n");
    printf("shouwangxiansheng\n");
    sleep(20);
    close(fd);
    return 0;
}

观察的结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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。如果一行代码都不想动,使用命令行重定向。如果你完全不关心,当我啥都没说。本文相关内容见相关精彩推荐。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
想后台运行没想到导致磁盘满了
之前在《如何让程序真正地后台运行》一文中提到了程序后台运行的写法,但是里面的示例程序在某些场景下是会有问题的,这里先不说什么问题,我们先看看这个磁盘满的问题是怎么产生的,通过这篇文章你将会学习到大量linux命令的实操使用。
编程珠玑
2020/10/27
1.2K0
Linux 中前台进程转后台,并重定向输出
有时候我们直接在终端中执行了命令,等待很长时间后发现还需要很久才能执行完,又不想一直开着终端等待结果,可以按照下面的方式,将前台进程转至后台,并重定向输出内容至文件,以便随时连接回来查看执行状态。
AlphaHinex
2024/04/09
5520
Linux 中前台进程转后台,并重定向输出
关于 /dev/null 差点直播吃鞋的一个小问题
我们的定时任务、异步 MQ 的 jar 包程序等都会使用 System.in.read() 等阻塞程序,防止程序退出,在本地测试一直都没有问题,直到有同学反馈,线上 Docker 环境中代码 System.in.read() 没有阻塞,执行到了后面的程序,简化过的代码如下所示。
挖坑的张师傅
2022/05/13
6220
关于 /dev/null 差点直播吃鞋的一个小问题
rm 删除文件空间就释放了吗?
在 Linux,你是不是曾经天真的以为,使用rm删除一个文件,占用的空间就释放了?事情可能不是常常如人意。
杰哥的IT之旅
2020/06/29
1.6K0
如何创建多进程程序?(文末福利)
在《对进程和线程的一些总结》已经介绍了进程和线程的区别,但是在C/C++中如何创建进程呢?或者说如何编写多进程的程序呢?
编程珠玑
2019/08/19
1.7K0
如何让程序真正地在后台运行?
来源:公众号【编程珠玑】 作者:守望先生 ID:shouwangxiansheng 如何实现一个守护进程?如何让程序在后台运行?这是后台开发面试常问的一道题,那么守护进程到底是什么?又该如何实现?
编程珠玑
2019/12/16
2.6K0
linux17-详说linux的重定向与文件描述符
上一节中 [[16-linux程序后台执行指西]],我们提到了,重定向操作,对于后台执行命令来说,很有用,这一节来详细说说。
北野茶缸子
2022/05/19
1.7K0
linux17-详说linux的重定向与文件描述符
Linux下你还知道这些特殊文件?
/dev/null 可无限接收数据,你可以认为是一个黑洞,因此如果我们需要丢弃某些终端输出,可以重定向到这里:
编程珠玑
2019/11/23
9270
【Linux】基础IO(文件描述符、缓冲区、重定向)
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。
秦jh
2024/10/08
2130
【Linux】基础IO(文件描述符、缓冲区、重定向)
面试题:rm 删除文件之后,空间就被释放了吗?你知道答案吗?
在Linux,你是不是曾经天真的以为,使用rm删除一个文件,占用的空间就释放了?事情可能不是常常如人意。
程序员白楠楠
2020/12/06
1.6K0
Deno TCP Echo Server 是怎么运行的
在 “了不起的 Deno 入门教程” 这篇文章中,我们介绍了如何使用 Deno 搭建一个简单的 TCP echo server,本文将使用该示例来探究 TCP echo server 是怎么运行的?前方高能,请小伙伴们深吸一口气做好准备。
山月
2020/05/26
1.2K0
Deno TCP Echo Server 是怎么运行的
【Linux】 基础IO——文件(下)
但为啥是3,不是0 ,1,2 任何一个进程,在启动的时候,默认会打开当前进程的三个文件: 标准输入、标准输出、标准错误 ——本质都是文件 C语言:标准输入(stdin) 标准输出(stdout) 、标准错误(stderr) ——文件在系统层的表现 C++: 标准输入(cin) 标准输出(cout) 、标准错误(cerr) ——文件在系统层的表现,它是一个类
lovevivi
2023/05/02
2.2K0
【Linux】 基础IO——文件(下)
第十章·Linux系统管理-输入输出
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
DriverZeng
2022/09/26
1.1K0
第十章·Linux系统管理-输入输出
【Linux】基础 IO(文件描述符fd & 缓冲区 & 重定向)
💫 那么我们现在有个问题,我们编好了代码,这个文件是不是就打开了 -- 没有,因为我们把代码写好之后,这个还只是一个文本,那是不是把代码编译成可执行程序,文件就打开了 -- 答案也是没有的,把原代码编译成可执行程序仅仅是跑起来了。
IsLand1314
2024/10/23
3580
【Linux】基础 IO(文件描述符fd & 缓冲区 & 重定向)
【Linux篇】文件描述符背后的秘密:让你的代码更加高效
功能:函数 fwrite() 将 nmemb 个数据项写入由 stream 指针指向的流中,每个数据项的长度为 size 字节,这些数据项从 ptr 所指向的位置获取。 返回值:fwrite() 返回已写入的数据项数量。注意:数据项并不是返回字节数,返回的是指定类型准确写入的个数。
熬夜学编程的小王
2025/04/09
1060
【Linux篇】文件描述符背后的秘密:让你的代码更加高效
系统编程-简洁而不简单的文件操作
我们都听过Linux下一切皆文件,实际上无论是普通的文件读写,还是网络IO读写,它们都有着类似的操作过程。本文通过基本文件IO操作,来了解Linux“一切文件”的读写。当然过程中穿插着很多其他内容。
编程珠玑
2019/12/26
4900
linux手误rm可能不需要跑路
原文链接:https://rumenz.com/rumenbiji/linux-rm-restore.html
入门笔记
2021/10/01
4470
网络编程-一个简单的echo程序(1)
在《网络编程-一个简单的echo程序(0)》中已经对程序整体有了宏观的认识,本文将抽丝剥茧,逐步深入了解echo程序。
编程珠玑
2019/07/12
1K0
网络编程-一个简单的echo程序(1)
如何查看linux中文件打开情况?
我们都知道,在linux下,“一切皆文件”,因此有时候查看文件的打开情况,就显得格外重要,而这里有一个命令能够在这件事上很好的帮助我们-它就是lsof。
编程珠玑
2019/09/02
12K0
基础I/O--重定向&&缓冲区&&stderr
代码解释: 在这段代码中,使用了 open 函数打开文件,并通过 fd 文件描述符来引用该文件。然后,您使用 printf 和 fprintf 函数向标准输出写入内容,并使用 fflush 函数刷新标准输出缓冲区,确保内容被写入文件。最后,使用 close 函数关闭文件。
南桥
2024/05/26
1010
基础I/O--重定向&&缓冲区&&stderr
相关推荐
想后台运行没想到导致磁盘满了
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验