31

私が達成しようとしているタスクは、rubyファイルをストリーミングして出力を出力することです。(:一度にすべてを印刷したくない)

main.py

from subprocess import Popen, PIPE, STDOUT

import pty
import os

file_path = '/Users/luciano/Desktop/ruby_sleep.rb'

command = ' '.join(["ruby", file_path])

master, slave = pty.openpty()
proc = Popen(command, bufsize=0, shell=True, stdout=slave, stderr=slave, close_fds=True)     
stdout = os.fdopen(master, 'r', 0)

while proc.poll() is None:
    data = stdout.readline()
    if data != "":
        print(data)
    else:
        break

print("This is never reached!")

ruby_sleep.rb

puts "hello"

sleep 2

puts "goodbye!"

問題

ファイルのストリーミングは正常に機能します。hello/goodbye出力は2秒の遅延で出力されます。スクリプトが機能するはずです。問題は、readline()が最後にハングし、終了しないことです。私は最後のプリントに到達することはありません。

私はここにスタックオーバーフローのような多くの質問があることを知っていますが、それらのどれも私に問題を解決させませんでした。私はサブプロセス全体に精通しているわけではないので、もっと実践的で具体的な答えを教えてください。

よろしく

編集

意図しないコードを修正します。(実際のエラーとは関係ありません)

4

4 に答える 4

34

Q:パイプ(popen())だけを使用ptyないのはなぜですか?(これまでの他のすべての回答は、 「注:すべてを一度に印刷したくない」を無視します)。

ptyドキュメントで述べられているようにLinuxのみです:

疑似端末の処理はプラットフォームに大きく依存するため、Linuxでのみ処理するコードがあります。(Linuxコードは他のプラットフォームで動作するはずですが、まだテストされていません。)

他のOSでどれだけうまく機能するかは不明です。

あなたは試すことができますpexpect

import sys
import pexpect

pexpect.run("ruby ruby_sleep.rb", logfile=sys.stdout)

またはstdbuf、非対話モードでラインバッファリングを有効にするには:

from subprocess import Popen, PIPE, STDOUT

proc = Popen(['stdbuf', '-oL', 'ruby', 'ruby_sleep.rb'],
             bufsize=1, stdout=PIPE, stderr=STDOUT, close_fds=True)
for line in iter(proc.stdout.readline, b''):
    print line,
proc.stdout.close()
proc.wait()

または、 @ Antti Haapalaの回答ptyに基づいてstdlibから使用します:

#!/usr/bin/env python
import errno
import os
import pty
from subprocess import Popen, STDOUT

master_fd, slave_fd = pty.openpty()  # provide tty to enable
                                     # line-buffering on ruby's side
proc = Popen(['ruby', 'ruby_sleep.rb'],
             stdin=slave_fd, stdout=slave_fd, stderr=STDOUT, close_fds=True)
os.close(slave_fd)
try:
    while 1:
        try:
            data = os.read(master_fd, 512)
        except OSError as e:
            if e.errno != errno.EIO:
                raise
            break # EIO means EOF on some systems
        else:
            if not data: # EOF
                break
            print('got ' + repr(data))
finally:
    os.close(master_fd)
    if proc.poll() is None:
        proc.kill()
    proc.wait()
print("This is reached!")

3つのコード例はすべて、すぐに「hello」を出力します(最初のEOLが表示されるとすぐに)。


SOの他の投稿で参照および説明されている可能性があるため、古いより複雑なコード例をここに残します

または@AnttiHaapalaの回答ptyに基づいて使用します:

import os
import pty
import select
from subprocess import Popen, STDOUT

master_fd, slave_fd = pty.openpty()  # provide tty to enable
                                     # line-buffering on ruby's side
proc = Popen(['ruby', 'ruby_sleep.rb'],
             stdout=slave_fd, stderr=STDOUT, close_fds=True)
timeout = .04 # seconds
while 1:
    ready, _, _ = select.select([master_fd], [], [], timeout)
    if ready:
        data = os.read(master_fd, 512)
        if not data:
            break
        print("got " + repr(data))
    elif proc.poll() is not None: # select timeout
        assert not select.select([master_fd], [], [], 0)[0] # detect race condition
        break # proc exited
os.close(slave_fd) # can't do it sooner: it leads to errno.EIO error
os.close(master_fd)
proc.wait()

print("This is reached!")
于 2012-09-18T06:58:53.747 に答える
5

コードの何が問題になっているのかわかりませんが、次のことがうまくいくようです。

#!/usr/bin/python

from subprocess import Popen, PIPE
import threading

p = Popen('ls', stdout=PIPE)

class ReaderThread(threading.Thread):

    def __init__(self, stream):
        threading.Thread.__init__(self)
        self.stream = stream

    def run(self):
        while True:
            line = self.stream.readline()
            if len(line) == 0:
                break
            print line,


reader = ReaderThread(p.stdout)
reader.start()

# Wait until subprocess is done
p.wait()

# Wait until we've processed all output
reader.join()

print "Done!"

私はRubyをインストールしていないため、実際の問題を確認できないことに注意してください。ただし、で正常に動作しlsます。

于 2012-09-17T14:23:30.250 に答える
2

基本的に、ここで見ているのは、proc.poll()との間の競合状態readline()です。ファイルハンドルの入力masterが閉じられることはないためreadline()、rubyプロセスの出力が終了した後にプロセスがファイルハンドルに対して入力を行おうとすると、読み取るものはありませんが、パイプは閉じられません。コードが機能するのは、コードが別のreadline()を試行する前にシェルプロセスが閉じた場合のみです。

タイムラインは次のとおりです。

readline()
print-output
poll()
readline()
print-output (last line of real output)
poll() (returns false since process is not done)
readline() (waits for more output)
(process is done, but output pipe still open and no poll ever happens for it).

簡単な修正は、openptyと組み合わせてではなく、ドキュメントで提案されているようにサブプロセスモジュールを使用することです。

http://docs.python.org/library/subprocess.html

これは、さらに研究するための非常によく似た問題です。

出力をキャプチャするときにselectとptyでサブプロセスを使用するとハングします

于 2012-09-17T17:34:47.440 に答える
1

これを試して:

proc = Popen(command, bufsize=0, shell=True, stdout=PIPE, close_fds=True)
for line in proc.stdout:
    print line

print("This is most certainly reached!")

他の人が指摘しているように、readline()データを読み取るときにブロックされます。あなたの子プロセスが死んだときでさえそうするでしょう。他の回答のように実行時にこれが発生しない理由はわかりませlsんが、rubyインタープリターが、PIPEに書き込んでいることを検出したため、自動的に閉じない可能性があります。

于 2012-09-17T17:52:39.497 に答える