পাইথন-এ সাব-প্রসেসে পাইপ করুন P


506

আমি একটি সাবপ্রসেস শুরু করতে এবং এটির আউটপুট স্ট্রিমের সাথে সংযোগ করতে সাবপ্রসেস মডিউলটি ব্যবহার করছি d আমি এর স্টডআউটে অ-ব্লকিং রিডগুলি কার্যকর করতে সক্ষম হতে চাই। রাইডলাইন অ-ব্লক করার কোনও উপায় আছে বা আমি প্রার্থনা করার আগে এই স্ট্রিমটিতে ডেটা আছে কিনা তা পরীক্ষা করে দেখুন.readline ? আমি এটি পোর্টেবল হতে চাই বা কমপক্ষে উইন্ডোজ এবং লিনাক্সের অধীনে কাজ করি।

আমি এখনই এটি কীভাবে করব তা এখানে রয়েছে ( .readlineকোনও ডেটা উপলভ্য না হলে এটি ব্লক করে দেওয়া হয় ):

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

14
(গুগল থেকে আসছেন?) পিআইপিই'র কোনও একটি বাফার পূরণ না হয়ে এবং পড়তে না পারলে সমস্ত পাইপগুলি অচল হয়ে যাবে। উদাহরণস্বরূপ stdrr পূরণ করা হয় যখন stdout অচল। আপনি যে পাইপটি চান না তা কখনই পাস করবেন না।
নাসের আল-ওহাইবি

@ নাসেরআল-ওহাইবি এর অর্থ কি তখন সবসময় ফাইল তৈরি করা ভাল?
চার্লি পার্কার

আমি যে বিষয়টি বুঝতে আগ্রহী ছিলাম তা হ'ল এটি কেন প্রথম স্থানে অবরুদ্ধ হচ্ছে ... আমি জিজ্ঞাসা করছি কারণ আমি মন্তব্যটি দেখেছি:To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
চার্লি পার্কার

এটি, "ডিজাইন দ্বারা", ইনপুটগুলি পাওয়ার জন্য অপেক্ষা করছে।
ম্যাথিউ প্যাগ

সম্পর্কিত: stackoverflow.com/q/19880190/240515
user240515

