当我们想一边监控一个程序的输出,一边又想将输出记录到一个文件当中的时候,tee命令就是一个非常好的选择

./buffer_demo | tee a.txt

比如上面的命令,将buffer_demo程序的输出打印到屏幕上,同时写入a.txt文件

当buffer_demo持续输出比较多的文本的时候,上面的命令运行的没有什么问题。

但是如果buffer_demo使用的是printf这样的标准io库的函数,并且输出文本速度很慢的时候,我们可能半天也看不到屏幕上有输出。这是因为当你使用管道的时候,标准io库发现stdout是一个管道,于是会启用块缓冲,有时这个缓冲会达到8192字节。

比如buffer_demo程序

#include <stdio.h>
#include <unistd.h>int main(void) {while (1) {printf("123\n");sleep(1);}return 0;
}

需要输出很久才能达到一块

当然在拥有源码的情况下我们可以加一个fflush来解决这个问题

比如这样

#include <stdio.h>
#include <unistd.h>int main(void) {while (1) {printf("123\n");fflush(stdout);sleep(1);}return 0;
}

但是更多数的情况是我们没有源码

这时候我们可以使用expect自带的一个名为unbuffer的工具,像这样,就可以去掉缓存的影响

unbuffer ./buffer_demo | tee a.txt

unbuffer是靠伪终端来实现这个功能的

具体的思路是创建一个子进程将其连接到一个伪终端的slave上,在子进程中执行buffer_demo,这样buffer_demo会认为标准输出是终端而不是文件,此时标准io库的缓冲模式为行缓冲,就不会像块缓冲一样缓冲很多数据了。而unbuffer要保证每收到少量的数据就通过标准输出打印出来,这样tee程序就能够有持续的输入了。

linux 下可以将程序连接 libutil.so,就可以直接使用它提供的forkpty函数来简化fork和关联stdout stdin stderr到pts描述符的动作了

pid_t forkpty(int *amaster, char *name, const struct termios *termp, const struct winsize *winp);

仔细看函数原型,并没有返回slave的描述符,因为函数已经自动的将其dup到子进程的 stdout stdin stderr了。

下面我们模仿unbuffer实现一个简化版本

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pty.h>#define max(a, b) ((a) > (b) ? (a) : (b))int written(int fd, char *write_buffer, int length) {int send_len = 0;while (send_len < length) {int write_size = write(fd, write_buffer + send_len, length - send_len);if (write_size <= 0) {break;}send_len += write_size;}return send_len;
}void bridge(int fd1, int fd2) {char buf[1024];fd_set fds;int fm = max(fd1, fd2) + 1;int l = 0;while (1) {FD_ZERO(&fds);FD_SET(fd1, &fds);FD_SET(fd2, &fds);select(fm, &fds, NULL, NULL, NULL);if (FD_ISSET(fd1, &fds)) {l = read(fd1, buf, sizeof(buf));if (l == 0) {break;}written(fd2, buf, l);}if (FD_ISSET(fd2, &fds)) {l = read(fd2, buf, sizeof(buf));if (l == 0) {break;}written(fd1, buf, l);}}
}int main(int argc, char *argv[]) {if (argc < 2) {printf("too few args\n");return -1;}int ptm;pid_t pid = forkpty(&ptm, NULL, NULL, NULL);if (pid == -1) {printf("fork error\n");return -1;}if (pid == 0) {if (execvp(argv[1], &argv[1]) < 0) {printf("exec failed.\n");return -1;}} else {bridge(1, ptm);}return 0;
}

程序非常简单,主要就三个步骤

1.  forkpty 创建子进程,关联pts给子进程stdin stdout stderr, 获取到主设备的描述符ptm,通过读ptm,相当于读子进程的标准输出

2. 在子进程中exec要执行的二进制程序

3. 在主进程中将标准输出和ptmx做一个桥接,桥接使用select调用同时监听两个描述符,将一个描述符read出来的数据写入另一个

