সাব-প্রসেস স্টডআউট লাইন লাইন পড়ুন


235

আমার পাইথন স্ক্রিপ্টটি একটি লিনাক্স ইউটিলিটি কল করতে সাবপ্রসেস ব্যবহার করে যা খুব গোলমাল। আমি সমস্ত আউটপুট একটি লগ ফাইলে সংরক্ষণ করতে এবং এটির কিছু ব্যবহারকারীর কাছে প্রদর্শন করতে চাই। আমি ভেবেছিলাম নিম্নলিখিতটি কাজ করবে তবে ইউটিলিটি উল্লেখযোগ্য পরিমাণে আউটপুট তৈরি না করা পর্যন্ত আউটপুটটি আমার অ্যাপ্লিকেশনটিতে প্রদর্শিত হবে না।

#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
   print hex(i)*512
   i += 1
   time.sleep(0.5)

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
   #the real code does filtering here
   print "test:", line.rstrip()

আমি প্রকৃতপক্ষে যে আচরণটি করতে চাই তা হ'ল ফিল্টার স্ক্রিপ্টটি প্রতিটি লাইনটি প্রিন্ট করে যেমন এটি সাবপ্রসেস থেকে প্রাপ্ত হয়। বাছাইয়ের মতো teeপাইথন কোড সহ যা করে।

আমি কী মিস করছি? এটা কি সম্ভব?


হালনাগাদ:

যদি sys.stdout.flush()নকল_ইউটিলিটি.পি-তে যুক্ত করা হয়, কোডটি পাইথন ৩.১ এ পছন্দসই আচরণ করে। আমি পাইথন ২.6 ব্যবহার করছি। আপনি ভাববেন যে proc.stdout.xreadlines()ব্যবহারটি পাই 3 কে হিসাবে একই কাজ করবে, তবে তা হয় না।


আপডেট 2:

এখানে সর্বনিম্ন কার্যকারী কোড।

#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
   print i
   sys.stdout.flush()
   time.sleep(0.5)

#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
   print line.rstrip()

4
আপনি এর print line,পরিবর্তে ব্যবহার করতে পারেন print line.rstrip()(দ্রষ্টব্য: শেষে কমা)।
jfs


2
আপডেট 2 উল্লেখ করেছে যে এটি অজগর 3.0+ এর সাথে কাজ করে তবে এটি পুরানো মুদ্রণ বিবৃতি ব্যবহার করে, তাই এটি অজগর 3.0+ এর সাথে কাজ করে না।
রকি 21

এখানে তালিকাভুক্ত উত্তরগুলির জন্য আমার পক্ষে কাজ করা হয়নি , তবে স্ট্যাকওভারফ্লো . com/ প্রশ্নগুলি / ৫৪১১80০০/২ করেন!
boxed

উত্তর:


179

পাইথনের সাথে সর্বশেষে কাজ করার পরে অনেক দিন হয়েছে, তবে আমি মনে করি যে সমস্যাটি বিবৃতিটির সাথে রয়েছে for line in proc.stdout, যা এটির পুনরাবৃত্তি করার আগে পুরো ইনপুটটি পড়ে। readline()পরিবর্তে সমাধানটি হ'ল :

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
while True:
  line = proc.stdout.readline()
  if not line:
    break
  #the real code does filtering here
  print "test:", line.rstrip()

অবশ্যই আপনাকে এখনও সাব-প্রসেস 'বাফারিংয়ের সাথে ডিল করতে হবে।

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


11
জন্য file.readline()বনাম for line in fileদেখতে bugs.python.org/issue3907 (সংক্ষেপে: এটা Python3 উপর কাজ করে; ব্যবহার io.open()পাইথন উপর 2.6+)
JFS

5
কোনও ইওএফ-এর জন্য আরও পাইথোনিক পরীক্ষা, পিইপি 8 ( পাইথন.অর্গ / দেবদেবী / পেপস / পেপ-0008 ) এর "প্রোগ্রামিং প্রস্তাবনাগুলি" অনুসারে , 'যদি লাইন না থাকে:' হবে।
জেসন মক