উত্তর:


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
হ্যাঁ এটি আমার পক্ষে কাজ করে, যদিও আমি অনেক সরিয়েছি। এটি ভাল অনুশীলন অন্তর্ভুক্ত তবে সর্বদা প্রয়োজনীয় নয়। পাইথন 3.x 2. এক্স কমপ্যাট এবং ক্লোজ_এফডিএস বাদ দেওয়া যেতে পারে, এটি এখনও কাজ করবে। তবে সবকিছু কী করে তা সম্পর্কে সচেতন থাকুন এবং এটি ঠিক কাজ করলেও অন্ধভাবে অনুলিপি করবেন না! (আসলে সহজ সমাধান হ'ল একটি থ্রেড ব্যবহার করা এবং সেডের মতো একটি রিডলাইন করা, কিউইসগুলি ডেটা পাওয়ার সহজ উপায়, অন্যগুলি রয়েছে, থ্রেডগুলির উত্তর রয়েছে!)
আকি

3
থ্রেডের অভ্যন্তরে, থ্রেডটিতে কলটি out.readlineএবং মূল থ্রেডটি ব্লক করে, এবং সমস্ত কিছু চালিয়ে যাওয়ার আগে আমাকে পঠনরেখা ফিরে না আসা পর্যন্ত অপেক্ষা করতে হবে। কোন সহজ উপায় কাছাকাছি? (আমি আমার প্রক্রিয়া থেকে একাধিক লাইন পড়ছি, এটি ডিবি এবং জিনিসগুলি করছে এমন অন্য একটি .পি ফাইল)
জাস্টিন

3
@ জাস্টিন: 'আউটড্রেডলাইন' এটি অন্য থ্রেডে নির্বাহ করা মূল থ্রেডটিকে অবরুদ্ধ করে না।
jfs

4
আমি যদি সাব-প্রসেস বন্ধ করতে ব্যর্থ হই, যেমন। ব্যতিক্রম কারণে? স্টাডাউট-রিডার থ্রেডটি মারা যাবে না এবং পাইথন ঝুলবে, মূল থ্রেডটি বেরিয়ে গেলেও তাই না? কিভাবে এই চারপাশে কাজ করতে পারে? পাইথন ২.x থ্রেডগুলি হত্যার পক্ষে সমর্থন করে না, সবচেয়ে খারাপ এটি তাদের বাধাকে সমর্থন করে না। :( (অবশ্যই সাব-প্রসেসটি বন্ধ হয়ে যাওয়ার নিশ্চয়তা দেওয়ার জন্য ব্যতিক্রমগুলি পরিচালনা করা উচিত তবে এটি যদি না হয় তবে আপনি কী করতে পারেন?)
n611x007

3
আমি এর কিছু বন্ধুত্বপূর্ণ shelljob মোড়ক
এডিএ-কি মর্ট-ওরা-ই

77

আমার প্রায়শই একইরকম সমস্যা হয়েছে; পাইথন প্রোগ্রামগুলি আমি প্রায়শই লিখি কিছু কমান্ড প্রাথমিক কার্যকারিতা সম্পাদন করার ক্ষমতা থাকতে হবে যখন একই সাথে কমান্ড লাইন (স্টাডিন) থেকে ব্যবহারকারীদের ইনপুট গ্রহণ করে। কেবলমাত্র অন্য ইনপুট হ্যান্ডলিং কার্যকারিতা অন্য থ্রেডে রাখলে সমস্যাটি সমাধান হয় না কারণ readline()ব্লকস এবং কোনও সময়সীমা নেই। যদি প্রাথমিক কার্যকারিতাটি সম্পূর্ণ হয়ে থাকে এবং পরবর্তী ব্যবহারকারী ইনপুটটির জন্য আর অপেক্ষা করার দরকার নেই আমি সাধারণত আমার প্রোগ্রামটি প্রস্থান করতে চাই, তবে এটি করতে পারে না কারণ readline()এখনও অন্য থ্রেডে একটি লাইনের জন্য অপেক্ষা করা অবরুদ্ধ। এই সমস্যার যে সমাধানটি আমি পেয়েছি তা হ'ল 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)

আমার মতে এই সমস্যাটি সমাধান করতে সিলেক্ট বা সিগন্যাল মডিউলগুলি ব্যবহার করার চেয়ে এটি কিছুটা পরিষ্কার er তবে এটি আবার ইউনিক্সে কেবল কাজ করে ...


1
দস্তাবেজ অনুসারে, fcntl () হয় ফাইল বর্ণনাকারী, বা একটি ফাইল যা .fileno () পদ্ধতিতে প্রাপ্ত হতে পারে।
ডেনিলসন সা মিয়া

10
জেসির উত্তর সঠিক নয়। গাইডো মতে, readline অ ব্লক মোড সঙ্গে সঠিকভাবে কাজ করে না, এবং এটা করবে না পাইথন 3000. আগে bugs.python.org/issue1175#msg56041 আপনি fcntl ব্যবহার করতে অ ব্লক মোডে ফাইল সেট করতে চান তাহলে, আপনাকে নিম্ন-স্তরের os.read () ব্যবহার করতে হবে এবং লাইনগুলি নিজেই আলাদা করতে হবে। লাইন বাফারিং সম্পাদনকারী উচ্চ-স্তরের কলগুলির সাথে fcntl মেশানো সমস্যার জন্য জিজ্ঞাসা করছে।
অ্যান্ন

2
Readline ব্যবহার বলে মনে হয় পাইথন 2. anonnn দেখি উত্তরে ভুল stackoverflow.com/questions/375427/...
Catalin Iacob

10
দয়া করে ব্যস্ত লুপগুলি ব্যবহার করবেন না। ডেটা অপেক্ষা করার জন্য একটি সময়সীমা সহ পোল () ব্যবহার করুন।
আইভো দানহেলকা

@ স্টেফানো কি buffer_sizeহিসাবে সংজ্ঞায়িত হয়েছে?
বিড়াল

39

পাইথন 3.4 অ্যাসিক্রোনাস আইও - মডিউলটির জন্য নতুন অস্থায়ী এপিআই প্রবর্তন করেasyncio

এই পন্থাটি twisted@ ব্রায়ান ওয়ার্ডের ভিত্তিক উত্তরের অনুরূপ - একটি প্রোটোকল সংজ্ঞায়িত করুন এবং ডেটা প্রস্তুত হওয়ার সাথে সাথে এর পদ্ধতিগুলি বলা হয়:

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

ডক্সে "সাবপ্রসেস" দেখুন cess

একটা উচ্চ পর্যায়ের ইন্টারফেস asyncio.create_subprocess_exec()যে আয় Processবস্তু যে একটি লাইন asynchroniosly ব্যবহার পড়তে দেয় তাই StreamReader.readline()coroutine (সঙ্গে async/ awaitপাইথন 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() নিম্নলিখিত কাজ সম্পাদন:

  • সাবপ্রসেস শুরু করুন, তার স্টাডাউটটিকে একটি পাইপে পুনর্নির্দেশ করুন
  • উপ-প্রসেসের স্ট্যান্ডআউট থেকে একটি লাইন অ্যাসিঙ্ক্রোনাসলি পড়ুন
  • উপপ্রসেস হত্যা
  • এটি প্রস্থান করার জন্য অপেক্ষা করুন

প্রতিটি পদক্ষেপ প্রয়োজনে সময়সীমা সেকেন্ড দ্বারা সীমাবদ্ধ হতে পারে।


পাইথন ৩.৪ কর্টিন ব্যবহার করে যখন আমি এরকম কিছু চেষ্টা করি তখন পুরো স্ক্রিপ্টটি চালানোর পরে আমি কেবল আউটপুট পাই। সাব-প্রসেস একটি লাইন প্রিন্ট করার সাথে সাথেই আমি আউটপুট প্রিন্ট করা একটি লাইন দেখতে চাই। আমি যা পেয়েছি তা এখানে: পেস্টবিন . com / কিপিএসএসএফজিপ
flutefreak7

1
@ ফ্লুটফ্রেইক:: বাফারিং বিষয়গুলি বর্তমান প্রশ্নের সাথে সম্পর্কিত নয়। সম্ভাব্য সমাধানের জন্য লিঙ্কটি অনুসরণ করুন।
jfs

ধন্যবাদ! কেবলমাত্র ব্যবহার করে আমার স্ক্রিপ্টের জন্য সমস্যাটি সমাধান করুন print(text, flush=True)যাতে মুদ্রিত পাঠ্যটি তাত্ক্ষণিকভাবে নজরদারি কলিংয়ের জন্য পাওয়া যায় readline। আমি যখন এটি ফোর্টরান ভিত্তিক এক্সিকিউটেবলের সাথে পরীক্ষা করে দেখি আমি আসলে মোড়ানো / ঘড়িতে চাই, এটি আউটপুটটি বাফার করে না, সুতরাং এটি প্রত্যাশার সাথে আচরণ করে।
flutefreak7

সাব-প্রসেসটিকে আরও পড়া / লেখার ক্রিয়া চালিয়ে যাওয়ার এবং সঞ্চালনের অনুমতি দেওয়া কি সম্ভব? readline_and_kill, আপনার দ্বিতীয় স্ক্রিপ্টে, subprocess.comunicateএটির মতো কাজ করে যা এটি পড়ার / লেখার ক্রিয়াকলাপের পরে প্রক্রিয়াটি সমাপ্ত করে। আমি আরও দেখতে পাচ্ছি যে আপনি একটি একক পাইপ ব্যবহার করছেন stdout, যা সাব-প্রসেস অ-ব্লকিং হিসাবে পরিচালনা করে। উভয়ই ব্যবহার করার চেষ্টা করছি stdoutএবং stderr আমি দেখতে পেয়েছি যে আমি অবরুদ্ধ
ক্যারেল

@ ক্যারেল উত্তরের কোডটি উত্তর হিসাবে বর্ণিত বর্ণনার মতো কাজ করে। ইচ্ছা করলে অন্যান্য আচরণ বাস্তবায়ন করা সম্ভব। উভয় পাইপ ব্যবহার করা হলে সমান নন-ব্লকিং, এখানে উভয় পাইপগুলি একই সাথে কীভাবে পড়তে হবে তার একটি উদাহরণ এখানে ।
jfs

19

অ্যাসিঙ্কপ্রোক মডিউল চেষ্টা করে দেখুন । উদাহরণ স্বরূপ:

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
একদম অসাধারণ. কাঁচা সাবপ্রসেস মডিউলটির চেয়ে অনেক বেশি সহজ। উবুন্টুতে আমার জন্য পুরোপুরি কাজ করে।
Cerin

12
asyncproc Windows এ কাজ করে না, এবং জানালা os.WNOHANG :-( সমর্থন করে না
ব্রায়ান Oakley,

26
অ্যাসিঙ্কপ্রোক হ'ল জিপিএল, যা এর ব্যবহার আরও সীমাবদ্ধ করে :-(
ব্রায়ান ওকলে

ধন্যবাদ। একটি ছোট জিনিস: মনে হচ্ছে asyncproc.py এ 8 টি স্পেসের সাথে ট্যাবগুলি প্রতিস্থাপন করার উপায় হল :)
বেনজমিং

দেখে মনে হচ্ছে এটি অ্যাসিঙ্কপ্রোক মডিউলের মাধ্যমে আপনি যে প্রক্রিয়াটি চালু করেছিলেন তার রিটার্ন কোডটি পেতে পারেন; এটি তৈরি করে কেবল আউটপুট।
গ্রেইয়াই

17

আপনি ট্যুইস্টে খুব সহজেই এটি করতে পারেন । আপনার বিদ্যমান কোড বেসের উপর নির্ভর করে, এটি ব্যবহার করা এত সহজ নাও হতে পারে তবে আপনি যদি কোনও বাঁকানো অ্যাপ্লিকেশন তৈরি করে থাকেন তবে এর মতো জিনিসগুলি প্রায় তুচ্ছ become আপনি একটি 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()

দ্য পাক ডকুমেন্টেশন এই কিছু ভাল তথ্য আছে।

যদি আপনি আপনার পুরো অ্যাপ্লিকেশনটি ট্যুইস্টেডের আশেপাশে তৈরি করেন তবে এটি স্থানীয় বা দূরবর্তী অন্যান্য প্রক্রিয়াগুলির সাথে অ্যাসিনক্রোনাস যোগাযোগকে সত্যই মার্জিত করে তোলে। অন্যদিকে, যদি আপনার প্রোগ্রামটি ট্যুইস্টের শীর্ষে তৈরি না হয় তবে এটি সত্যিই সহায়ক হবে না। আশা করি এটি অন্যান্য পাঠকদের পক্ষে সহায়ক হতে পারে, যদিও এটি আপনার নির্দিষ্ট প্রয়োগের জন্য প্রযোজ্য নয়।


ভাল না. দস্তাবেজselect অনুসারে ফাইল বর্ণনাকারী উইন্ডোতে কাজ করা উচিত নয়
n611x007

2
@ নেক্সা আমি মনে করি না যে select()তিনি উল্লেখ করছেন আপনি একই রকম আছেন। আমি এটি ধরে নিচ্ছি কারণ Twistedউইন্ডোজে কাজ করে ...
notbad.jpeg


1
"টুইস্টেড (ব্যবহৃত রিঅ্যাক্টরের উপর নির্ভর করে) সাধারণত একটি বড় নির্বাচন () লুপ হয়" এর মধ্যে বেছে নিতে বেশ কয়েকটি চুল্লি রয়েছে। select()এক unixes এবং UNIX-লাইক সবচেয়ে পোর্টেবল এক, কিন্তু এছাড়াও আছে দুই চুল্লি Windows এর জন্য পাওয়া যায়: twistedmatrix.com/documents/current/core/howto/...
clacke

14

নির্বাচন করুন এবং পড়ুন (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))

পঠনের জন্য () - যেমন:

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 অনুসারে ফাইল বর্ণনাকারী উইন্ডোতে কাজ করা উচিত নয়
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/

যাইহোক, স্ট্যাডআউটটি আসার সাথে সাথে কী পড়তে হবে? আর একটি সমাধান হতে পারে কোনও ফাইলের আউটপুট ডাম্প করা এবং 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()

মনে হচ্ছে রিসিটির থ্রেড টাইমআউট হওয়ার পরে বেরিয়ে আসবে না এবং এটি হত্যার উপর নির্ভর করে সাবপ্রসেসটি মেরে ফেলতে পারার উপর নির্ভর করে (এসএইচ। অন্যথায় এই বিষয়ে সম্পর্কিত নয়) এটি পড়ছে (এমন একটি জিনিস যা আপনি সক্ষম হবেন তবে কেবল আপনি না করতে পারলে ..) ।
n611x007

7

দাবি অস্বীকার: এটি কেবল টর্নেডোর জন্য কাজ করে

আপনি এফডিটিকে ননব্লকিং হিসাবে সেট করে এটি করতে পারবেন এবং তারপরে কলব্যাকগুলি নিবন্ধিত করতে আইলোপ ব্যবহার করুন। আমি এটি একটি টর্নেডো_সুবপ্রসেস নামে একটি ডিমের মধ্যে প্যাকেজ করেছি এবং আপনি এটি পিপিআই এর মাধ্যমে ইনস্টল করতে পারেন:

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

আপনি এটি একটি অনুরোধহ্যান্ডলার দিয়েও ব্যবহার করতে পারেন

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টর্নেডো ওয়েবসকেট উদাহরণে ব্যবহার করেছি এবং এটি কাজটি ভাল করেছে।
ভিজিওএন

1
থ্রেডিং বেশিরভাগ টর্নেডোতে নিরুৎসাহিত করা হয়। তারা ছোট, স্বল্প চলমান ফাংশনগুলির জন্য ভাল। আপনি এখানে এটি সম্পর্কে পড়তে পারেন: stackoverflow.com/questions/7846323/tornado-web-and-threads github.com/facebook/tornado/wiki/Threading-and-concurrency
Vukasin Toroman

@ ভুকাসিনটোরোমেন আপনি সত্যিই আমাকে এখানে এটি দিয়ে সংরক্ষণ করেছেন। টর্নেডো_সুবপ্রসেস মডিউলটির জন্য আপনাকে অনেক ধন্যবাদ :)
জেমস জেনেটস

এটি কি উইন্ডোতে কাজ করে? (নোট করুন select, ফাইল বর্ণনাকারী সহ, এটি করেন না )
n611x007

এই lib selectকলটি ব্যবহার করে না । আমি উইন্ডোজ এর অধীনে এটি চেষ্টা করি নি তবে আপনি সম্ভবত সমস্যার মধ্যে পড়বেন যেহেতু লিবটি fcntlমডিউলটি ব্যবহার করছে । সংক্ষেপে: কোনটি সম্ভবত উইন্ডোজ এর অধীনে কাজ করবে না।
ভুকাসিন তরোমন

6

বিদ্যমান সমাধানগুলি আমার জন্য কার্যকর হয়নি (নীচে বিশদ)। পরিশেষে যা কাজ করেছে তা হ'ল রিডলাইন প্রয়োগ করে (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. সলিউশনগুলির জন্য যা পাঠ্যরেখার প্রয়োজন (কুইউ ভিত্তিকগুলি সহ) সর্বদা অবরুদ্ধ। রিডলাইন কার্যকর করে এমন থ্রেডটি হত্যা করা কঠিন (অসম্ভব?)? এটি কেবল তখনই মারা যায় যখন এটি তৈরি করা প্রক্রিয়াটি শেষ হয়, তবে আউটপুট উত্পাদনের প্রক্রিয়াটি মারা গেলে তা নয়।
  2. উচ্চ স্তরের রিডলাইন কলগুলির সাথে নিম্ন-স্তরের fcntl মিশ্রিত করা অ্যান্ন উল্লেখ করেছে বলে সঠিকভাবে কাজ করতে পারে না।
  3. সিলেক্ট.পল () ব্যবহার করা ঝরঝরে, তবে পাইথন ডক্স অনুসারে উইন্ডোজটিতে কাজ করে না।
  4. তৃতীয় পক্ষের গ্রন্থাগারগুলি ব্যবহার করা এই কাজের জন্য ওভারকিল বলে মনে হচ্ছে এবং অতিরিক্ত নির্ভরতা যুক্ত করে।

1
১. q.get_nowait()আমার উত্তর থেকে অবশ্যই এটি ব্লক করা উচিত নয়, এটি এটি ব্যবহারের মূল বিষয়। ২. রিডলাইন ( enqueue_output()ফাংশন ) সম্পাদনকারী থ্রেডটি ইওএফ , যেমন আউটপুট উত্পাদনের প্রক্রিয়াটি মারা যাওয়ার পরে মামলা সহ প্রস্থান করে। আপনি যদি বিশ্বাস করেন যে এটি না হয়; দয়া করে একটি সম্পূর্ণ ন্যূনতম কোড উদাহরণ সরবরাহ করুন যা অন্যথায় (সম্ভবত একটি নতুন প্রশ্ন হিসাবে ) দেখায় ।
jfs

1
@ সেবাস্তিয়ান আমি একটি ঘন্টা বা তারও বেশি সময় ব্যয় করেছি যে একটি ন্যূনতম উদাহরণ নিয়ে আসতে পারি। শেষ পর্যন্ত আমাকে অবশ্যই সম্মত করতে হবে যে আপনার উত্তরটি সমস্ত ক্ষেত্রে পরিচালনা করে। আমার ধারণা এটি আমার পক্ষে আগে কার্যকর হয়নি কারণ যখন আমি আউটপুট-উত্পাদন প্রক্রিয়াটি হত্যার চেষ্টা করছিলাম তখন এটি ইতিমধ্যে নিহত হয়েছিল এবং একটি হার্ড-টু-ডিবাগ ত্রুটি দিয়েছে। ঘন্টাটি ভালভাবেই কাটিয়েছিল, কারণ একটি ন্যূনতম উদাহরণ সহকারে আসার সময় আমি একটি সহজ সমাধান নিয়ে আসতে পারি।
বিক্রম পুডি

আপনি কি সহজ সমাধান পোস্ট করতে পারেন? :) (এটি যদি সেবাস্তিয়ানদের থেকে আলাদা হয়)
n611x007

