即时输出重定向,在程序仍在运行时查看文件重定向输出

gro*_*ine 11 linux bash

如果我使用这样的命令:
./ program >> a.txt&
,并且程序是一个长时间运行的命令,那么我只能在程序结束后看到输出.这意味着我无法知道计算是否进展顺利,直到实际停止计算.我希望能够在程序运行时读取文件上的重定向输出.

这类似于打开文件,附加到文件,然后在每次写入后关闭它.如果文件仅在程序结束时关闭,则在程序结束之前不能读取任何数据.我所知道的唯一重定向类似于在程序结束时关闭文件.

你可以用这个小python脚本测试它.语言并不重要.任何写入标准输出的程序都有同样的问题.

l = range(0,100000)
for i in l:
  if i%1000==0:
    print i
  for j in l:
    s = i + j
Run Code Online (Sandbox Code Playgroud)

可以使用以下
命令运行:./ python program.py >> a.txt
然后cat a.txt ..一旦脚本完成计算,您将只获得结果.

thk*_*ala 7

stdout手册页:

流stderr是无缓冲的.当流stdout 指向终端时,它是行缓冲的 .在调用fflush(3)或exit(3)或打印换行之前,不会出现部分行.

底线:除非输出是终端,否则默认情况下程序的标准输出将采用完全缓冲模式.这实际上意味着它将以大型块输出数据,而不是逐行输出,更不用说逐个字符了.

解决这个问题的方法:

  • 修复程序:如果需要实时输出,则需要修复程序.在C中,您可以fflush(stdout)在每个输出语句之后使用,或者setvbuf()更改标准输出的缓冲模式.对于Python有sys.stdout.flush(),甚至一些建议在这里.

  • 使用可以从PTY记录的实用程序,而不是直接的stdout重定向.GNU Screen可以为您完成此操作:

    screen -d -m -L python test.py
    
    Run Code Online (Sandbox Code Playgroud)

    将是一个开始.这会将程序的输出记录到screenlog.0当前目录中名为(或类似)的文件中,默认延迟为10秒,您可以使用screen连接到运行命令的会话来提供输入或终止它.日志文件的延迟和名称可以在配置文件中更改,也可以在连接到后台会话后手动更改.

编辑:

在大多数Linux系统上,还有第三种解决方法:您可以使用LD_PRELOAD变量和预加载的库来覆盖C库的select函数,并stdout在程序调用这些函数时使用它们来设置缓冲模式.这种方法可行,但它有许多缺点:

  • 它根本不适用于静态可执行文件

  • 它很脆弱而且相当丑陋.

  • 它根本不适用于SUID可执行文件 - 动态加载器LD_PRELOAD在出于安全原因加载此类可执行文件时将拒绝读取变量.

  • 它很脆弱而且相当丑陋.

  • 它要求您找到并覆盖程序最初设置stdout缓冲模式调用的库函数,最好是任何输出之前.getenv()是许多程序的好选择,但不是全部.您可能必须覆盖常见的I/O功能,例如,printf()或者fwrite()- 如果推送,您可能只需要覆盖控制缓冲模式的所有功能并为其引入特殊条件stdout.

  • 它很脆弱而且相当丑陋.

  • 很难确保没有不受欢迎的副作用.要做到这一点,你必须确保只有stdout受影响,并且如果stdout关闭,你的覆盖不会使程序的其余部分崩溃.

  • 我是否提到它很脆弱而且相当难看?

也就是说,这个过程比较简单.你输入了一个C文件,例如linebufferedstdout.c替换函数:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>


char *getenv(const char *s) {
    static char *(*getenv_real)(const char *s) = NULL;

    if (getenv_real == NULL) {
        getenv_real = dlsym(RTLD_NEXT, "getenv");

        setlinebuf(stdout);
    }

    return getenv_real(s);
}
Run Code Online (Sandbox Code Playgroud)

然后将该文件编译为共享对象:

gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Run Code Online (Sandbox Code Playgroud)

然后设置LD_PRELOAD变量以将其与程序一起加载:

$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out 
0
1000
2000
3000
4000
Run Code Online (Sandbox Code Playgroud)

如果你很幸运,你的问题将得到解决,没有不幸的副作用.

LD_PRELOAD如有必要,您可以在shell中设置库,甚至可以在系统范围内指定该库(绝对推荐)/etc/ld.so.preload.