Python 将标准输出重定向到文件时,转发子进程标准输出不起作用

Python 将标准输出重定向到文件时,转发子进程标准输出不起作用,python,subprocess,Python,Subprocess,将子进程的标准输出转发到当前进程的标准输出时出现问题 这是我的MWE调用程序代码(runner.py): 以下是callee test.py的内容: import time while True: time.sleep(1) print "Heartbeat" 以下操作将起作用,并将所有心跳打印到控制台: python runner.py 但是,以下操作不起作用,输出文本文件仍然为空(使用Python 2.7): 我必须做什么?当标准输出是一个TTY(终端)时,sys.st

将子进程的标准输出转发到当前进程的标准输出时出现问题

这是我的MWE调用程序代码(runner.py):

以下是callee test.py的内容:

import time

while True:
    time.sleep(1)
    print "Heartbeat"
以下操作将起作用,并将所有心跳打印到控制台:

python runner.py
但是,以下操作不起作用,输出文本文件仍然为空(使用Python 2.7):


我必须做什么?

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

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

打印后使用
sys.stdout.flush()
,您就可以:

import sys
import time

while True:
    time.sleep(1)
    print "Heartbeat"
    sys.stdout.flush()
如果您使用的是Python 3,还可以使用
print
函数的
flush
参数,如下所示:

import time

while True:
    time.sleep(1)
    print("Heartbeat", flush=True)
或者,您也可以为
SIGTERM
设置一个处理程序,以确保在调用
p.terminate()
时刷新缓冲区:

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

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

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


您不需要传递
stdout=sys.stdout
,除非
sys.stdout
使用的文件描述符与
python
可执行文件启动时使用的文件描述符不同。默认情况下,C stdout fd是继承的:您不需要做任何事情就可以让子进程继承它

,如果输出重定向到文件,则python使用块缓冲模式,并且
“Heartbeat”*10(通常)太小,无法溢出缓冲区。
我希望
python
在退出时刷新其内部stdout缓冲区,但在
SIGTERM
信号(由
.terminate()
调用生成)时不会这样做。
要允许子进程正常退出,请使用
SIGINT
(Ctrl+C)而不是
p.terminate()

在这种情况下,
test.py
将刷新缓冲区,您将在
test.txt
文件中看到输出。放弃子项中的stderr或catch
KeyboardInterrupt
异常

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


注意:您的父进程也可能缓冲输出。如果未及时刷新缓冲区,则在
test.py
启动之前打印的输出可能会在
test.txt
文件中的输出后出现。父进程和子进程中的缓冲区是独立的。手动刷新缓冲区或确保在每个进程中使用适当的缓冲模式。请参阅

shell=True
可能会修复它。@zondo不,我不想包含shell命令您可以将
env=dict(os.environ,PYTHONUNBUFFERED=1)
传递到
Popen()
以避免影响其他python进程。1-调用
sys.stdout.flush()容易出错(而且效率低下)
在整个程序中手动执行。您可以在整个过程中取消缓冲标准输出,而不是2-
signal(signal.SIGTERM,sys.stdout.flush)
将不起作用。您可能是指
signal(signal.SIGTERM,lamba*a:(sys.stdout.flush(),sys.exit(-signal.SIGTERM))
。尽管可能更简单,但对@J.F.Sebastian来说,刷新输出实际上是一种方式。你不必相信我:看看
ping
tail
或系统中所有其他定期写入输出的工具,你认为
grep
为什么有
--行缓冲
参数?为什么不像你建议的那样,默认情况下它不在每一行之后刷新?
import signal
signal.signal(signal.SIGTERM, sys.stdout.flush)
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'])
p.send_signal(signal.SIGINT)