Неблокування зчитування на підпроцесі.PIPE в python


506

Я використовую модуль підпроцеси для запуску підпроцесу та підключення до його вихідного потоку (stdout). Я хочу мати можливість виконувати незаблокуючі читання на його stdout. Чи є спосіб зробити .readline не блокуючим або перевірити, чи є дані в потоці перед тим, як викликати .readline? Я хотів би, щоб це було портативним чи принаймні працювало під Windows та Linux.

ось як я це роблю зараз (блокується, .readlineякщо немає даних):

p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()

14
(Походить з google?) Всі PIPE будуть глухими, коли один з буферів PIPE заповниться і не буде прочитаний. наприклад, тупик stdout, коли заповнюється stderr. Ніколи не проходьте PIPE, який ви не збираєтесь читати.
Насер Аль-Вохайбі

@ NasserAl-Wohaibi це означає, що краще завжди створювати файли тоді?
Чарлі Паркер

щось мені цікаво було зрозуміти, чому його блокування в першу чергу ... я прошу, тому що я бачив коментар:To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Чарлі Паркер

Це, "за задумом", чекає отримання вхідних даних.
Патьє Матьє

Відповіді:


403

fcntl, select,asyncproc Чи не допоможе в цьому випадку.

Надійним способом зчитування потоку без блокування незалежно від операційної системи є використання Queue.get_nowait():

import sys
from subprocess import PIPE, Popen
from threading  import Thread

try:
    from queue import Queue, Empty
except ImportError:
    from Queue import Queue, Empty  # python 2.x

ON_POSIX = 'posix' in sys.builtin_module_names

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()

# ... do other things here

# read line without blocking
try:  line = q.get_nowait() # or q.get(timeout=.1)
except Empty:
    print('no output yet')
else: # got line
    # ... do something with line

6
Так, це для мене працює, хоча я багато чого видалив. Вона включає в себе добрі практики, але не завжди необхідні. Python 3.x 2.X compat та close_fds можуть бути опущені, вони все одно працюватимуть. Але просто будьте в курсі того, що все робить, і не копіюйте його наосліп, навіть якщо це просто працює! (Насправді найпростішим рішенням є використання потоку та виконання рядку читання так, як це робив Seb. Черги - це просто простий спосіб отримати дані. Є інші, теми - це відповідь!)
Акі,

3
Всередині потоку виклик out.readlineблокує потік та основну нитку, і мені доведеться чекати, поки повернення читання повернеться, перш ніж все інше продовжиться. Будь-який простий спосіб цього? (Я читаю декілька рядків з мого процесу, який також є іншим .py-файлом, який робить DB та інші речі)
Джастін,

3
@Justin: 'out.readline' не блокує основний потік, який він виконується в іншій потоці.
jfs

4
що робити, якщо я не зможу вимкнути підпроцес, наприклад через винятки? нитка читача stdout не загине, і python зависне, навіть якщо основна нитка вийшла, чи не так? як можна було обійти це? python 2.x не підтримує вбивання потоків, що гірше, не підтримує їх переривання. :( (очевидно, слід обробляти винятки, щоб переконатися, що підпроцес вимкнено, але на випадок, якщо цього не стане, що робити?)
n611x007

3
Я створив кілька дружніх shelljob обгортків
edA-qa mort-ora-y

77

У мене часто виникали подібні проблеми; Програми Python, які я пишу часто, повинні мати можливість виконувати деякі основні функції, одночасно приймаючи введення користувача з командного рядка (stdin). Просте введення функцій обробки даних користувача в інший потік не вирішує проблему, оскільки readline()блокує та не має часу. Якщо основний функціонал завершений і більше немає потреби чекати подальшого введення користувача, я зазвичай хочу, щоб моя програма вийшла, але вона не може, тому що readline()все ще блокується в іншому потоці, що чекає рядка. Я знайшов рішення цієї проблеми - зробити stdin незаблокувальний файл за допомогою модуля fcntl:

import fcntl
import os
import sys

# make stdin a non-blocking file
fd = sys.stdin.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

# user input handling thread
while mainThreadIsRunning:
      try: input = sys.stdin.readline()
      except: continue
      handleInput(input)

На мій погляд, це трохи чистіше, ніж використання модулів вибору або сигналу для вирішення цієї проблеми, але знову ж таки, це працює лише на UNIX ...


1
Згідно з документами, fcntl () може отримати або дескриптор файлу, або об'єкт, який має метод .fileno ().
Denilson Sá Maia

10
Відповідь Джессі не правильна. За словами Guido, readline не працює належним чином у режимі, що не блокує, і це не стане перед Python 3000. bugs.python.org/issue1175#msg56041 Якщо ви хочете використовувати fcntl для встановлення файлу в режим незаблокування , ви повинні використовувати нижній рівень os.read () та самостійно відокремити рядки. Змішування fcntl з викликами високого рівня, які виконують буферизацію лінії, задає проблеми.
anonnn

2
Використання Readline здається неправильним у відповідь Python 2. Див anonnn в stackoverflow.com/questions/375427 / ...
Каталін Iacob

10
Будь ласка, не використовуйте зайняті петлі. Використовуйте опитування () з тимчасовим очікуванням, щоб чекати даних.
Іво Даниелка

@Stefano що buffer_sizeвизначено як?
кіт

39

Python 3.4 представляє новий попередній API для асинхронного IO - asyncioмодуля .

Підхід схожий twistedна відповідь на основі @Bryan Ward - визначте протокол і його методи викликаються, як тільки дані будуть готові:

#!/usr/bin/env python3
import asyncio
import os

class SubprocessProtocol(asyncio.SubprocessProtocol):
    def pipe_data_received(self, fd, data):
        if fd == 1: # got stdout data (bytes)
            print(data)

    def connection_lost(self, exc):
        loop.stop() # end loop.run_forever()

if os.name == 'nt':
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, 
        "myprogram.exe", "arg1", "arg2"))
    loop.run_forever()
finally:
    loop.close()

Див. "Підпроцес" у документах .