使用tee命令获取不到输出的解决方案相关推荐

  1. 关于 在国产麒麟系统上使用QProcess配合管道命令执行shell命令获取预期结果输出失败 的解决方法

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/126678079 红胖子(红模仿)的博文大全:开发技术集 ...

  2. linux tee作用,linux tee 命令使用详解(大量实例)

    tee 功能说明:读取标准输入的数据,并将其内容输出成文件. 语 法:tee [-ai][--help][--version][文件-] 补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标 ...

  3. linux tee错误日志,linux tee命令使用详解教程

    在执行Linux命令时,我们既想把输出保存到文件中,又想在屏幕上看到输出内容,就可以使用tee命令.接下来是小编为大家收集的linux tee 命令使用详解教程,希望能帮到大家. linux tee ...

  4. linux tee 命令 输出的同时写入文件

    有时候,你会想手动跟踪命令的输出内容,同时又想将输出的内容写入文件,确保之后可以用来参考.如果你想寻找这相关的工具,那么恭喜你,Linux 已经有了一个叫做 tee 的命令可以帮助你. tee 命令基 ...

  5. inux C程序中获取shell脚本输出(如获取system命令输出)

    1. 前言 Unix 界有一句名言:"一行shell脚本胜过万行C程序",虽然这句话有些夸张,但不可否认的是,借助脚本确实能够极大的简化一些编程工作.比如实现一个 ping程序来测 ...

  6. c中获取python控制台输出_linux c程序中获取shell脚本输出的实现方法

    [转]Linux Shell脚本调试技术 本文转载自:https://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/ Shell脚本调试技术 ...

  7. linux命令之tee,linux tee命令

    tee命令读取标准输入并将其写入标准输出和一个或多个文件.该命令以管道中使用的T分隔器命名.它基本上破坏了程序的输出,因此它既可以显示又可以保存在文件中 该tee命令从标准输入读取并同时写入标准输出和 ...

  8. Linux命令之tee命令

    一.命令简介   tee命令的作用就是读取标准输入内容,将读取到的数据写到标准输出和文件.应用场景一就是有时候我们希望操作命令既显示到屏幕又保存到文档,tee命令是我们的不二选择:应用场景二是重复展示 ...

  9. linux命令之tee,技术|为初学者介绍的 Linux tee 命令(6 个例子)

    有时候,你会想手动跟踪命令的输出内容,同时又想将输出的内容写入文件,确保之后可以用来参考.如果你想寻找这相关的工具,那么恭喜你,Linux 已经有了一个叫做 tee 的命令可以帮助你. 本教程中,我们 ...

最新文章

  1. python sql语句传参数_pyMySQL SQL语句传参问题,单个参数或多个参数说明
  2. 一次查找sqlserver死锁的经历
  3. 推荐系统遇上深度学习(四)--多值离散特征的embedding解决方案
  4. python中怎么调整代码字体格式_PyCharm中代码字体大小调整方法
  5. 佩戴十字架项链有什么特殊含义?
  6. java 蜂鸣器_蜂鸣器的介绍
  7. python按钮事件,Python按钮的响应事件详解
  8. 【图】Excel快捷键大全+函数公式 职场必备
  9. 数据库服务器,sql
  10. PicGo+Gitee(码云)搭建个人博客的免费图床
  11. Cascadeur笔记:导出非ASC II格式FBX的方法
  12. 1335:【例2-4】连通块——dfs、bfs
  13. 如何裁剪图片,裁剪图片的几个方法
  14. 嵌入式软件开发工程师求职要求
  15. 野百合的春天 ——布鲁斯鲍文
  16. Wins10系统忘记开机密码快速解锁方法(图文教程)
  17. 【Homeassistant 与RC522 RFID握手】
  18. java中构造方法只能有一个_对Java中类的构造方法描述正确的是()A.如果在类中没有定义,Java就提供一个默认的构造方法B.只能...
  19. [秀] MarsBook 手机书吧
  20. 关于RDLC打印时自主选择横向纵向打印的一些问题

热门文章

  1. Kotlin ?.let 、!! 、?:等运算符的使用
  2. Bootstrap响应式Web开发(一)
  3. java实现邮箱激活注册账号完整案例
  4. jmeter后置处理器JSON Extractor
  5. 4.28 DP练习赛
  6. Java27岁啦——一次争执引起的Java内卷生涯
  7. C/C++内存检测工具valgrind--memcheck
  8. python画蜡烛图_Python量化交易-绘制蜡烛图 !这个图不像你的钱哦!
  9. Blender 使骨骼旋转方向一致
  10. Button点击事件