@ danger89: আমার মনে হয় dcmpid = myprocess
ViFI

পড়ার পরে শর্তে () কল করার পরে (ঠিক ঠিক পরে): আউট কখনই ফাঁকা স্ট্রিং হবে না কারণ আপনি কমপক্ষে স্ট্রিং / বাইটগুলি 1 এর দৈর্ঘ্যের সাথে
পড়েছেন

6

আংশিক লাইন সহ এএসএপি সাবপ্রসেস থেকে প্রতিটি আউটপুট ধরতে ব্যবহৃত আমার কোড এখানে। এটি একই সময়ে পাম্প করে এবং প্রায় সঠিক ক্রমে স্টাডআউট এবং স্টডারার।

পাইথন ২.7 লিনাক্স এবং উইন্ডোতে পরীক্ষিত এবং সঠিকভাবে কাজ করেছে।

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

আমি কিছু সাবপ্রসেস পড়তে এই সমস্যাটি যুক্ত করছি out এখানে আমার নন-ব্লকিং পড়ার সমাধানটি দেওয়া হয়েছে:

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

msvcrt.kbhit()পরিবর্তে @ এনাটোলিটেটোনিক ব্যবহার করুন
বিড়াল

4

অ-ব্লকিং পঠনের এই সংস্করণটির জন্য বিশেষ মডিউলগুলির প্রয়োজন নেই এবং বেশিরভাগ লিনাক্স ডিস্ট্রোজে বাক্সের বাইরে কাজ করবে।

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