Існує інтерфейс високого рівня, asyncio.create_subprocess_exec()який повертає Processоб'єкти, що дозволяє читати рядок асинхронізовано за допомогою StreamReader.readline()підпрограмисинтаксисом async/ awaitPython 3.5+ ):

#!/usr/bin/env python3.5
import asyncio
import locale
import sys
from asyncio.subprocess import PIPE
from contextlib import closing

async def readline_and_kill(*args):
    # start child process
    process = await asyncio.create_subprocess_exec(*args, stdout=PIPE)

    # read line (sequence of bytes ending with b'\n') asynchronously
    async for line in process.stdout:
        print("got line:", line.decode(locale.getpreferredencoding(False)))
        break
    process.kill()
    return await process.wait() # wait for the child process to exit


if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

with closing(loop):
    sys.exit(loop.run_until_complete(readline_and_kill(
        "myprogram.exe", "arg1", "arg2")))

readline_and_kill() виконує такі завдання:

  • запустити підпроцес, перенаправити його stdout на трубу
  • асинхронно читати рядок з підпроцесу 'stdout
  • вбити підпроцес
  • дочекайтеся його виходу

Кожен крок при необхідності може бути обмежений секундами тайм-ауту.


Коли я пробую щось подібне, використовуючи python 3.4 підпрограми, я отримую вихід лише після запуску всього сценарію. Я хотів би побачити друкований рядок виводу, як тільки підпроцес надрукує рядок. Ось що я отримав: pastebin.com/qPssFGep .
flutefreak7

1
@ flutefreak7: проблеми буферизації не пов'язані з поточним питанням. Перейдіть за посиланням для можливих рішень.
jfs

Дякую! Вирішили проблему для мого сценарію, просто скориставшись print(text, flush=True)таким чином, щоб надрукований текст був негайно доступним для того, щоб він звертався до телефону readline. Коли я тестував його за допомогою виконуваного на Fortran файлу, я насправді хочу завернути / переглядати, він не буферизує його вихід, тому він веде себе так, як очікувалося.
flutefreak7

Чи можна дозволити підпроцесу зберігатися та виконувати подальші операції читання / запису. readline_and_kill, у вашому другому сценарії працює дуже схоже subprocess.comunicateна те, що він припиняє процес після однієї операції читання / запису. Я також бачу, що ви використовуєте одну трубу stdout, яка підпроцесуру обробляє як незаблокуючу. Намагаюсь використовувати обидва, stdoutі stderr я виявляю, що в кінцевому рахунку блокується .
Карел

@Carel код у відповіді працює так, як задумано, як явно описано у відповіді. За бажанням можна реалізувати іншу поведінку. Обидві труби однаково не блокують, якщо вони використовуються, ось приклад того, як одночасно читати з обох труб .
jfs

19

Спробуйте модуль asyncproc . Наприклад:

import os
from asyncproc import Process
myProc = Process("myprogram.app")

while True:
    # check to see if process has ended
    poll = myProc.wait(os.WNOHANG)
    if poll != None:
        break
    # print any new output
    out = myProc.read()
    if out != "":
        print out

Модуль піклується про всі нитки, як запропонував С.Лотт.


1
Абсолютно геніально. Набагато простіше, ніж необроблений модуль підпроцесу. Для мене прекрасно працює на Ubuntu.
Серін

