簡體   English   中英

Python子進程readlines()掛起

[英]Python subprocess readlines() hangs

我嘗試完成的任務是流式傳輸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()最后會掛起而永不退出。 我從未到過最后一個印刷品。

我知道有很多這樣的問題,這里有一個stackoverflow但是沒有它們讓我解決問題。 我不是那個整個子流程的東西,所以請給我一個更實際/具體的答案。

問候

編輯

修復意外的代碼。 (與實際錯誤無關)

我假設您使用pty由於Q中概述的原因:為什么不使用管道(popen())? (到目前為止所有其他答案都忽略了你的“注意:我不想一次打印出所有內容” )。

pty是Linux,僅在文檔中說

因為偽終端處理是高度依賴於平台的,所以只有Linux代碼才能執行。 (Linux代碼應該在其他平台上運行,但尚未經過測試。)

目前還不清楚它在其他操作系統上的效果如何。

你可以嘗試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的答案使用stdlib中的pty

#!/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!")

所有三個代碼示例立即打印'hello'(只要看到第一個EOL)。


在這里留下舊的更復雜的代碼示例,因為它可能在SO上的其他帖子中被引用和討論

或者根據@Antti Haapala的回答使用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!")

不確定您的代碼有什么問題,但以下似乎對我有用:

#!/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正常工作。

基本上你在這里看到的是proc.poll()和你的readline()之間的競爭條件。 由於master文件句柄上的輸入永遠不會關閉,如果進程在ruby進程完成輸出后嘗試對其執行readline() ,則永遠不會有任何內容可讀,但管道將永遠不會關閉。 只有在代碼嘗試另一個readline()之前shell進程關閉時,代碼才有效。

這是時間表:

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的子進程掛起

嘗試這個:

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,因此它不會自動關閉。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM