如果我使用这样的命令:
./ 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 ..一旦脚本完成计算,您将只获得结果.
流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.
| 归档时间: |
|
| 查看次数: |
6753 次 |
| 最近记录: |