2016-04-15 5 views
2

、私は簡単でパイプラインを作成し、別のプロセスのに標準に一つのプロセスのうち標準を接続することができますUNIXシェルは|との接続のコマンドを実行しますと同じ方法:接続2つのプロセスは(asyncio.subprocess.create_subprocess_execで開始)古い学校<code>subprocess.Popen()</code> APIを持つ2つのプロセスを起動すると

asyncio.subprocess.create_subprocess_exec()(または類似)から非同期APIを使用しているとき、私は同じことを達成することができますどのように
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() 

? (真である)

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()への2回目の呼び出しは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' 

私は同じ結果を得ることができますどのように上の同期の例では?

+1

関連しない: 'popen(['wc']、..)'の後に 'process_1.stdout.close()'を追加するべきです。 [サブプロセスを使用して複数のプロセスをパイプで接続するにはどうすればいいですか?](http://stackoverflow.com/q/295459/4279) – jfs

答えて

5

asyncioでは、process.stdoutは実際にはファイルオブジェクトではなくStreamReaderです。ファイルオブジェクトはprocess._transport._proc.stdoutからアクセスできます。残念ながら、ストリームインターフェイスprocess.stdoutを提供するためにイベントループに既に登録されているため、使用することはできません。問題に対処するための

一つの方法は、独自のパイプを作成し、サブプロセスにファイル記述子を渡すことです:最初のサブプロセスが開始されるとwriteファイルディスクリプタを明示的に閉じなければならないことを

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() 

は注意( subprocess.PIPEを使用しない限り、自動的に閉じられません)。 hereのように、ファイル記述子readも閉じておく必要があります。

+0

次の文の参照を提供できますか?* "サブプロセスを使用します.PIPE "*?親に 'os.close(write) 'をしたい理由が分かっているので、' ls'は 'SIGPIPE'を、' wc'は早期に死ぬと 'EPIPE'を得ます。 'subprocess.PIPE'(少なくとも普通の' Popen() 'と同じですが、[docsのコード例](https://docs.python.org/3/library/subprocess.html##)で示されています。交換 - シェル - パイプライン)) – jfs

+0

@JFSebastian [コード](https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1501)を見て気づいたが、それがどこかに書かれているかどうかは分かりません。しかし、あなたの例は、作家ではなく読者を閉じることです。私の例では、ライターは、パイプを適切にセットアップするために閉鎖する必要があります([この回答](http://stackoverflow.com/a/15512202/2846140)参照)。 – Vincent

+0

あなたはパイプの 'write'端については正しいです(親では使用されていないので閉じられています。なぜなら、' subprocess.PIPE'パイプを閉じてそれを閉じるコードが責任を負う理由を説明しているからです) 'asyncio'とは無関係で、対応する' os.close() '呼び出しは' subprocess'モジュールにあります)。私が引用した例は、 'process_1.stdout.close()'に対応しています。つまり、 'os.close(read)'が呼び出されます。 – jfs

関連する問題