12
asyncproc не працює на windows, а windows не підтримує os.WNOHANG :-(
Брайан Оуклі

26
asyncproc - це GPL, що ще більше обмежує його використання :-(
Брайан Оуклі

Дякую. Одна невелика річ: схоже, що заміна вкладок з 8 пробілами в asyncproc.py - це спосіб
дійти

Не схоже, що ви можете отримати код повернення процесу, який ви запустили, хоча через модуль asyncproc; тільки вихід, який він створив.
граяїї

17

Це можна зробити дуже легко в Twisted . Залежно від наявної кодової бази, це може бути не таким простим у використанні, але якщо ви будуєте скручену програму, такі речі стають майже тривіальними. Ви створюєте ProcessProtocolклас та заміняєте outReceived()метод. Скручений (залежно від використовуваного реактора), як правило, є лише великим select()циклом із встановленими зворотними дзвінками для обробки даних з різних дескрипторів файлів (часто мережевих розеток). Таким чином, outReceived()метод є просто встановленням зворотного дзвінка для обробки даних, що надходять STDOUT. Простий приклад, що демонструє таку поведінку:

from twisted.internet import protocol, reactor

class MyProcessProtocol(protocol.ProcessProtocol):

    def outReceived(self, data):
        print data

proc = MyProcessProtocol()
reactor.spawnProcess(proc, './myprogram', ['./myprogram', 'arg1', 'arg2', 'arg3'])
reactor.run()

Документація Twisted має добру інформацію щодо цього.

Якщо ви будуєте всю свою програму навколо Twisted, це робить асинхронний зв’язок з іншими процесами, локальними чи віддаленими, справді елегантними, як це. З іншого боку, якщо ваша програма не побудована поверх закрученого, це насправді не буде таким корисним. Сподіваємось, це може бути корисним для інших читачів, навіть якщо це не застосовується для вашої конкретної програми.


не добре. selectне повинен працювати у Windows з дескрипторами файлів, згідно з документами
n611x007

2
@naxa Я не думаю, що select()він має на увазі такий самий, як ти. Я припускаю це, оскільки Twistedпрацює на windows ...
notbad.jpeg


1
"Скручена (залежно від використовуваного реактора), як правило, є великим циклом вибору ()" означає, що між ними можна вибрати кілька реакторів. Один з select()них є найбільш портативним на unix і unix-like, але також є два реактори, доступні для Windows: twistedmatrix.com/documents/current/core/howto/…
clacke

14

Скористайтеся функцією Select & read (1).

import subprocess     #no new requirements
def readAllSoFar(proc, retVal=''): 
  while (select.select([proc.stdout],[],[],0)[0]!=[]):   
    retVal+=proc.stdout.read(1)
  return retVal
p = subprocess.Popen(['/bin/ls'], stdout=subprocess.PIPE)
while not p.poll():
  print (readAllSoFar(p))

Для readline () - як:

lines = ['']
while not p.poll():
  lines = readAllSoFar(p, lines[-1]).split('\n')
  for a in range(len(lines)-1):
    print a
lines = readAllSoFar(p, lines[-1]).split('\n')
for a in range(len(lines)-1):
  print a

6
не добре. selectне повинен працювати у Windows з дескрипторами файлів, згідно з документами
n611x007

О БОЖЕ МІЙ. Читайте мегабайти або, можливо, гігабайти по одному символу за часом ... це найгірша ідея, яку я бачив за останній час ... непотрібно згадувати, що цей код не працює, бо proc.stdout.read()як би аргумент малий не був. дзвінок блокування.
wvxvw

OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
nmz787

8

Одне рішення - зробити інший процес для того, щоб виконати ваше читання процесу, або зробити потік процесу із затримкою часу.

Ось потокова версія функції тайм-ауту:

http://code.activestate.com/recipes/473878/

Однак чи потрібно читати stdout, коли він надходить? Іншим рішенням може бути скидання виводу у файл та чекання закінчення процесу за допомогою p.wait () .

f = open('myprogram_output.txt','w')
p = subprocess.Popen('myprogram.exe', stdout=f)
p.wait()
f.close()


str = open('myprogram_output.txt','r').read()

здається , що нитка recpie не вийде після закінчення тайм-ауту, і її вбивство залежить від можливості вбити підпроцес (наприклад, інакше не пов'язаний з цього приводу), який він читає (річ, яку ви повинні вміти, але про всяк випадок, коли не можете ..) .
n611x007

7

Відмова від відповідальності: це працює лише для смерчі

Це можна зробити, встановивши fd на неблокуючий, а потім скористайтеся ioloop для реєстрації зворотних дзвінків. Я упакував це в яйце під назвою tornado_subprocess, і ви можете встановити його через PyPI:

easy_install tornado_subprocess

тепер ви можете зробити щось подібне:

import tornado_subprocess
import tornado.ioloop

    def print_res( status, stdout, stderr ) :
    print status, stdout, stderr
    if status == 0:
        print "OK:"
        print stdout
    else:
        print "ERROR:"
        print stderr

t = tornado_subprocess.Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] )
t.start()
tornado.ioloop.IOLoop.instance().start()

Ви також можете використовувати його з RequestHandler

class MyHandler(tornado.web.RequestHandler):
    def on_done(self, status, stdout, stderr):
        self.write( stdout )
        self.finish()

    @tornado.web.asynchronous
    def get(self):
        t = tornado_subprocess.Subprocess( self.on_done, timeout=30, args=[ "cat", "/etc/passwd" ] )
        t.start()

Дякую за гарну особливість! Для уточнення, чому ми не можемо просто використовувати threading.Threadдля створення нових незаблокувальних процесів? Я використовував його в on_messageекземплярі веб-сорочок Tornado, і це справило чудово.
VisioN

1
різьблення в основному не відволікається від торнадо. вони чудово підходять для невеликих функцій, що працюють в короткому режимі. Ви можете прочитати про це тут: stackoverflow.com/questions/7846323/tornado-web-and-threads github.com/facebook/tornado/wiki/Threading-and-concurrency
Vukasin Toroman

@VukasinToroman ти справді врятував мене тут. велике дякую за модуль tornado_subprocess :)
Джеймс Гентес

це працює на windows? (зауважте, що selectз дескрипторами файлів немає )
n611x007

Ця вкладка не використовує selectдзвінок. Я не пробував цього в Windows, але ви, мабуть, зіткнулися з проблемою, оскільки lib використовує fcntlмодуль. Отже, коротше: ні це, ймовірно, не буде працювати під Windows.
Вукасін Тороман

6

Існуючі рішення для мене не спрацювали (детальніше нижче). Нарешті, що працювало - це реалізовувати readline за допомогою read (1) (виходячи з цієї відповіді ). Останнє не блокує:

from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
    nextline = None
    buf = ''
    while True:
        #--- extract line using read(1)
        out = myprocess.stdout.read(1)
        if out == '' and myprocess.poll() != None: break
        if out != '':
            buf += out
            if out == '\n':
                nextline = buf
                buf = ''
        if not nextline: continue
        line = nextline
        nextline = None

        #--- do whatever you want with line here
        print 'Line is:', line
    myprocess.stdout.close()

myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread
p1.daemon = True
p1.start()

#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
    myprocess.kill()
    myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
    p1.join()

Чому існуючі рішення не спрацювали:

  1. Рішення, для яких потрібна лінія для читання (включаючи основу черги), завжди блокуються. Важко (неможливо?) Вбити потік, який виконує readline. Він вбивається лише тоді, коли процес, який його створив, закінчується, але не тоді, коли процес виробництва, який виробляє.
  2. Змішання низького рівня fcntl з високими рівнями викликів зчитування може не працювати належним чином, як зазначає anonnn.
  3. Використання select.poll () є акуратним, але не працює в Windows згідно з документами python.
  4. Використання сторонніх бібліотек здається непосильним для цього завдання та додає додаткових залежностей.

1
1. q.get_nowait()з моєї відповіді не слід блокувати ніколи, в чому полягає сенс її використання. 2. Потік, що виконує readline ( enqueue_output()функцію ), виходить на EOF, наприклад, включаючи випадок, коли процес створення виходу знищений. Якщо ви вірите, це не так; надайте повний приклад мінімального коду, який відображається інакше (можливо, як нове запитання ).
jfs

1
@sebastian Я витратив годину або більше, намагаючись придумати мінімальний приклад. Зрештою, я повинен погодитися, що ваша відповідь стосується всіх випадків. Я вважаю, що раніше це не спрацювало для мене, тому що коли я намагався вбити процес, що виробляє вихід, він уже був убитий і дав важку помилку. Година була добре проведена, бо, придумуючи мінімальний приклад, я міг придумати більш просте рішення.
Vikram Pudi