থ্রেডের উপর ভিত্তি করে এখানে একটি সহজ সমাধান যা:

  • লিনাক্স এবং উইন্ডোজ উভয় ক্ষেত্রেই কাজ করে (নির্ভর করে না) select )।
  • উভয় stdoutএবংstderr asynchronouly।
  • স্বেচ্ছাসেবী নির্বাচনের উপর নির্বিচারে ওয়েটিং টাইম (সিপিইউ বান্ধব) সহ নির্ভর করে না।
  • ব্যবহার করে না 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)

reader.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

উইন্ডোজ এবং ইউনিক্সে নন-ব্লকিং পাইপ সেট করার ক্ষমতা সরবরাহ করা হওয়ায় এই উত্তরটি এখানে যুক্ত করা হচ্ছে।

সমস্ত ctypesবিবরণটি @ টেকটোনিকের উত্তরের জন্য ধন্যবাদ

ইউনিক্স এবং উইন্ডোজ উভয় সিস্টেমে ব্যবহার করার জন্য কিছুটা পরিবর্তিত সংস্করণ রয়েছে।

  • পাইথন 3 সামঞ্জস্যপূর্ণ (কেবলমাত্র সামান্য পরিবর্তন প্রয়োজন)
  • পজিক্স সংস্করণ অন্তর্ভুক্ত করে এবং কোনওটির জন্য ব্যতিক্রম সংজ্ঞায়িত করে।

