如果我使用这样的命令:
./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
可以用以下方式运行:
./python program.py>> a.txt&
然后cat a.txt ..一旦脚本完成计算,你才会得到结果。
答案 0 :(得分:7)
流stderr是无缓冲的。 流标准输出是行缓冲的 指向终端时。 部分线将不会出现 调用fflush(3)或exit(3),或 印刷了一条新线。
底线:除非输出是终端,否则默认情况下程序的标准输出将处于完全缓冲模式。这实际上意味着它将以大数据块输出数据,而不是逐行输出数据,更不用说逐个字符了。
解决这个问题的方法:
修复您的程序:如果您需要实时输出,则需要修复程序。在C中,您可以在每个输出语句后使用fflush(stdout)
,或setvbuf()
来更改标准输出的缓冲模式。对于Python,甚至有一些建议here只有sys.stdout.flush()
。
使用可以从PTY记录的实用程序,而不是直接的stdout重定向。 GNU Screen可以为您完成此操作:
screen -d -m -L python test.py
将是一个开始。这会将程序的输出记录到当前目录中名为screenlog.0
(或类似)的文件中,默认延迟为10秒,您可以使用screen
连接到命令的会话正在运行以提供输入或终止它。日志文件的延迟和名称可以在配置文件中更改,也可以在连接到后台会话后手动更改。
编辑:
在大多数Linux系统上有第三种解决方法:您可以使用LD_PRELOAD
变量和预加载的库来覆盖C库的选择函数,并使用它们来设置stdout
缓冲模式函数由您的程序调用。这种方法可行,但它有许多缺点:
它根本不适用于静态可执行文件
它很脆弱而且相当难看。
它根本不适用于SUID可执行文件 - 动态加载程序在出于安全原因加载此类可执行文件时将拒绝读取LD_PRELOAD
变量。
它很脆弱而且相当难看。
它要求您找到并覆盖程序在初始设置stdout
缓冲模式后调用的库函数,最好是之前的 >任何输出。 getenv()
是许多程序的不错选择,但不是全部。您可能必须覆盖常见的I / O函数,例如printf()
或fwrite()
- 如果推送,您可能只需要覆盖控制缓冲模式的所有函数并为{{引入特殊条件1}}。
它很脆弱而且相当难看。
很难确保没有不受欢迎的副作用。要做到这一点,您必须确保只有stdout
受到影响,并且您的替换不会导致程序其余部分崩溃,例如: stdout
已关闭。
我是否提到它很脆弱而且相当难看?
stdout
替换功能:
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);
}
然后设置gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
变量以将其与程序一起加载:
LD_PRELOAD
如果你很幸运,你的问题将在没有不幸的副作用的情况下解决。
如果需要,您可以在shell中设置$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
库,甚至可以在LD_PRELOAD
中指定系统范围内的库(绝对 NOT 推荐)。
答案 1 :(得分:3)
您是否考虑过T恤管道?
./program | tee a.txt
但是,如果“程序”在完成之前没有向stdout写入任何内容,即使发球也行不通。因此,有效性很大程度上取决于程序的行为方式。
答案 2 :(得分:2)
如果您正在尝试修改现有程序的行为,请尝试stdbuf(显然是从版本7.5开始的coreutils的一部分)。
这会将stdout缓冲到一行:
stdbuf -oL command > output
这样就完全禁用了stdout缓冲:
stdbuf -o0 command > output
答案 3 :(得分:1)
如果程序写入文件,您可以在使用tail -f a.txt
编写文件时阅读。
答案 4 :(得分:0)
您的问题是大多数程序检查输出是否是终端。如果输出是一个终端,则输出一次缓冲一行(因此每条线在生成时输出)但如果输出不是终端,则输出以较大的块缓冲(一次4096字节是典型的) )此行为是C库中的正常行为(例如,当使用printf时)以及C ++库中的行为(例如,当使用cout时),因此用C或C ++编写的任何程序都会执行此操作。
大多数其他脚本语言(如perl,python等)都是用C或C ++编写的,因此它们具有完全相同的缓冲行为。
上面的答案(使用LD_PRELOAD)可以用于perl或python脚本,因为解释器本身是用C语言编写的。
答案 5 :(得分:0)
expect
软件包中的unbuffer
命令完全可以满足您的需求。
$ sudo apt-get install expect
$ unbuffer python program.py | cat -
<watch output immediately show up here>