我正在运行一个脚本,它通过另一个脚本将其输出管道化;该包装器脚本为日志的每一行添加了一个时间戳。但是,数据是以大约 8000 个字符(大约 180 行)的块写入日志文件的。这导致所有这些行的时间戳完全相同。如果输出信息的底层脚本直接在 shell 中运行,则输出将实时逐行显示。
作为参考,脚本如下所示:
#!/bin/bash
python foo.py | ~/timestamp.sh >> ~/logs/foo.log
exit
时间戳脚本只是在每一行输出中添加一个时间戳(您猜对了)。该脚本包含以下内容:
#!/bin/bash
while read x; do
echo -n `date +%d/%m/%Y\ %H:%M:%S`;
echo -n " ";
echo $x;
done
如果我从等式中删除 timestamp.sh,行为完全相同。我通过运行脚本并使用以下方式监视日志文件来检查这一点tail -f script.log
是否有一个设置可以在将文件输出写入日志文件之前对其进行缓冲?我还有其他方法可以尝试为日志文件的每一行添加时间戳吗?我以前在以前的系统上也曾使用过这种类型的方法,但无论我尝试什么,似乎都无法解决这个问题。
这一切都在 Ubuntu 14.04.4 x64 上
答案1
根本原因是您的进程在这里python
使用libc
stdout
当输出到终端时使用行缓冲但当输出到其他东西(例如这里的管道)时使用块缓冲。
您可以在 Python 代码中通过在每次日志输出后明确刷新缓冲区来解决该问题:
sys.stdout.flush()
或者通过一些技巧来控制缓冲的方式:
stdbuf -oL python foo.py | ~/timestamp.sh >> ~/logs/foo.log
或者
unbuffer python foo.py | ~/timestamp.sh >> ~/logs/foo.log
我建议在代码中明确刷新缓冲区(sys.stdout.flush()
),因为您是唯一知道输出应该在哪里刷新的人。行缓冲(stdbuf -oL
)的优化程度略低,但考虑到您每行都有时间戳,应该没问题,就性能而言,禁用所有缓冲(unbuffer
或 heemayl 的python -u
建议)是最糟糕的方法(尽管根据您的 python 代码如何编写其输出,它可能不会被注意到)。
答案2
由于 STDOUT 流默认是块缓冲的(当不进入终端时)python
,因此您需要使流无缓冲(或行缓冲)。
python
提供了一种使流无缓冲的方法,这是 Pythonic 方法:
python -u foo.py
因此,您的整个命令行变成:
python -u foo.py | ~/timestamp.sh >> ~/logs/foo.log
从man python
:
-u Force stdin, stdout and stderr to be totally unbuffered
我现在知道了您的脚本,但是在脚本中您可以使用模块刷新某个流sys
:
sys.stdout.flush()