আপনি ইউনিক্স এবং উইন্ডোজ কোডের জন্য একই ফাংশন এবং ব্যতিক্রম ব্যবহার করতে পারেন।

# 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) এই মন্তব্যটি ইঙ্গিত দেয় যে পাইথন 2 এ readline()অ-ব্লকিং পাইপগুলি (যেমন সেট ব্যবহার করে fcntl) দিয়ে কাজ করে না - আপনি কি মনে করেন যে এটি আর সঠিক নয়? (আমার উত্তরটিতে লিঙ্কটি রয়েছে ( fcntl) যা একই তথ্য সরবরাহ করে তবে মনে হয় এটি এখন মুছে ফেলা হয়েছে))। (২) কীভাবে multiprocessing.connection.Pipeব্যবহারগুলি দেখুনSetNamedPipeHandleState
jfs

আমি কেবল পাইথন 3 এ এটি পরীক্ষা করেছি। তবে এই তথ্যটিও দেখেছেন এবং এটি বৈধ থাকার আশা করছেন। পঠনের জায়গার অভ্যন্তরে ব্যবহার করার জন্য আমি আমার নিজস্ব কোডও লিখেছি, আমি আমার উত্তরটি এতে অন্তর্ভুক্ত করার জন্য আপডেট করেছি।
ধারণাগুলি 42

2

