python যবহ অর্ডার সংরক্ষণের সময় পাইথন সাবপ্রসেস স্টাডাউট এবং স্টডার থেকে পৃথকভাবে পড়ে read




পাইথন প্রোগ্রামিং বই pdf download (6)

আমি জানি এই প্রশ্নটি অনেক পুরানো, তবে এই উত্তরটি অন্যদের যারা এই পৃষ্ঠাটির উপর একই রকম পরিস্থিতির সমাধান সমাধানে হোঁচট খেতে সহায়তা করতে পারে, তাই আমি যাইহোক পোস্ট করছি।

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

এটি প্রতিটি পাইপগুলির জন্য একটি থ্রেড তৈরি করে, সেগুলি প্রতিটি লাইনে পড়ে এবং একটি সারিতে ফেলে দেয় (যা ফিফো হয়) is মূল থ্রেডটি প্রতিটি লাইনে ফলন করে সারির মধ্য দিয়ে যায়।

import threading, queue
def merge_pipes(**named_pipes):
    r'''
    Merges multiple pipes from subprocess.Popen (maybe other sources as well).
    The keyword argument keys will be used in the output to identify the source
    of the line.

    Example:
    p = subprocess.Popen(['some', 'call'],
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    outputs = {'out': log.info, 'err': log.warn}
    for name, line in merge_pipes(out=p.stdout, err=p.stderr):
        outputs[name](line)

    This will output stdout to the info logger, and stderr to the warning logger
    '''

    # Constants. Could also be placed outside of the method. I just put them here
    # so the method is fully self-contained
    PIPE_OPENED=1
    PIPE_OUTPUT=2
    PIPE_CLOSED=3

    # Create a queue where the pipes will be read into
    output = queue.Queue()

    # This method is the run body for the threads that are instatiated below
    # This could be easily rewritten to be outside of the merge_pipes method,
    # but to make it fully self-contained I put it here
    def pipe_reader(name, pipe):
        r"""
        reads a single pipe into the queue
        """
        output.put( ( PIPE_OPENED, name, ) )
        try:
            for line in iter(pipe.readline,''):
                output.put( ( PIPE_OUTPUT, name, line.rstrip(), ) )
        finally:
            output.put( ( PIPE_CLOSED, name, ) )

    # Start a reader for each pipe
    for name, pipe in named_pipes.items():
        t=threading.Thread(target=pipe_reader, args=(name, pipe, ))
        t.daemon = True
        t.start()

    # Use a counter to determine how many pipes are left open.
    # If all are closed, we can return
    pipe_count = 0

    # Read the queue in order, blocking if there's no data
    for data in iter(output.get,''):
        code=data[0]
        if code == PIPE_OPENED:
            pipe_count += 1
        elif code == PIPE_CLOSED:
            pipe_count -= 1
        elif code == PIPE_OUTPUT:
            yield data[1:]
        if pipe_count == 0:
            return

আমার কাছে পাইথন সাব প্রসেস রয়েছে যা থেকে আমি আউটপুট এবং ত্রুটি প্রবাহগুলি পড়ার চেষ্টা করছি। বর্তমানে আমার এটি কাজ করছে তবে stderr থেকে পড়া শেষ করার পরে আমি কেবল stderr থেকে পড়তে সক্ষম। এটি দেখতে কেমন দেখাচ্ছে তা এখানে:

process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_iterator = iter(process.stdout.readline, b"")
stderr_iterator = iter(process.stderr.readline, b"")

for line in stdout_iterator:
    # Do stuff with line
    print line

for line in stderr_iterator:
    # Do stuff with line
    print line

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

স্পষ্ট করার জন্য: আমার এখনও বলার দরকার আছে যে stdout বা stderr থেকে কোনও লাইন এসেছে কিনা কারণ সেগুলি আমার কোডে অন্যরকম আচরণ করা হবে।


আপনার প্রক্রিয়া কোডটি অচল অবস্থায় থাকতে পারে যদি শিশু প্রক্রিয়াটি স্ট্ডার (আমার লিনাক্স মেশিনে ~ 100KB) পর্যাপ্ত আউটপুট উত্পাদন করে।