Чи можете ви також розмістити більш просте рішення? :) (якщо вона відрізняється від Себастьянської)
n611x007

@ небезпека89: Я думаю dcmpid = myprocess.
ViFI

В умові після виклику read () (відразу після True): out ніколи не буде порожнім рядком, тому що ви читаєте принаймні рядок / байти довжиною 1.
sergzach

6

Ось мій код, який використовується для лову кожного виводу з підпроцесу ASAP, включаючи часткові рядки. Він накачує одночасно та stdout та stderr майже у правильному порядку.

Перевірено та правильно працювали на Python 2.7 linux & windows.

#!/usr/bin/python
#
# Runner with stdout/stderr catcher
#
from sys import argv
from subprocess import Popen, PIPE
import os, io
from threading import Thread
import Queue
def __main__():
    if (len(argv) > 1) and (argv[-1] == "-sub-"):
        import time, sys
        print "Application runned!"
        time.sleep(2)
        print "Slept 2 second"
        time.sleep(1)
        print "Slept 1 additional second",
        time.sleep(2)
        sys.stderr.write("Stderr output after 5 seconds")
        print "Eol on stdin"
        sys.stderr.write("Eol on stderr\n")
        time.sleep(1)
        print "Wow, we have end of work!",
    else:
        os.environ["PYTHONUNBUFFERED"]="1"
        try:
            p = Popen( argv + ["-sub-"],
                       bufsize=0, # line-buffered
                       stdin=PIPE, stdout=PIPE, stderr=PIPE )
        except WindowsError, W:
            if W.winerror==193:
                p = Popen( argv + ["-sub-"],
                           shell=True, # Try to run via shell
                           bufsize=0, # line-buffered
                           stdin=PIPE, stdout=PIPE, stderr=PIPE )
            else:
                raise
        inp = Queue.Queue()
        sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
        serr = io.open(p.stderr.fileno(), 'rb', closefd=False)
        def Pump(stream, category):
            queue = Queue.Queue()
            def rdr():
                while True:
                    buf = stream.read1(8192)
                    if len(buf)>0:
                        queue.put( buf )
                    else:
                        queue.put( None )
                        return
            def clct():
                active = True
                while active:
                    r = queue.get()
                    try:
                        while True:
                            r1 = queue.get(timeout=0.005)
                            if r1 is None:
                                active = False
                                break
                            else:
                                r += r1
                    except Queue.Empty:
                        pass
                    inp.put( (category, r) )
            for tgt in [rdr, clct]:
                th = Thread(target=tgt)
                th.setDaemon(True)
                th.start()
        Pump(sout, 'stdout')
        Pump(serr, 'stderr')

        while p.poll() is None:
            # App still working
            try:
                chan,line = inp.get(timeout = 1.0)
                if chan=='stdout':
                    print "STDOUT>>", line, "<?<"
                elif chan=='stderr':
                    print " ERROR==", line, "=?="
            except Queue.Empty:
                pass
        print "Finish"

if __name__ == '__main__':
    __main__()

Один з небагатьох відповідей, який дозволяє читати матеріали, які не обов'язково закінчуються новим рядком.
вчора

5

Я додаю цю проблему, щоб прочитати деякий підпроцес. Відкрити stdout. Ось моє рішення для читання, що не блокує:

import fcntl

def non_block_read(output):
    fd = output.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    try:
        return output.read()
    except:
        return ""

# Use example
from subprocess import *
sb = Popen("echo test && sleep 1000", shell=True, stdout=PIPE)
sb.kill()

# sb.stdout.read() # <-- This will block
non_block_read(sb.stdout)
'test\n'

5
fcntl не працює на windows, згідно з документами .
n611x007

@anatolytechtonik використовуйте msvcrt.kbhit()натомість
кіт

4

Ця версія не блокує зчитування не потребує спеціальних модулів і працюватиме нестандартно для більшості дистрибутивів Linux.

import os
import sys
import time
import fcntl
import subprocess

def async_read(fd):
    # set non-blocking flag while preserving old flags
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    # read char until EOF hit
    while True:
        try:
            ch = os.read(fd.fileno(), 1)
            # EOF
            if not ch: break                                                                                                                                                              
            sys.stdout.write(ch)
        except OSError:
            # waiting for data be available on fd
            pass

def shell(args, async=True):
    # merge stderr and stdout
    proc = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if async: async_read(proc.stdout)
    sout, serr = proc.communicate()
    return (sout, serr)

if __name__ == '__main__':
    cmd = 'ping 8.8.8.8'
    sout, serr = shell(cmd.split())

3

Ось просте рішення, засноване на потоках, які:

  • працює і в Linux, і в Windows (не покладаючись на select).
  • читає stdoutі stderrасинхронно.
  • не покладається на активне опитування з довільним часом очікування (дружнє до процесора).
  • не використовує asyncio(що може суперечити іншим бібліотекам).
  • триває доти, доки не припиниться процес дитини.

printer.py

import time
import sys

sys.stdout.write("Hello\n")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("World!\n")
sys.stdout.flush()
time.sleep(1)
sys.stderr.write("That's an error\n")
sys.stderr.flush()
time.sleep(2)
sys.stdout.write("Actually, I'm fine\n")
sys.stdout.flush()
time.sleep(1)

читач.py

import queue
import subprocess
import sys
import threading


def enqueue_stream(stream, queue, type):
    for line in iter(stream.readline, b''):
        queue.put(str(type) + line.decode('utf-8'))
    stream.close()


def enqueue_process(process, queue):
    process.wait()
    queue.put('x')


