web-dev-qa-db-ja.com

pythonサブプロセス通信の複数の入力と出力

この投稿 のようなことをする必要がありますが、入力を与えたり出力を何度も与えることができるサブプロセスを作成する必要があります。その投稿の受け入れられた答えは良いコードを持っています...

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())

# four
# five

...私はこのように続けたいと思います:

grep_stdout2 = p.communicate(input=b'spam\neggs\nfrench fries\nbacon\nspam\nspam\n')[0]
print(grep_stdout2.decode())

# french fries

しかし、悲しいかな、私は次のエラーを受け取ります:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 928, in communicate
    raise ValueError("Cannot send input after starting communication")
ValueError: Cannot send input after starting communication

私が正しく理解している場合、proc.stdin.write()メソッドでは出力を収集できません。進行中の入出力のためにラインを開いたままにする最も簡単な方法は何ですか?

編集:====================

pexpectは、私がやろうとしていることには便利なライブラリのようですが、機能させるのに苦労しています。これが私の実際のタスクのより完全な説明です。私はhfstを使用して、個々の(ロシア語)単語の文法分析を取得しています。以下は、bashシェルでの動作を示しています。

$ hfst-lookup analyser-gt-desc.hfstol
> слово
слово   слово+N+Neu+Inan+Sg+Acc 0.000000
слово   слово+N+Neu+Inan+Sg+Nom 0.000000

> сработай
сработай    сработать+V+Perf+IV+Imp+Sg2 0.000000
сработай    сработать+V+Perf+TV+Imp+Sg2 0.000000

> 

スクリプトで一度に1つのフォームの分析を取得できるようにしたいと考えています。このようなコードを試しましたが、機能しません。

import pexpect

analyzer = pexpect.spawnu('hfst-lookup analyser-gt-desc.hfstol')
for newWord in ['слово','сработай'] :
    print('Trying', newWord, '...')
    analyzer.expect('> ')
    analyzer.sendline( newWord )
    print(analyzer.before)

# trying слово ...
# 
# trying сработай ...
# слово
# слово слово+N+Neu+Inan+Sg+Acc 0.000000
# слово слово+N+Neu+Inan+Sg+Nom 0.000000
# 
# 

私は明らかに何を誤解していますpexpect.beforeします。各Wordの出力を一度に1つ取得するにはどうすればよいですか?

15
reynoldsnlp

この回答は@ J.F。Sebastianによるものです。コメントをありがとう!

次のコードは私の期待される動作をしました:

import pexpect

analyzer = pexpect.spawnu('hfst-lookup analyser-gt-desc.hfstol')
analyzer.expect('> ')

for Word in ['слово', 'сработай']:
    print('Trying', Word, '...')
    analyzer.sendline(Word)
    analyzer.expect('> ')
    print(analyzer.before)
12
reynoldsnlp

Popen.communicate()は、stdinへのデータの1回限りの書き込みを行い、stdoutおよびstderrからデータをプルするスレッドを作成するヘルパーメソッドです。データの書き込みが完了するとstdinを閉じ、それらのパイプが閉じるまでstdoutstderrを読み取ります。子が戻ったときにはすでに子が出ているため、2番目のcommunicateを実行することはできません。

子プロセスとの対話型セッションは、かなり複雑です。

1つの問題は、子プロセスが対話型であることを認識しているかどうかです。ほとんどのコマンドラインプログラムが対話に使用するCライブラリでは、端末(たとえば、Linuxコンソールまたは「pty」疑似端末)から実行されるプログラムは対話型であり、出力を頻繁にフラッシュしますが、PIPESを介して他のプログラムから実行されるプログラムは非インタラクティブで、出力をまれにフラッシュします。

もう1つは、デッドロックなしでstdoutおよびstderrを読み取って処理する方法です。たとえば、stdoutの読み取りをブロックしても、stderrがパイプをいっぱいにすると、子は停止し、行き詰まります。スレッドを使用して、両方を内部バッファーにプルすることができます。

さらにもう1つは、予期せずに終了する子供への対処方法です。

LinuxやOSXなどの「ユニキシ」システムの場合、pexpectモジュールは、インタラクティブな子プロセスの複雑さを処理するために作成されます。 Windowsの場合、私が知っている優れたツールはありません。

18
tdelaney

入力をプロセスに送りたいときはいつでもproc.stdin.write()を使用してください。プロセスから出力を取得したい場合はいつでもproc.stdout.read()を使用してください。コンストラクタのstdin引数とstdout引数の両方をPIPEに設定する必要があります。

3
Tom Hunt

HFSTにはPython bindings: https://pypi.python.org/pypi/hfst

これらを使用すると、フラッシュの問題全体が回避され、pexpectからの文字列出力を解析するよりも、よりクリーンなAPIで作業できます。

Python REPLから、バインディングに関するいくつかのドキュメントを取得できます

dir(hfst)
help(hfst.HfstTransducer)

または https://hfst.github.io/python/3.12.2/QuickStart.html をお読みください

ドキュメントの関連部分をひったくり:

istr = hfst.HfstInputStream('hfst-lookup analyser-gt-desc.hfstol')
transducers = []
while not (istr.is_eof()):
    transducers.append(istr.read())
istr.close()
print("Read %i transducers in total." % len(transducers))
if len(transducers) == 1:
  out = transducers[0].lookup_optimize("слово")
  print("got %s" % (out,))
else: 
  pass # or handle >1 fst in the file, though I'm guessing you don't use that feature
2
unhammer