14
@naxa: পাইপ জন্য: for line in iter(proc.stdout.readline, ''):
jfs

3
@ জান-ফিলিপ জিহরস্কে: হ্যাঁ। 1. আপনি for line in proc.stdoutপাইথন 3 এ ব্যবহার করতে পারেন (কোনও রিড-ফরোড বাগ নেই) 2. '' != b''পাইথন 3 এ - কোড অন্ধভাবে কপি-পেস্ট করবেন না - ভাবুন এটি কী করে এবং এটি কীভাবে কাজ করে।
jfs

2
@ জেএসএবেস্টিয়ান: নিশ্চিত, iter(f.readline, b'')সমাধানটি বরং সুস্পষ্ট (এবং যদি কেউ আগ্রহী তবে পাইথন ২-তেও কাজ করে)। আমার মন্তব্যের মূল বক্তব্যটি আপনার সমাধানটিকে দোষ দেওয়ার জন্য ছিল না (দুঃখিত, যদি এটির মতো উপস্থিত হয় তবে আমি এখন এটিও পড়েছি!), তবে লক্ষণগুলির মাত্রাটি বর্ণনা করার জন্য, যা এই ক্ষেত্রে বেশ গুরুতর (পিআই 2 / বেশিরভাগ) 3 টি সমস্যার ব্যতিক্রম হয়, যেখানে এখানে একটি ভাল-আচরণের লুপটি অন্তহীন হিসাবে পরিবর্তিত হয় এবং আবর্জনা সংগ্রহের ফলে সদ্য নির্মিত বস্তুর বন্যার বিরুদ্ধে লড়াই করা লড়াই করে, দীর্ঘকাল এবং বৃহত্তর প্রশস্ততা সহ স্মৃতি ব্যবহারের দোলনা দেয়)।
ডাঃ জানু-ফিলিপ গেহর্কেকে

45

পার্টিতে দেরি হয়ে গিয়েছিল, তবে আমি এখানে সবচেয়ে সহজ সমাধান বলে মনে করি তা না দেখে অবাক হয়েছিলেন:

import io
import subprocess

proc = subprocess.Popen(["prog", "arg"], stdout=subprocess.PIPE)
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):  # or another encoding
    # do something with line

(এটি পাইথন 3 প্রয়োজন।)


25
আমি এই উত্তরটি ব্যবহার করতে চাই তবে আমি পাচ্ছি: AttributeError: 'file' object has no attribute 'readable' পাই 2.7
ড্যান


স্পষ্টত এই কোড একাধিক কারণে বৈধ নয় py3 / py3 সামঞ্জস্য এবং পেয়ে ValueError প্রকৃত ঝুঁকি: আমি / বন্ধ ফাইলে হে অপারেশন
sorin

3
@ সোরিন এই দুটি জিনিসই এটিকে "বৈধ নয়" করে তোলে। যদি আপনি এমন একটি লাইব্রেরি লিখছেন যা এখনও পাইথন 2 সমর্থন করা প্রয়োজন, তবে এই কোডটি ব্যবহার করবেন না। তবে এক দশকেরও বেশি আগে প্রকাশিত সফটওয়্যারটি ব্যবহার করতে পেরে অনেকের বিলাসিতা রয়েছে। আপনি যদি কোনও বন্ধ ফাইলটিতে পড়ার চেষ্টা করেন তবে আপনি ব্যবহার করবেন TextIOWrapperবা না করুক না কেন আপনি এই ব্যতিক্রমটি পাবেন । আপনি কেবল ব্যতিক্রমটি পরিচালনা করতে পারেন।
jbg

1
আপনি হয়ত পার্টিতে দেরীতে হয়ে
গেছেন

20

প্রকৃতপক্ষে, আপনি যদি পুনরুক্তিটি বাছাই করে থাকেন তবে এখন বাফারিং আপনার সমস্যা হতে পারে। আপনি উপ-প্রক্রিয়ায় অজগরটিকে বলতে পারেন যে এর আউটপুটটি বাফার করবেন না।

proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)

হয়ে

proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)

