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

时间:2011-03-31 13:20:40

标签: 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

可以用以下方式运行:
./python program.py>> a.txt&
然后cat a.txt ..一旦脚本完成计算,你才会得到结果。

6 个答案:

答案 0 :(得分:7)

来自stdout manual page

  

流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已关闭。

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

那说,这个过程比较简单。你输入了一个C文件,例如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>