আমার আসল প্রশ্নকারীর সমস্যা আছে, তবে থ্রেডগুলি চালিত করতে চান নি। আমি জেসির সমাধানটি পাইপ থেকে সরাসরি পঠন () এর সাথে মিশ্রিত করেছি এবং লাইন রিডের জন্য আমার নিজের বাফার-হ্যান্ডলারটি (তবে, আমার উপ-প্রক্রিয়া - পিং - সর্বদা সম্পূর্ণ লাইনগুলি <একটি সিস্টেম পৃষ্ঠার আকার) লিখেছিল। আমি কেবলমাত্র গবজেক্ট-নিবন্ধিত io ঘড়িতে পড়ে ব্যস্ত-অপেক্ষাকে এড়াতে পারি। আজকাল আমি থ্রেডগুলি এড়ানোর জন্য সাধারণত কোনও গবজেক্ট মেইনলুপের মধ্যে কোড চালনা করি।

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

এবং প্রধান প্রোগ্রামটি একটি পিং সেট আপ করে এবং তারপরে গবজেক্ট মেইল ​​লুপকে কল করে।

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

গবজেক্টে কলব্যাকের সাথে অন্য কোনও কাজ সংযুক্ত রয়েছে।


2

আধুনিক পাইথনে বিষয়গুলি অনেক বেশি ভাল।

এখানে একটি সহজ শিশু প্রোগ্রাম, "হ্যালো.পি":

#!/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'

নোট করুন যে আসল প্যাটার্নটি, যা পূর্ববর্তী উত্তরগুলির প্রায় সমস্তই এখানে এবং সম্পর্কিত উভয় প্রশ্নের মধ্যে রয়েছে, হ'ল সন্তানের স্টডআউট ফাইল বর্ণনাকারীকে অ-ব্লক করাতে সেট করা এবং তারপরে এটি কোনও ধরণের নির্বাচিত লুপে পোল করা। আজকাল অবশ্যই লুপটি অ্যাসিনসিও সরবরাহ করেছে।


1

নির্বাচন মডিউল আপনাকে নির্ধারণ যেখানে পরবর্তী দরকারী ইনপুট সাহায্য করে।

তবে, আপনি পৃথক থ্রেড সহ প্রায় সবসময় সুখী হন। একজন স্টিডিনকে একটি ব্লক করে, অন্যটি যেখানেই আপনি যেখানে অবরুদ্ধ চান তা করেন না।


11
আমি মনে করি এই উত্তরটি দুটি কারণে অকার্যকর: (ক) নির্বাচিত মডিউলটি উইন্ডোজের অধীনে থাকা পাইপগুলিতে কাজ করবে না (সরবরাহিত লিঙ্কটি পরিষ্কারভাবে উল্লেখ করেছে), যা বহনযোগ্য সমাধানের জন্য ওপি'র উদ্দেশ্যকে পরাস্ত করে। (খ) অ্যাসিঙ্ক্রোনাস থ্রেডগুলি পিতা-মাতার এবং সন্তানের প্রক্রিয়ার মধ্যে একটি সুসংগত সংলাপের অনুমতি দেয় না। যদি পিতামাতার প্রক্রিয়াটি সন্তানের কাছ থেকে পড়ার পরের লাইন অনুসারে পরবর্তী ক্রিয়া প্রেরণ করতে চায় ?!
টমাসএইচ

4
বাছাইয়ের পরে পাইথনের পঠনগুলি ব্লক হয়ে যাবে বলেও সিলেক্টে কার্যকর নয় কারণ এটিতে স্ট্যান্ডার্ড সি শব্দার্থবিজ্ঞান নেই এবং আংশিক ডেটা ফেরত দেবে না।
হেলমুট গ্রোহনে

সন্তানের আউটপুট থেকে পড়ার জন্য একটি পৃথক থ্র্রেসড আমার সমস্যার সমাধান করে যা এর সাথে একই ছিল। আপনার যদি সিঙ্ক্রোনাস ইন্টারঅ্যাকশন দরকার হয় তবে আমার ধারণা আপনি এই সমাধানটি ব্যবহার করতে পারবেন না (যদি না জানেন তবে কী আউটপুট আশা করবেন)। আমি এই উত্তরটি গ্রহণ করতাম
এমিলিয়ানো

1

কেন বিরক্তিকর থ্রেড এবং সারি? রিডলাইন () এর বিপরীতে, BufferedReader.read1 () block r \ n এর জন্য অপেক্ষা করবে না block

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

যদি কিছু আসছে না তবে এটি ASAP ফিরিয়ে দেবে? এটি না হলে এটি অবরুদ্ধ হয়।
ম্যাথিউ প্যাগ

@ ম্যাথিউপাগে ঠিক আছে। read1প্রথম অন্তর্নিহিত পঠিত ব্লকগুলি ব্লক করবে, যা পাইপটি এখনও খোলা থাকে তবে কোনও ইনপুট উপলব্ধ না থাকে।
জ্যাক ও'কনোর

1

আমার ক্ষেত্রে আমার একটি লগিং মডিউল দরকার যা ব্যাকগ্রাউন্ড অ্যাপ্লিকেশনগুলি থেকে আউটপুট ধরে এবং এটি বৃদ্ধি করে (টাইম-স্ট্যাম্প, রঙ ইত্যাদি যোগ করে)।

আমি একটি পটভূমি থ্রেড দিয়ে শেষ করেছি যা আসল I / O করে। নিম্নলিখিত কোডটি কেবল পসিক্স প্ল্যাটফর্মের জন্য। আমি অপ্রয়োজনীয় অংশ ছিনিয়ে নিয়েছি।

