我在将子进程的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
我该怎么做?
答案 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