Python 将标准输出重定向到文件时,转发子进程标准输出不起作用
将子进程的标准输出转发到当前进程的标准输出时出现问题 这是我的MWE调用程序代码(runner.py): 以下是callee test.py的内容: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
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或catchKeyboardInterrupt
异常
如果希望在子进程仍在运行时查看输出,请运行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)