যদি কেউ এই প্রাণীটি দীর্ঘ সময়ের জন্য ব্যবহার করতে থাকে তবে উন্মুক্ত বর্ণনাকারীদের পরিচালনার কথা বিবেচনা করুন। আমার ক্ষেত্রে এটি কোনও বড় সমস্যা ছিল না।

# -*- 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 উভয়ই সংগ্রহ করতে চেয়েছিলাম, তবে শেষ পর্যন্ত একই কারণ যেহেতু আমি উইজেটে আউটপুটটিকে উত্পন্ন হিসাবে রেন্ডার করতে চেয়েছিলাম।

আমি ক্যুইস বা অতিরিক্ত থ্রেড ব্যবহার করে প্রস্তাবিত অনেকগুলি কাজের অবলম্বন করতে চাইনি কারণ তাদের অন্য স্ক্রিপ্ট চালানো এবং এর আউটপুট সংগ্রহ করার মতো সাধারণ কাজ সম্পাদনের প্রয়োজন হবে না।

প্রস্তাবিত সমাধান এবং অজগর ডক্স পড়ার পরে আমি নীচের বাস্তবায়ন দিয়ে আমার সমস্যাটি সমাধান করেছি। হ্যাঁ এটি কেবল পসিক্সের জন্য কাজ করে কারণ আমি selectফাংশন কলটি ব্যবহার করছি ।

আমি সম্মত হই যে ডকগুলি বিভ্রান্ত করছে এবং বাস্তবায়ন যেমন সাধারণ স্ক্রিপ্টিং কাজের জন্য বিশ্রী হয় k আমি বিশ্বাস করি যে পাইথনের পুরানো সংস্করণগুলির জন্য আলাদা ডিফল্ট Popenএবং আলাদা আলাদা ব্যাখ্যা রয়েছে যাতে এটি প্রচুর বিভ্রান্তি তৈরি করে। এটি পাইথন ২.7.১২ এবং ৩.৩.২ উভয়ের জন্যই ভাল কাজ করে বলে মনে হচ্ছে।

কীটি 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% কার্যকর কারণ এটি এখনও অবরুদ্ধকরণ readlineফাংশনটি ব্যবহার করে , তাই আমরা ধরে নিই সাব প্রক্রিয়াটি দুর্দান্ত এবং সম্পূর্ণ লাইনগুলি আউটপুট করে।

আমি পাইথনে এখনও নতুন থাকায় সমাধানের উন্নতি করতে আমি প্রতিক্রিয়া জানাই।


এই বিশেষ ক্ষেত্রে, আপনি পোপেন কনস্ট্রাক্টরে stderr = subprocess.STDOUT সেট করতে পারেন এবং cmd.stdout.readline () থেকে সমস্ত আউটপুট পেতে পারেন।
হারুন

সুন্দর স্পষ্ট উদাহরণ। সিলেক্ট (সিলেক্ট) () দিয়ে সমস্যা ছিল কিন্তু এটি আমার জন্য সমাধান করেছে।
maharvey67


0

জেএফ সেবাস্তিয়ান এর উত্তর এবং অন্যান্য বেশ কয়েকটি উত্স থেকে কাজ করে আমি একটি সহজ সাবপ্রসেসি ম্যানেজারকে একসাথে রেখেছি। এটি অনুরোধটি অ-ব্লক করা পঠন সরবরাহ করে পাশাপাশি সমান্তরালে বেশ কয়েকটি প্রক্রিয়া চালায়। এটি কোনও ওএস-নির্দিষ্ট কল ব্যবহার করে না (যা আমি সচেতন) এবং সুতরাং যে কোনও জায়গায় কাজ করা উচিত।

এটি পাইপি থেকে পাওয়া যায়, তাই ঠিক pip install shelljob। পড়ুন প্রকল্প পাতা উদাহরণ এবং সম্পূর্ণ দস্তাবেজের জন্য।


0

সম্পাদনা: এই বাস্তবায়ন এখনও অবরুদ্ধ। পরিবর্তে 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? এটি থ্রেডলেস (একক থ্রেড) এবং যখন আপনার কোডগুলি ব্লক করে blocks
jfs

@ জেএফ-সেবাস্তিয়ান হ্যাঁ, আমি শেষ পর্যন্ত আপনার উত্তরে ফিরে এসেছি। আমার বাস্তবায়ন এখনও মাঝে মাঝে অবরুদ্ধ। অন্যদের এই পথে নামতে না দেওয়ার সতর্ক করতে আমি আমার উত্তর সম্পাদনা করব।
romc

0

আমি সম্প্রতি একই সমস্যার উপর হোঁচট খেয়েছি আমাকে অ-ব্লকিং মোডে স্ট্রিম থেকে একটি সময় লাইন (সাবপ্রসেসে লেজ চালানো) পড়তে হবে আমি পরবর্তী সমস্যাগুলি এড়াতে চেয়েছিলাম: সিপিইউ পোড়াতে নয়, এক বাইট দ্বারা স্ট্রিমটি পড়বেন না ( রিডলাইন যেমন করেছে) ইত্যাদি

এখানে আমার বাস্তবায়ন https://gist.github.com/grubberr/5501e1a9760c3eab5e0a এটি উইন্ডোজ (পোল) সমর্থন করে না, ইওএফ হ্যান্ডেল করবে না, তবে এটি আমার পক্ষে ভালভাবে কাজ করে