পাইথনের ভেতর থেকে পাইথন কল করার সময় আমার এটি দরকার ছিল।


14

আপনি এই অতিরিক্ত পরামিতিগুলি এখানে পাস করতে চান subprocess.Popen:

bufsize=1, universal_newlines=True

তারপরে আপনি নিজের উদাহরণ হিসাবে পুনরাবৃত্তি করতে পারেন। (পাইথন 3.5 এর সাথে পরীক্ষিত)


2
@nicoulaj সাবপ্রসেস 32 প্যাকেজটি ব্যবহার করা হলে এটি কাজ করা উচিত।
কোয়ান্টাম 7

4

এমন একটি ফাংশন যা রিয়েলটাইম, উভয় stdoutএবং stderrএকই সাথে উভয়কেই পুনরুক্ত করার অনুমতি দেয়

আপনার উভয়ের জন্য stdoutএবং stderrএকই সময়ে আউটপুট স্ট্রিম পাওয়ার প্রয়োজন হলে আপনি নিম্নলিখিত ফাংশনটি ব্যবহার করতে পারেন।

ফাংশনটি উভয় পোপেন পাইপকে একক পুনরাবৃত্তিতে মার্জ করার জন্য ক্যু ব্যবহার করে।

এখানে আমরা ফাংশন তৈরি read_popen_pipes():

from queue import Queue, Empty
from concurrent.futures import ThreadPoolExecutor


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


def read_popen_pipes(p):

    with ThreadPoolExecutor(2) as pool:
        q_stdout, q_stderr = Queue(), Queue()

        pool.submit(enqueue_output, p.stdout, q_stdout)
        pool.submit(enqueue_output, p.stderr, q_stderr)

        while True:

            if p.poll() is not None and q_stdout.empty() and q_stderr.empty():
                break

            out_line = err_line = ''

            try:
                out_line = q_stdout.get_nowait()
            except Empty:
                pass
            try:
                err_line = q_stderr.get_nowait()
            except Empty:
                pass

            yield (out_line, err_line)

read_popen_pipes() ব্যাবহৃত হচ্ছে:

import subprocess as sp


with sp.Popen(my_cmd, stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:

    for out_line, err_line in read_popen_pipes(p):

        # Do stuff with each line, e.g.:
        print(out_line, end='')
        print(err_line, end='')

    return p.poll() # return status-code

2

আপনি লাইন ডাব্লু / ও লুপও পড়তে পারেন। পাইথন 3.6 এ কাজ করে।

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
list_of_byte_strings = process.stdout.readlines()

1
অথবা স্ট্রিংয়ে রূপান্তর করতে:list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
ndtreviv

1

আমি পাইথন 3 দিয়ে এটি চেষ্টা করেছি এবং এটি কাজ করেছে, উত্স

def output_reader(proc):
    for line in iter(proc.stdout.readline, b''):
        print('got line: {0}'.format(line.decode('utf-8')), end='')


def main():
    proc = subprocess.Popen(['python', 'fake_utility.py'],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)

    t = threading.Thread(target=output_reader, args=(proc,))
    t.start()

    try:
        time.sleep(0.2)
        import time
        i = 0

        while True:
        print (hex(i)*512)
        i += 1
        time.sleep(0.5)
    finally:
        proc.terminate()
        try:
            proc.wait(timeout=0.2)
            print('== subprocess exited with rc =', proc.returncode)
        except subprocess.TimeoutExpired:
            print('subprocess did not terminate in time')
    t.join()

1

রামুলোর উত্তরের নিম্নলিখিত পরিবর্তনটি পাইথন 2 এবং 3 (2.7.12 এবং 3.6.1) এ আমার জন্য কাজ করে:

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
while True:
  line = process.stdout.readline()
  if line != '':
    os.write(1, line)
  else:
    break

0

ডান্নো যখন এটি সাবপ্রসেস মডিউলটিতে যুক্ত করা হয়েছে তবে পাইথন 3 এর সাথে আপনার ব্যবহারটি ভাল হওয়া উচিত proc.stdout.splitlines():

for line in proc.stdout.splitlines():
   print "stdout:", line
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.