p = subprocess.Popen('python printer.py', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
q = queue.Queue()
to = threading.Thread(target=enqueue_stream, args=(p.stdout, q, 1))
te = threading.Thread(target=enqueue_stream, args=(p.stderr, q, 2))
tp = threading.Thread(target=enqueue_process, args=(p, q))
te.start()
to.start()
tp.start()

while True:
    line = q.get()
    if line[0] == 'x':
        break
    if line[0] == '2':  # stderr
        sys.stdout.write("\033[0;31m")  # ANSI red color
    sys.stdout.write(line[1:])
    if line[0] == '2':
        sys.stdout.write("\033[0m")  # reset ANSI code
    sys.stdout.flush()

tp.join()
to.join()
te.join()

2

Додавши сюди цю відповідь, оскільки вона надає можливість встановлювати неблокуючі труби в Windows та Unix.

Усі ctypesдеталі - завдяки відповіді @ techtonik .

Існує дещо модифікована версія, яка використовується як для систем Unix, так і для Windows.

  • Сумісний з Python3 (потрібні лише незначні зміни) .
  • Включає версію posix і визначає виняток, який слід використовувати для будь-якої.

Таким чином ви можете використовувати ту саму функцію та виняток для коду Unix та Windows.

# pipe_non_blocking.py (module)
"""
Example use:

    p = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            )

    pipe_non_blocking_set(p.stdout.fileno())

    try:
        data = os.read(p.stdout.fileno(), 1)
    except PortableBlockingIOError as ex:
        if not pipe_non_blocking_is_error_blocking(ex):
            raise ex
"""


__all__ = (
    "pipe_non_blocking_set",
    "pipe_non_blocking_is_error_blocking",
    "PortableBlockingIOError",
    )

import os


if os.name == "nt":
    def pipe_non_blocking_set(fd):
        # Constant could define globally but avoid polluting the name-space
        # thanks to: /programming/34504970
        import msvcrt

        from ctypes import windll, byref, wintypes, WinError, POINTER
        from ctypes.wintypes import HANDLE, DWORD, BOOL

        LPDWORD = POINTER(DWORD)

        PIPE_NOWAIT = wintypes.DWORD(0x00000001)

        def pipe_no_wait(pipefd):
            SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
            SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
            SetNamedPipeHandleState.restype = BOOL

            h = msvcrt.get_osfhandle(pipefd)

            res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
            if res == 0:
                print(WinError())
                return False
            return True

        return pipe_no_wait(fd)

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        from ctypes import GetLastError
        ERROR_NO_DATA = 232

        return (GetLastError() == ERROR_NO_DATA)

    PortableBlockingIOError = OSError
else:
    def pipe_non_blocking_set(fd):
        import fcntl
        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        return True

    def pipe_non_blocking_is_error_blocking(ex):
        if not isinstance(ex, PortableBlockingIOError):
            return False
        return True

    PortableBlockingIOError = BlockingIOError

Щоб уникнути читання неповних даних, я закінчив писати власний генератор читання рядків (який повертає рядок байтів для кожного рядка).

Його генератор, щоб ви могли, наприклад, ...

def non_blocking_readlines(f, chunk=1024):
    """
    Iterate over lines, yielding b'' when nothings left
    or when new data is not yet available.

    stdout_iter = iter(non_blocking_readlines(process.stdout))

    line = next(stdout_iter)  # will be a line or b''.
    """
    import os

    from .pipe_non_blocking import (
            pipe_non_blocking_set,
            pipe_non_blocking_is_error_blocking,
            PortableBlockingIOError,
            )

    fd = f.fileno()
    pipe_non_blocking_set(fd)

    blocks = []

    while True:
        try:
            data = os.read(fd, chunk)
            if not data:
                # case were reading finishes with no trailing newline
                yield b''.join(blocks)
                blocks.clear()
        except PortableBlockingIOError as ex:
            if not pipe_non_blocking_is_error_blocking(ex):
                raise ex

            yield b''
            continue

        while True:
            n = data.find(b'\n')
            if n == -1:
                break

            yield b''.join(blocks) + data[:n + 1]
            data = data[n + 1:]
            blocks.clear()
        blocks.append(data)

(1) цей коментар вказує на те, що на Python 2 readline()не працює з трубами, що не блокують (як, наприклад, встановлено використання fcntl) - ви вважаєте, що це більше не правильно? (моя відповідь містить посилання ( fcntl), яке надає ту саму інформацію, але вона здається видаленою зараз). (2) Дивіться, як multiprocessing.connection.PipeвикористовуєтьсяSetNamedPipeHandleState
jfs

Я протестував це лише на Python3. Але бачила цю інформацію і очікувала, що вона залишається дійсною. Я також написав власний код, щоб використовувати замість рядка читання, я оновив свою відповідь, щоб включити її.
ideaman42

2

У мене є оригінальна проблема запитувача, але не хотів викликати теми. Я змішав рішення Джессі з прямим зчитуванням () з труби, і власний буфер-обробник для зчитування рядків (проте мій підпроцес - ping - завжди писав повні рядки <розмір системної сторінки). Я уникаю зайнятого очікування, лише читаючи в зареєстрованому на gobject іо годинник. Ці дні я зазвичай запускаю код у MainLoop gobject, щоб уникнути потоків.

def set_up_ping(ip, w):
# run the sub-process
# watch the resultant pipe
p = subprocess.Popen(['/bin/ping', ip], stdout=subprocess.PIPE)
# make stdout a non-blocking file
fl = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
stdout_gid = gobject.io_add_watch(p.stdout, gobject.IO_IN, w)
return stdout_gid # for shutting down

Сторож - це

def watch(f, *other):
print 'reading',f.read()
return True

І основна програма встановлює ping, а потім викликає цикл пошти gobject.

def main():
set_up_ping('192.168.1.8', watch)
# discard gid as unused here
gobject.MainLoop().run()

Будь-яка інша робота додається до зворотних викликів у gobject.


2

У сучасному Python все набагато краще.

Ось проста дитяча програма "hello.py":

#!/usr/bin/env python3

while True:
    i = input()
    if i == "quit":
        break
    print(f"hello {i}")

І програма для взаємодії з нею:

import asyncio


async def main():
    proc = await asyncio.subprocess.create_subprocess_exec(
        "./hello.py", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE
    )
    proc.stdin.write(b"bob\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"alice\n")
    print(await proc.stdout.read(1024))
    proc.stdin.write(b"quit\n")
    await proc.wait()


asyncio.run(main())

Це виводить:

b'hello bob\n'
b'hello alice\n'

Зауважте, що фактична закономірність, яка є також майже у всіх попередніх відповідях, як тут, так і у відповідних питаннях, полягає у встановленні дескриптора файлу stdout для дитини, що не блокує, а потім запитуйте його в якомусь циклі вибору. Ці дні, звичайно, цю петлю забезпечує асинціо.


1

виберіть допоможе визначити, де знаходиться наступний корисний ввід.

Однак ви майже завжди щасливіші з окремими нитками. Один робить блокування читання stdin, інший робить, де б ви не хотіли заблоковані.


11
Я вважаю, що ця відповідь не є корисною з двох причин: (a) Модуль вибору не буде працювати на трубах під Windows (як чітко зазначено надане посилання), що перешкоджає намірам ОП мати портативне рішення. (b) Асинхронні потоки не допускають синхронного діалогу між батьком і дочірнім процесом. Що робити, якщо батьківський процес хоче відправити наступну дію відповідно до наступного рядка, прочитаного від дитини ?!
ThomasH

4
select також не корисний тим, що зчитування Python блокуються навіть після вибору, оскільки він не має стандартної семантики C і не повертає часткових даних.
Гельмут Гроне

Окрема мотанка для читання з виходу дитини вирішила мою проблему, схожу на цю. Якщо вам потрібна синхронна взаємодія, я думаю, ви не можете використовувати це рішення (якщо ви не знаєте, якого результату очікувати). Я прийняв би цю відповідь
Еміліано

1

чому турбує нитка та черга? на відміну від readline (), BuckedReader.read1 () блоку не буде чекати \ r \ n, він повертає ASAP, якщо надходить який-небудь вихід.

#!/usr/bin/python
from subprocess import Popen, PIPE, STDOUT
import io

def __main__():
    try:
        p = Popen( ["ping", "-n", "3", "127.0.0.1"], stdin=PIPE, stdout=PIPE, stderr=STDOUT )
    except: print("Popen failed"); quit()
    sout = io.open(p.stdout.fileno(), 'rb', closefd=False)
    while True:
        buf = sout.read1(1024)
        if len(buf) == 0: break
        print buf,

if __name__ == '__main__':
    __main__()

Чи повернеться якнайшвидше, якщо нічого не буде? Якщо цього немає, він блокує.
Патьє Матьє

@ MathieuPagé має рацію. read1буде заблоковано, якщо перші основні блоки зчитування, що відбувається, коли труба все ще відкрита, але вхід недоступний.
Джек О'Коннор

1

У моєму випадку мені знадобився модуль реєстрації, який фіксує вихід із фонових програм та збільшує його (додаючи часові позначки, кольори тощо).

Я закінчив фонову нитку, яка робить фактичне введення / виведення. Наступний код призначений лише для POSIX-платформ. Я зняв неістотні частини.

Якщо хтось буде використовувати цього звіра на тривалий час, подумайте про управління відкритими дескрипторами. У моєму випадку це не було великою проблемою.

# -*- python -*-
import fcntl
import threading
import sys, os, errno
import subprocess

class Logger(threading.Thread):
    def __init__(self, *modules):
        threading.Thread.__init__(self)
        try:
            from select import epoll, EPOLLIN
            self.__poll = epoll()
            self.__evt = EPOLLIN
            self.__to = -1
        except:
            from select import poll, POLLIN
            print 'epoll is not available'
            self.__poll = poll()
            self.__evt = POLLIN
            self.__to = 100
        self.__fds = {}
        self.daemon = True
        self.start()

    def run(self):
        while True:
            events = self.__poll.poll(self.__to)
            for fd, ev in events:
                if (ev&self.__evt) != self.__evt:
                    continue
                try:
                    self.__fds[fd].run()
                except Exception, e:
                    print e

    def add(self, fd, log):
        assert not self.__fds.has_key(fd)
        self.__fds[fd] = log
        self.__poll.register(fd, self.__evt)

class log:
    logger = Logger()

    def __init__(self, name):
        self.__name = name
        self.__piped = False

    def fileno(self):
        if self.__piped:
            return self.write
        self.read, self.write = os.pipe()
        fl = fcntl.fcntl(self.read, fcntl.F_GETFL)
        fcntl.fcntl(self.read, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        self.fdRead = os.fdopen(self.read)
        self.logger.add(self.read, self)
        self.__piped = True
        return self.write

    def __run(self, line):
        self.chat(line, nl=False)

    def run(self):
        while True:
            try: line = self.fdRead.readline()
            except IOError, exc:
                if exc.errno == errno.EAGAIN:
                    return
                raise
            self.__run(line)

    def chat(self, line, nl=True):
        if nl: nl = '\n'
        else: nl = ''
        sys.stdout.write('[%s] %s%s' % (self.__name, line, nl))

def system(command, param=[], cwd=None, env=None, input=None, output=None):
    args = [command] + param
    p = subprocess.Popen(args, cwd=cwd, stdout=output, stderr=output, stdin=input, env=env, bufsize=0)
    p.wait()

ls = log('ls')
ls.chat('go')
system("ls", ['-l', '/'], output=ls)

date = log('date')
date.chat('go')
system("date", output=date)

1

Моя проблема дещо інша, тому що я хотів зібрати як stdout, так і stderr з запущеного процесу, але в кінцевому підсумку те саме, оскільки я хотів вивести результат у віджет як його генерований.

Я не хотів вдаватися до багатьох запропонованих обхідних шляхів, використовуючи черги або додаткові теми, оскільки вони не повинні бути необхідними для виконання такого поширеного завдання, як виконання іншого сценарію та збір його результатів.

Прочитавши запропоновані рішення та документи python, я вирішив свою проблему із реалізацією нижче. Так, він працює лише для POSIX, оскільки я використовую selectвиклик функції.

Я погоджуюся, що документи заплутані, і реалізація незручна для такого поширеного сценарію. Я вважаю, що в старих версіях python є різні за замовчуванням Popenі різні пояснення, так що це створювало багато плутанини. Це, здається, добре працює і для Python 2.7.12 та 3.5.2.

Ключовим моментом було встановити bufsize=1буферизацію рядків, а потім universal_newlines=Trueобробити як текстовий файл, а не двійковий, який, як видається, стане типовим при налаштуванні bufsize=1.

class workerThread(QThread):
   def __init__(self, cmd):
      QThread.__init__(self)
      self.cmd = cmd
      self.result = None           ## return code
      self.error = None            ## flag indicates an error
      self.errorstr = ""           ## info message about the error

   def __del__(self):
      self.wait()
      DEBUG("Thread removed")

   def run(self):
      cmd_list = self.cmd.split(" ")   
      try:
         cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None
                                        , universal_newlines=True
                                        , stderr=subprocess.PIPE
                                        , stdout=subprocess.PIPE)
      except OSError:
         self.error = 1
         self.errorstr = "Failed to execute " + self.cmd
         ERROR(self.errorstr)
      finally:
         VERBOSE("task started...")
      import select
      while True:
         try:
            r,w,x = select.select([cmd.stdout, cmd.stderr],[],[])
            if cmd.stderr in r:
               line = cmd.stderr.readline()
               if line != "":
                  line = line.strip()
                  self.emit(SIGNAL("update_error(QString)"), line)
            if cmd.stdout in r:
               line = cmd.stdout.readline()
               if line == "":
                  break
               line = line.strip()
               self.emit(SIGNAL("update_output(QString)"), line)
         except IOError:
            pass
      cmd.wait()
      self.result = cmd.returncode
      if self.result < 0:
         self.error = 1
         self.errorstr = "Task terminated by signal " + str(self.result)
         ERROR(self.errorstr)
         return
      if self.result:
         self.error = 1
         self.errorstr = "exit code " + str(self.result)
         ERROR(self.errorstr)
         return
      return

ERROR, DEBUG і VERBOSE - просто макроси, які друкують вихід на термінал.

Це рішення є ефективним на 99,99% IMHO, оскільки воно все ще використовує функцію блокування readline, тому ми припускаємо, що підпроцес є хорошим та виводить повні рядки.

Я вітаю відгуки, щоб покращити рішення, оскільки я все ще новачок у Python.


У цьому конкретному випадку ви можете встановити stderr = subprocess.STDOUT в конструкторі Popen і отримати весь вихід з cmd.stdout.readline ().
Аарон

Гарний наочний приклад. У мене виникли проблеми з select.select (), але це вирішило це для мене.
maharvey67


0

Працюючи з відповіді Дж. Ф. Себастьяна та кількох інших джерел, я склав простий менеджер підпроцесів. Він забезпечує запит, не блокуючи зчитування, а також виконуючи кілька процесів паралельно. Він не використовує жодного ОС, що дзвонить (про що я знаю) і тому повинен працювати в будь-якому місці.

Він доступний у pypi, так просто pip install shelljob. Перегляньте на сторінці проекту приклади та повні документи.


0

EDIT: Ця реалізація все ще блокується. Використовуйте замість відповіді JFSebastian .

Я спробував верхню відповідь , але додатковий ризик та підтримка потокового коду викликали занепокоєння.

Дивлячись через IO модуль (і обмежується до 2,6), я знайшов BufferedReader. Це моє рішення, що не блокує нитку.

import io
from subprocess import PIPE, Popen

p = Popen(['myprogram.exe'], stdout=PIPE)

SLEEP_DELAY = 0.001

# Create an io.BufferedReader on the file descriptor for stdout
with io.open(p.stdout.fileno(), 'rb', closefd=False) as buffer:
  while p.poll() == None:
      time.sleep(SLEEP_DELAY)
      while '\n' in bufferedStdout.peek(bufferedStdout.buffer_size):
          line = buffer.readline()
          # do stuff with the line

  # Handle any remaining output after the process has ended
  while buffer.peek():
    line = buffer.readline()
    # do stuff with the line

ви пробували for line in iter(p.stdout.readline, ""): # do stuff with the line? Він без ниток (один потік) і блокує, коли ваш код блокується.
jfs

@ jf-sebastian Так, я врешті повернувся до вашої відповіді. Моя реалізація все ще періодично блокується. Я відредагую свою відповідь, щоб попередити інших не йти цим маршрутом.
romc

0

Нещодавно я натрапив на ту ж проблему, що мені потрібно читати один рядок одночасно з потоку (запуск хвоста в підпроцесі) в режимі, що не блокує, я хотів уникнути наступних проблем: не записувати процесор, не читати потік на один байт ( як, наприклад, читав лінію) тощо

Ось моя реалізація https://gist.github.com/grubberr/5501e1a9760c3eab5e0a, вона не підтримує windows (опитування), не обробляє EOF, але це працює для мене добре


відповідь нитка на основі ніяк НЕ згоріти процесор (ви можете вказати довільне , timeoutяк у вашому рішенні) і .readline()читає більше ніж один байт в той час ( bufsize=1кошти від прямої лінії -buffered (підходить тільки для запису)). Які ще проблеми ви знайшли? Відповіді лише на посилання не дуже корисні.
jfs

0

Це приклад запуску інтерактивної команди в підпроцесі, а stdout - інтерактивний за допомогою псевдотерміналу. Ви можете звернутися до: https://stackoverflow.com/a/43012138/3555925

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import select
import termios
import tty
import pty
from subprocess import Popen

command = 'bash'
# command = 'docker run -it --rm centos /bin/bash'.split()

# save original tty setting then set it to raw mode
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())

# open pseudo-terminal to interact with subprocess
master_fd, slave_fd = pty.openpty()

# use os.setsid() make it run in a new process group, or bash job control will not be enabled
p = Popen(command,
          preexec_fn=os.setsid,
          stdin=slave_fd,
          stdout=slave_fd,
          stderr=slave_fd,
          universal_newlines=True)

while p.poll() is None:
    r, w, e = select.select([sys.stdin, master_fd], [], [])
    if sys.stdin in r:
        d = os.read(sys.stdin.fileno(), 10240)
        os.write(master_fd, d)
    elif master_fd in r:
        o = os.read(master_fd, 10240)
        if o:
            os.write(sys.stdout.fileno(), o)

# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)

0

Це рішення використовує select модуль для "зчитування будь-яких доступних даних" з потоку вводу-виводу. Ця функція спочатку блокується, поки дані не стануть доступними, але потім зчитує лише ті наявні дані та не блокується далі.

Зважаючи на той факт, що він використовує selectмодуль, це працює лише в Unix.

Код повністю сумісний з PEP8.

import select


def read_available(input_stream, max_bytes=None):
    """
    Blocks until any data is available, then all available data is then read and returned.
    This function returns an empty string when end of stream is reached.

    Args:
        input_stream: The stream to read from.
        max_bytes (int|None): The maximum number of bytes to read. This function may return fewer bytes than this.

    Returns:
        str
    """
    # Prepare local variables
    input_streams = [input_stream]
    empty_list = []
    read_buffer = ""

    # Initially block for input using 'select'
    if len(select.select(input_streams, empty_list, empty_list)[0]) > 0:

        # Poll read-readiness using 'select'
        def select_func():
            return len(select.select(input_streams, empty_list, empty_list, 0)[0]) > 0

        # Create while function based on parameters
        if max_bytes is not None:
            def while_func():
                return (len(read_buffer) < max_bytes) and select_func()
        else:
            while_func = select_func

        while True:
            # Read single byte at a time
            read_data = input_stream.read(1)
            if len(read_data) == 0:
                # End of stream
                break
            # Append byte to string buffer
            read_buffer += read_data
            # Check if more data is available
            if not while_func():
                break

    # Return read buffer
    return read_buffer

0

Я також зіткнувся з проблемою, описаною Джессі, і вирішив її, використовуючи "select", як це робили Бредлі , Енді та інші, але в режимі блокування, щоб уникнути напруженого циклу. Він використовує манекен Труби як фальшивий стдин. Виберіть блоки та зачекайте, поки stdin чи труба будуть готові. Після натискання клавіші stdin розблоковує вибір, і значення ключа можна отримати за допомогою read (1). Коли інша нитка записує в трубу, тоді труба розблокує вибір, і це може бути сприйнято як ознака того, що потреба в stdin закінчена. Ось декілька довідкових кодів:

import sys
import os
from select import select

# -------------------------------------------------------------------------    
# Set the pipe (fake stdin) to simulate a final key stroke
# which will unblock the select statement
readEnd, writeEnd = os.pipe()
readFile = os.fdopen(readEnd)
writeFile = os.fdopen(writeEnd, "w")

# -------------------------------------------------------------------------
def getKey():

    # Wait for stdin or pipe (fake stdin) to be ready
    dr,dw,de = select([sys.__stdin__, readFile], [], [])

    # If stdin is the one ready then read it and return value
    if sys.__stdin__ in dr:
        return sys.__stdin__.read(1)   # For Windows use ----> getch() from module msvcrt

    # Must finish
    else:
        return None

# -------------------------------------------------------------------------
def breakStdinRead():
    writeFile.write(' ')
    writeFile.flush()

# -------------------------------------------------------------------------
# MAIN CODE

# Get key stroke
key = getKey()

# Keyboard input
if key:
    # ... do your stuff with the key value

# Faked keystroke
else:
    # ... use of stdin finished

# -------------------------------------------------------------------------
# OTHER THREAD CODE

breakStdinRead()

ПРИМІТКА. Для того, щоб зробити цю роботу в Windows, трубу слід замінити на розетку. Я ще не пробував, але це має працювати відповідно до документації.
gonzaedu61

0

Спробуйте wexpect , який є альтернативою Windows для pexpect .

import wexpect

p = wexpect.spawn('myprogram.exe')
p.stdout.readline('.')               // regex pattern of any character
output_str = p.after()

0

На Unix-подібних системах та Python 3.5+ є те, os.set_blockingщо робить саме те, що говорить.

import os
import time
import subprocess

cmd = 'python3', '-c', 'import time; [(print(i), time.sleep(1)) for i in range(5)]'
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
os.set_blocking(p.stdout.fileno(), False)
start = time.time()
while True:
    # first iteration always produces empty byte string in non-blocking mode
    for i in range(2):    
        line = p.stdout.readline()
        print(i, line)
        time.sleep(0.5)
    if time.time() > start + 5:
        break
p.terminate()

Цей результат:

1 b''
2 b'0\n'
1 b''
2 b'1\n'
1 b''
2 b'2\n'
1 b''
2 b'3\n'
1 b''
2 b'4\n'

З os.set_blockingкоментарем:

0 b'0\n'
1 b'1\n'
0 b'2\n'
1 b'3\n'
0 b'4\n'
1 b''

-2

Ось модуль, який підтримує неблокуючі читання та фонові записи в python:

https://pypi.python.org/pypi/python-nonblock

Надає функцію,

nonblock_read, який буде читати дані з потоку, якщо вони доступні, інакше повернути порожній рядок (або None, якщо потік закритий з іншого боку і всі можливі дані були прочитані)

Ви також можете розглянути модуль python-subprocess2,

https://pypi.python.org/pypi/python-subprocess2

що додає до модуля підпроцесу. Отже, на об’єкт, повернутий з "subprocess.Popen", додається додатковий метод, runInBackground. Це запускає потік і повертає об'єкт, який автоматично заповнюватиметься, коли матеріал записується до stdout / stderr, не блокуючи основну тему.

Насолоджуйтесь!


Я хотів би спробувати це модуль неблокування , але я відносно новий у деяких процедурах Linux. Як саме я можу встановити ці підпрограми? Я запускаю Raspbian Jessie, аромат Debian Linux для Raspberry Pi. Я спробував "sudo apt-get install nonblock" і python-nonblock, і обидва кинули помилку - не знайдено. Я завантажив zip-файл з цього сайту pypi.python.org/pypi/python-nonblock , але не знаю, що з ним робити. Дякую .... RDK
RDK
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.