থ্রেড ভিত্তিক উত্তর নেই না cpu 'র বার্ন (আপনি নির্বিচারে নির্দিষ্ট করতে পারেন timeoutআপনার সমাধান হিসেবে) এবং .readline()সার্চ আরও একটি সময়ে একাধিক বাইট ( bufsize=1মানে লাইন -buffered (শুধুমাত্র লেখার জন্য প্রাসঙ্গিক))। আপনি কি অন্যান্য সমস্যা খুঁজে পেয়েছেন? কেবল লিঙ্কযুক্ত উত্তরগুলি খুব কার্যকর নয়।
jfs

0

এটি সাবপ্রসেসে ইন্টারেক্টিভ কমান্ড চালনার উদাহরণ, এবং সিডিওডো টার্মিনাল ব্যবহার করে ইন্টারেক্টিভ inte আপনি এখানে উল্লেখ করতে পারেন: 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মডিউলটি ব্যবহার করে এমনটি দেওয়া, এটি কেবল ইউনিক্সে কাজ করে।

কোডটি সম্পূর্ণরূপে 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

আমি জেসির বর্ণিত সমস্যারও মুখোমুখি হয়েছি এবং ব্র্যাডলি , অ্যান্ডি এবং অন্যরা "সিলেক্ট" ব্যবহার করে এটি সমাধান করেছি তবে ব্যস্ত লুপ এড়াতে একটি ব্লকিং মোডে রেখেছি। এটি একটি নকল স্টিডিন হিসাবে একটি ডামি পাইপ ব্যবহার করে। ব্লকগুলি নির্বাচন করুন এবং স্টিডিন বা পাইপ প্রস্তুত হওয়ার জন্য অপেক্ষা করুন। যখন একটি কী চাপলে স্টিন স্ট্যান্ড নির্বাচনকে অবরোধ মুক্ত করে এবং কী মানটি পঠন (1) দিয়ে পুনরুদ্ধার করা যায়। যখন কোনও ভিন্ন থ্রেড পাইপে লিখতে থাকে তখন পাইপটি নির্বাচনগুলি অবরোধমুক্ত করে এবং স্ট্যান্ডিনের প্রয়োজনীয়তা শেষ হয়ে যাওয়ার ইঙ্গিত হিসাবে গ্রহণ করা যেতে পারে। এখানে কিছু রেফারেন্স কোড দেওয়া হয়েছে:

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

দ্রষ্টব্য: উইন্ডোজে এই কাজটি করার জন্য পাইপটি সকেট দ্বারা প্রতিস্থাপন করা উচিত। আমি এটি এখনও চেষ্টা করি নি তবে এটি ডকুমেন্টেশন অনুযায়ী কাজ করা উচিত।
gonzaedu61

0

ব্যবহার করে দেখুন wexpect , উইন্ডোজ এর বিকল্প নেই যা pexpect

import wexpect

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

0

ইউনিক্সের মতো সিস্টেম এবং পাইথন 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

এখানে একটি মডিউল রয়েছে যা অ-অবরুদ্ধ পাঠক এবং ব্যাকগ্রাউন্ডটি পাইথনকে সমর্থন করে:

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

একটি ফাংশন সরবরাহ করে,

ননব্লক_ড্রেড যা প্রবাহ থেকে ডেটা পড়বে, যদি পাওয়া যায় তবে অন্যথায় একটি খালি স্ট্রিং ফিরিয়ে দেয় (বা প্রবাহটি অন্যদিকে বন্ধ থাকলে এবং সমস্ত সম্ভাব্য ডেটা পড়া হয়ে গেছে)

আপনি পাইথন-সাবপ্রসেস 2 মডিউলটিও বিবেচনা করতে পারেন,

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

যা সাবপ্রসেস মডিউল যোগ করে। সুতরাং "সাবপ্রসেস.পোপেন" থেকে প্রত্যাবর্তিত অবজেক্টটিতে একটি অতিরিক্ত পদ্ধতি যোগ করা হয়েছে, রানইনব্যাকগ্রাউন্ড। এটি একটি থ্রেড শুরু করে এবং এমন কোনও বস্তু দেয় যা আপনার মূল থ্রেডটি ব্লক না করে stdout / stderr এ স্টাফ লেখার সাথে সাথে স্বয়ংক্রিয়ভাবে পপুলেটে যাবে।

উপভোগ করুন!


আমি এটি চেষ্টা করে দেখতে চাই ননব্লক মডিউলটি তবে লিনাক্সের কয়েকটি পদ্ধতিতে আমি তুলনামূলকভাবে নতুন। ঠিক কীভাবে আমি এই রুটিনগুলি ইনস্টল করব? আমি রাস্পবিয়ান জেসি চালাচ্ছি, রাস্পবেরি পাইয়ের জন্য ডেবিয়ান লিনাক্সের গন্ধ। আমি 'সুডো এপটি-গেট ইনস্টল ননব্লক' এবং পাইথন-ননব্লক চেষ্টা করেছিলাম এবং দুজনেই একটি ত্রুটি ফেলেছিলাম - পাওয়া যায় নি। আমি এই সাইটটি থেকে জিপ ফাইলটি ডাউনলোড করেছি পিপাই.পাইথন.আর / পিপিআইপিপিথনঅনলব্যাক , তবে কী করতে হবে তা জানি না। ধন্যবাদ .... আরডিকে
আরডি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.