একটি communicate() পদ্ধতি রয়েছে যা স্টাডআউট এবং স্ট্ডার উভয় থেকে পৃথকভাবে পড়তে দেয়:

from subprocess import Popen, PIPE

process = Popen(command, stdout=PIPE, stderr=PIPE)
output, err = process.communicate()

আপনার যদি শিশু প্রক্রিয়াটি চলমান অবস্থায় স্ট্রিমগুলি পড়ার প্রয়োজন হয় তবে পোর্টেবল সমাধান হ'ল থ্রেডগুলি ব্যবহার করা (পরীক্ষিত নয়):

from subprocess import Popen, PIPE
from threading import Thread
from Queue import Queue # Python 2

def reader(pipe, queue):
    try:
        with pipe:
            for line in iter(pipe.readline, b''):
                queue.put((pipe, line))
    finally:
        queue.put(None)

process = Popen(command, stdout=PIPE, stderr=PIPE, bufsize=1)
q = Queue()
Thread(target=reader, args=[process.stdout, q]).start()
Thread(target=reader, args=[process.stderr, q]).start()
for _ in range(2):
    for source, line in iter(q.get, None):
        print "%s: %s" % (source, line),

দেখুন:

  • পাইথন: সাবপ্রসেস.কমিউনেট থেকে স্ট্রিমিং ইনপুট পড়ুন ()
  • পাইথন-এ সাব-প্রসেসে পাইপ করুন P
  • পাইথন সাবপ্রসেস বাচ্চাদের আউটপুট ফাইল এবং টার্মিনালে পাবেন?

এটি আমার জন্য কাজ করে (উইন্ডোজে): https://github.com/waszil/subpiper

from subpiper import subpiper

def my_stdout_callback(line: str):
    print(f'STDOUT: {line}')

def my_stderr_callback(line: str):
    print(f'STDERR: {line}')

my_additional_path_list = [r'c:\important_location']

retcode = subpiper(cmd='echo magic',
                   stdout_callback=my_stdout_callback,
                   stderr_callback=my_stderr_callback,
                   add_path_list=my_additional_path_list)

একটি প্রক্রিয়া বিভিন্ন পাইপগুলিতে ডেটা লেখার ক্রমটি লেখার পরে হারিয়ে যায়।

Stdr এর আগে stdout লেখা হয়েছে কিনা তা আপনি বলতে পারবেন না।

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

এই প্রোগ্রামটি এটি প্রদর্শন করা উচিত:

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

import os
import select
import subprocess

testapps={
    'slow': '''
import os
import time
os.write(1, 'aaa')
time.sleep(0.01)
os.write(2, 'bbb')
time.sleep(0.01)
os.write(1, 'ccc')
''',
    'fast': '''
import os
os.write(1, 'aaa')
os.write(2, 'bbb')
os.write(1, 'ccc')
''',
    'fast2': '''
import os
os.write(1, 'aaa')
os.write(2, 'bbbbbbbbbbbbbbb')
os.write(1, 'ccc')
'''
}

def readfds(fds, maxread):
    while True:
        fdsin, _, _ = select.select(fds,[],[])
        for fd in fdsin:
            s = os.read(fd, maxread)
            if len(s) == 0:
                fds.remove(fd)
                continue
            yield fd, s
        if fds == []:
            break

def readfromapp(app, rounds=10, maxread=1024):
    f=open('testapp.py', 'w')
    f.write(testapps[app])
    f.close()

    results={}
    for i in range(0, rounds):
        p = subprocess.Popen(['python', 'testapp.py'], stdout=subprocess.PIPE
                                                     , stderr=subprocess.PIPE)
        data=''
        for (fd, s) in readfds([p.stdout.fileno(), p.stderr.fileno()], maxread):
            data = data + s
        results[data] = results[data] + 1 if data in results else 1

    print 'running %i rounds %s with maxread=%i' % (rounds, app, maxread)
    results = sorted(results.items(), key=lambda (k,v): k, reverse=False)
    for data, count in results:
        print '%03i x %s' % (count, data)


