使用旧学校subprocess.Popen()
API启动两个流程时,我可以轻松地将一个流程的标准连接到另一个流程的标准,创建一个管道与UNIX shell一样,在使用|
:
from subprocess import Popen, PIPE
process_1 = Popen(['ls'], stdout = PIPE)
process_2 = Popen(['wc'], stdin = process_1.stdout)
process_1.wait()
process_2.wait()
使用asyncio.subprocess.create_subprocess_exec()
(或类似)的异步API时,如何才能完成相同的操作?这就是我试过的:
from asyncio.events import get_event_loop
from asyncio.subprocess import PIPE, create_subprocess_exec
async def main():
process_1 = await create_subprocess_exec('ls', stdout = PIPE)
process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout)
await process_1.wait()
await process_2.wait()
get_event_loop().run_until_complete(main())
但是对create_subprocess_exec()
的第二次调用抱怨传递给stdin
的参数没有fileno
(这是真的):
Traceback (most recent call last):
File ".../test-async.py", line 11, in <module>
get_event_loop().run_until_complete(main())
[...]
File ".../test-async.py", line 6, in main
process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout)
[...]
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1388, in _get_handles
p2cread = stdin.fileno()
AttributeError: 'StreamReader' object has no attribute 'fileno'
如何获得与上述同步示例相同的结果?
答案 0 :(得分:7)
在asyncio中,process.stdout实际上是StreamReader,而不是文件对象。可以通过process._transport._proc.stdout
访问文件对象。遗憾的是,您无法使用它,因为它已经在事件循环中注册,以便提供流接口process.stdout
。
解决此问题的一种方法是创建自己的管道并将文件描述符传递给子流程:
async def main():
read, write = os.pipe()
process_1 = await create_subprocess_exec('ls', stdout=write)
os.close(write)
process_2 = await create_subprocess_exec('wc', stdin=read, stdout=PIPE)
os.close(read)
return await process_2.stdout.read()
请注意,一旦第一个子进程启动,write
文件描述符应该被显式关闭(除非你使用subprocess.PIPE
,否则它不会自动关闭)。还需要关闭read
文件描述符,如here所述。