让我们考虑一下这个片段:
from subprocess import Popen, PIPE, CalledProcessError
def execute(cmd):
with Popen(cmd, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(line, end='')
if p.returncode != 0:
raise CalledProcessError(p.returncode, p.args)
base_cmd = [
"cmd", "/c", "d:\\virtual_envs\\py362_32\\Scripts\\activate",
"&&"
]
cmd1 = " ".join(base_cmd + ['python -c "import sys; print(sys.version)"'])
cmd2 = " ".join(base_cmd + ["python -m http.server"])
如果我运行execute(cmd1)
,输出将毫无问题地打印出来。
但是,如果我运行execute(cmd2)
而不会打印任何内容,为什么会这样,我该如何修复它以便我可以实时看到http.server的输出。
此外,for line in p.stdout
如何在内部进行评估?是什么样的无限循环,直到达到stdout eof或什么?
这个主题已经在SO中解决了几次,但我还没有找到一个Windows解决方案。上面的片段实际上是来自这个answer的代码并试图从virtualenv运行http.server(在win7上运行python3.6.2-32bits)
答案 0 :(得分:5)
使用此代码,由于缓冲,您无法看到实时输出:
for line in p.stdout:
print(line, end='')
但是如果你使用p.stdout.readline()
它应该有效:
while True:
line = p.stdout.readline()
if not line: break
print(line, end='')
有关详细信息,请参阅相应的python bug discussion
UPD:在这里,您可以在stackoverflow上找到几乎相同的problem with various solutions。
答案 1 :(得分:5)
如果要从正在运行的子流程中连续读取,则必须使 进程'输出无缓冲。您的子进程是一个Python程序,可以通过将const apiMiddleware = ({
dispatch,
getState
}) => next => async action => {
switch (action.type) {
//...
}
next(action);
};
传递给解释器来完成:
-u
这就是它在Windows机器上的外观。
答案 2 :(得分:3)
如何在内部评估p.stdout中的行?是什么样的无限循环,直到达到stoout eof或什么?
p.stdout
是一个缓冲区(阻塞)。当您从空缓冲区读取时,您将被阻止,直到将某些内容写入该缓冲区。一旦有内容,您就可以获得数据并执行内部部分。
想想tail -f
如何在linux上运行:它等待直到写入文件的某些内容,并且当它执行时它将新数据回显到屏幕上。没有数据时会发生什么? 等待。因此,当您的程序到达此行时,它会等待数据并进行处理。
由于您的代码有效,但当作为模型运行时,它必须以某种方式与此相关。 http.server
模块可能会缓冲输出。尝试将-u
参数添加到Python以将进程作为无缓冲运行:
-u:无缓冲的二进制stdout和stderr; PYTHONUNBUFFERED = x 有关'-u'
的内部缓冲的详细信息,请参见手册页
此外,您可能希望尝试将循环更改为for line in iter(lambda: p.stdout.read(1), ''):
,因为这会在处理之前一次读取1
个字节。
更新:完整的循环代码是
for line in iter(lambda: p.stdout.read(1), ''):
sys.stdout.write(line)
sys.stdout.flush()
此外,您将命令作为字符串传递。尝试将其作为列表传递,每个元素都在自己的插槽中:
cmd = ['python', '-m', 'http.server', ..]
答案 3 :(得分:3)
我认为主要的问题是http.server
以某种方式将输出记录到stderr
,这里我有一个asyncio
示例,从stdout
读取数据或stderr
。
我的第一次尝试是使用asyncio,一个很好的API,它存在于Python 3.4中。后来我找到了一个更简单的解决方案,所以你可以选择,它们都应该有效。
在后台,asyncio正在使用IOCP - 一个用于异步内容的Windows API。
# inspired by https://pymotw.com/3/asyncio/subprocesses.html
import asyncio
import sys
import time
if sys.platform == 'win32':
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
async def run_webserver():
buffer = bytearray()
# start the webserver without buffering (-u) and stderr and stdin as the arguments
print('launching process')
proc = await asyncio.create_subprocess_exec(
sys.executable, '-u', '-mhttp.server',
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
print('process started {}'.format(proc.pid))
while 1:
# wait either for stderr or stdout and loop over the results
for line in asyncio.as_completed([proc.stderr.readline(), proc.stdout.readline()]):
print('read {!r}'.format(await line))
event_loop = asyncio.get_event_loop()
try:
event_loop.run_until_complete(run_df())
finally:
event_loop.close()
根据您的示例,这是一个非常简单的解决方案。它只是将stderr重定向到stdout,只读取stdout。
from subprocess import Popen, PIPE, CalledProcessError, run, STDOUT import os
def execute(cmd):
with Popen(cmd, stdout=PIPE, stderr=STDOUT, bufsize=1) as p:
while 1:
print('waiting for a line')
print(p.stdout.readline())
cmd2 = ["python", "-u", "-m", "http.server"]
execute(cmd2)
答案 4 :(得分:3)
您可以在操作系统级别实现无缓冲行为。
在Linux中,您可以使用stdbuf
包装现有命令行:
stdbuf -i0 -o0 -e0 YOURCOMMAND
或者在Windows中,您可以使用winpty
包装现有命令行:
winpty.exe -Xallow-non-tty -Xplain YOURCOMMAND
我不知道操作系统中立的工具。