print
print "=> if output is produced slowly this should work as whished"
print "   and should return: aaabbbccc"
readfromapp('slow',  rounds=100, maxread=1024)

print
print "=> now mostly aaacccbbb is returnd, not as it should be"
readfromapp('fast',  rounds=100, maxread=1024)

print
print "=> you could try to read data one by one, and return"
print "   e.g. a whole line only when LF is read"
print "   (b's should be finished before c's)"
readfromapp('fast',  rounds=100, maxread=1)

print
print "=> but even this won't work ..."
readfromapp('fast2', rounds=100, maxread=1)

এবং এরকম কিছু আউটপুট দেয়:

=> if output is produced slowly this should work as whished
   and should return: aaabbbccc
running 100 rounds slow with maxread=1024
100 x aaabbbccc

=> now mostly aaacccbbb is returnd, not as it should be
running 100 rounds fast with maxread=1024
006 x aaabbbccc
094 x aaacccbbb

=> you could try to read data one by one, and return
   e.g. a whole line only when LF is read
   (b's should be finished before c's)
running 100 rounds fast with maxread=1
003 x aaabbbccc
003 x aababcbcc
094 x abababccc

=> but even this won't work ...
running 100 rounds fast2 with maxread=1
003 x aaabbbbbbbbbbbbbbbccc
001 x aaacbcbcbbbbbbbbbbbbb
008 x aababcbcbcbbbbbbbbbbb
088 x abababcbcbcbbbbbbbbbb

পাইথনের ডক অনুযায়ী

Popen.stdout stdout আর্গুমেন্ট যদি পাইপ হয় তবে এই বৈশিষ্ট্যটি একটি ফাইল অবজেক্ট যা শিশু প্রক্রিয়া থেকে আউটপুট সরবরাহ করে। অন্যথায়, এটি কিছুই নয়।

Popen.stderr যদি stderr যুক্তিটি পাইপ হয় তবে এই বৈশিষ্ট্যটি এমন একটি ফাইল অবজেক্ট যা শিশু প্রক্রিয়া থেকে ত্রুটি আউটপুট সরবরাহ করে। অন্যথায়, এটি কিছুই নয়।

নীচের নমুনা আপনি যা করতে পারেন তা করতে পারে

test.py

print "I'm stdout"

raise Exception("I'm Error")

printer.py

import subprocess

p = subprocess.Popen(['python', 'test.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

print "Normal"
std_lines = p.stdout.readlines()
for line in std_lines:
    print line.rstrip()

print "Error"
stderr_lines = p.stderr.readlines()
for line in stderr_lines:
    print line.rstrip()

আউটপুট:

Normal
I'm stdout

Error
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    raise Exception("I'm Error")
Exception: I'm Error

এখানে selectors উপর ভিত্তি করে একটি সমাধান রয়েছে, তবে ক্রম সংরক্ষণ করে এমন একটি এবং ভেরিয়েবল-দৈর্ঘ্যের অক্ষর (এমনকি একক অক্ষর) প্রবাহিত করে।

কৌশলটি হ'ল read1() পরিবর্তে read()

import selectors
import subprocess
import sys

p = subprocess.Popen(
    "python random_out.py", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

sel = selectors.DefaultSelector()
sel.register(p.stdout, selectors.EVENT_READ)
sel.register(p.stderr, selectors.EVENT_READ)

while True:
    for key, _ in sel.select():
        data = key.fileobj.read1().decode()
        if not data:
            exit()
        if key.fileobj is p.stdout:
            print(data, end="")
        else:
            print(data, end="", file=sys.stderr)

আপনি যদি কোনও পরীক্ষা প্রোগ্রাম চান তবে এটি ব্যবহার করুন।

import sys
from time import sleep


for i in range(10):
    print(f" x{i} ", file=sys.stderr, end="")
    sleep(0.1)
    print(f" y{i} ", end="")
    sleep(0.1)




stderr