当stdout重定向到文件时,转发子进程stdout不起作用

时间:2016-03-04 16:15:51

标签: python subprocess

我在将子进程的stdout转发到当前进程的stdout时遇到问题。

这是我的MWE来电代码(runner.py):

import sys
import subprocess
import time

p = subprocess.Popen([sys.executable, "test.py"], stdout=sys.stdout)
time.sleep(10)
p.terminate()

以下是被调用者test.py的内容:

import time

while True:
    time.sleep(1)
    print "Heartbeat"

以下内容将工作并将所有心跳打印到控制台:

python runner.py

但是,以下方法不起作用,输出文本文件保持为空(使用Python 2.7):

python runner.py > test.txt

我该怎么做?

3 个答案:

答案 0 :(得分:6)

当标准输出是TTY(终端)时,sys.stdout默认为行缓冲:您打印的每一行都会立即写入TTY。

但是当标准输出是文件时,sys.stdout是块缓冲的:只有在打印了一定数量的数据时才将数据写入文件。通过使用p.terminate(),您将在刷新缓冲区之前终止该进程。

sys.stdout.flush()后使用print,您就可以了:

import sys
import time

while True:
    time.sleep(1)
    print "Heartbeat"
    sys.stdout.flush()

如果您使用的是Python 3,您还可以使用flush函数的print参数,如下所示:

import time

while True:
    time.sleep(1)
    print("Heartbeat", flush=True)

或者,您也可以为SIGTERM设置处理程序,以确保在调用p.terminate()时刷新缓冲区:

import signal
signal.signal(signal.SIGTERM, sys.stdout.flush)

答案 1 :(得分:1)

每次打印后都可以通过sys.stdout.flush()强制刷新,但这很快就会变得很麻烦。由于您知道自己正在运行Python,因此可以强制Python进入无缓冲模式 - 使用-u开关或PYTHONUNBUFFERED环境变量:

p = subprocess.Popen([sys.executable, '-u', 'test.py'], stdout=sys.stdout)

import os
# force all future python processes to be unbuffered
os.environ['PYTHONUNBUFFERED'] = '1'

p = subprocess.Popen([sys.executable, 'test.py'])

答案 2 :(得分:1)

除非stdout=sys.stdout使用的文件描述符与sys.stdout可执行文件开头的文件描述符不同,否则不需要传递python。默认情况下,继承C stdout fd:您不需要执行任何操作以使子进程继承它。

As @Andrea Corbellini said,如果输出被重定向到文件,则python使用块缓冲模式,"Heartbeat"*10(通常)太小而不能溢出缓冲区。
我希望python在退出时刷新其内部stdout缓冲区,但它不会在SIGTERM信号上执行此操作(由.terminate()调用生成)。
要允许子进程正常退出,请使用SIGINT(Ctrl + C)代替p.terminate()

p.send_signal(signal.SIGINT)

在这种情况下,test.py将刷新缓冲区,您将在test.txt文件中看到输出。丢弃stderr或在孩子中捕获KeyboardInterrupt例外。

如果要在子进程仍在运行时查看输出,请运行python -u,禁用缓冲或设置PYTHONUNBUFFERED envvar以将所有受影响的python进程的行为更改为@Antti Haapala suggested

注意:您的父进程也可以缓冲输出。如果您没有及时刷新缓冲区,那么 test.py之前打印的输出甚至可能会在<{em>} 输出test.txt文件后出现。父进程和子进程中的缓冲区是独立的。手动刷新缓冲区或确保在每个进程中使用适当的缓冲模式。见Disable output buffering