সাবপ্রসেস.কমুনিকেট () থেকে স্ট্রিমিং ইনপুট পড়ুন


85

আমি পাইথন ব্যবহার করছি subprocess.communicate()প্রায় এক মিনিট চলমান প্রক্রিয়া থেকে স্টাডআউট পড়তে।

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

subprocess.communicate() একবারে সমস্ত আউটপুট দিতে প্রদর্শিত হবে।


উত্তর:


46

দয়া করে নোট করুন, আমি মনে করি জেএফ সেবাস্তিয়ানের পদ্ধতিটি (নীচে) আরও ভাল।


এখানে একটি সহজ উদাহরণ রয়েছে (ত্রুটির জন্য কোনও পরীক্ষণ ছাড়াই):

import subprocess
proc = subprocess.Popen('ls',
                       shell=True,
                       stdout=subprocess.PIPE,
                       )
while proc.poll() is None:
    output = proc.stdout.readline()
    print output,

যদি lsখুব দ্রুত শেষ হয় তবে আপনি সমস্ত ডেটা পড়ার আগে লুপটি শেষ হতে পারে।

আপনি স্টাডাউটে এইভাবে বাকীটি ধরতে পারেন:

output = proc.communicate()[0]
print output,

4
এই স্কিমটি কি অজগর ডকটিকে বোঝায় বাফার ব্লকিংয়ের সমস্যায় পড়ে?
হেইনরিচ শেমটারলিং

@ হেইনরিচ, বাফার ব্লক করার সমস্যাটি আমি ভালভাবে বুঝতে পারি না is আমি বিশ্বাস করি (কেবল গুগল করে) এই সমস্যাটি তখনই ঘটে যখন আপনি স্টুডআউট (এবং স্ট্যাডার?) থেকে লুপের ভিতরে না পড়েন read সুতরাং আমি মনে করি উপরের কোডটি ঠিক আছে, তবে আমি নিশ্চিতভাবে বলতে পারি না।
unutbu

4
এটি আসলে একটি ব্লকিং সমস্যায় ভুগছে, কয়েক বছর আগে আমার সেই ঝামেলার শেষ ছিল না যেখানে রিডলাইনটি ব্লক করবে 'ততক্ষণ প্র্যাকটি শেষ হয়ে গেলেও এটি একটি নতুন লাইন পেয়েছে। আমি সমাধানটি মনে করি না, তবে আমি মনে করি এটির কোনও শ্রমিকের থ্রেডে পড়াগুলি করা এবং কেবল লুপিং while proc.poll() is None: time.sleep(0)বা সেই প্রভাবটির সাথে কিছু করার ছিল। মূলত- আপনার প্রক্রিয়াটি যে আউটপুট নিউলাইনটি শেষ কাজটি শেষ তা নিশ্চিত করতে হবে (কারণ আপনি দোভাষীকে আবার লুপ দেওয়ার জন্য সময় দিতে পারবেন না) বা আপনার কিছু করার দরকার "অভিনব"।
ড্যাশ-টম-ব্যাং

: @Heinrich: অ্যালেক্স Martelli কিভাবে এখানে অচলাবস্থা এড়াতে সম্পর্কে লিখেছেন stackoverflow.com/questions/1445627/...
unutbu

6
বাফার ব্লকিংটি কখনও কখনও শোনা যায় এমন থেকে সহজ: পিতামাতারা ব্লকগুলি সন্তানের বাইরে বেরিয়ে আসার অপেক্ষায় থাকে + বাচ্চার ব্লকগুলি যোগাযোগের পাইপে পিতামাতার পড়ার এবং কিছু জায়গা মুক্ত করার অপেক্ষায় থাকে যা পূর্ণ = অচলাবস্থা is এটা যে সহজ। পাইপ যত ছোট হবে তার সম্ভাবনা তত বেশি।
মার্চএইচ

163

সাবপ্রসেসটি তার স্টাডআউট বাফারটি ফ্লাশ করার সাথে সাথে সাব-প্রসেসের আউটপুট লাইনটি লাইনের সাথে পেতে:

#!/usr/bin/env python2
from subprocess import Popen, PIPE

p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1)
with p.stdout:
    for line in iter(p.stdout.readline, b''):
        print line,
p.wait() # wait for the subprocess to exit

iter()পাইথন 2-তে রিড-ফরোয়ার্ড বাগটি নিয়ে কাজ করার জন্য লাইনগুলি পড়ার সাথে সাথেই এটি ব্যবহৃত হয় ।

যদি সাবপ্রসেসের স্টাডাউট অ-ইন্টারেক্টিভ মোডে লাইন বাফারিংয়ের পরিবর্তে একটি ব্লক বাফারিং ব্যবহার করে (যা সন্তানের দ্বারা বাফার পূর্ণ বা স্পষ্টতভাবে প্রবাহিত না হওয়া পর্যন্ত আউটপুটে বিলম্বিত করে) তখন আপনি ব্যবহার করে একটি অসুরক্ষিত আউটপুটকে জোর করার চেষ্টা করতে পারেন pexpect, ptyমডিউল বা unbuffer, stdbuf, scriptইউটিলিটি দেখুন কেন শুধু শুধু একটি পাইপ ব্যবহার করুন (popen ()): প্রশ্ন?


পাইথন 3 কোড এখানে:

#!/usr/bin/env python3
from subprocess import Popen, PIPE

with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1,
           universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='')

দ্রষ্টব্য: পাইথন 2 এর বিপরীতে যা সাবপ্রসেসকে 'বাইটস্ট্রিংগুলি আউটপুট করে; পাইথন 3 টেক্সট মোড ব্যবহার করে (সেন্টিমিডির আউটপুট locale.getpreferredencoding(False)এনকোডিং ব্যবহার করে ডিকোড করা হয় )।


খ 'এর অর্থ কী?
হারুন

4
b''একটি হল bytesপাইথন 2.7 এবং পাইথন 3 আক্ষরিক
JFS

4
@JinghaoShi: bufsize=1একটি পার্থক্য করেন তাহলে আপনি করতে পারেন লিখতে (ব্যবহার p.stdin) subprocess যেমন, এটা সময় একটি ইন্টারেক্টিভ (করছেন একটি অচলাবস্থা এড়াতে সাহায্য করতে পারেন pexpect-একটি) বিনিময় - অভিমানী শিশু প্রক্রিয়া নিজেই কোন বাফারিং বিষয় আছে। আপনি যদি কেবলমাত্র পড়তে থাকেন তবে আমি যেমন বলেছি পার্থক্যটি কেবল পারফরম্যান্সে: যদি তা না হয় তবে আপনি এটি একটি ন্যূনতম সম্পূর্ণ কোড উদাহরণ প্রদান করতে পারেন যা এটি দেখায়?
jfs

4
@ আইলিয়ন: হ্যাঁ এর জন্য এমন কৌশলগুলি দরকার যা স্টাডআউট / স্ট্ডারার একই সাথে পড়তে পারে যদি না আপনি স্টাডারকে স্টাডআউটে (পাশ stderr=subprocess.STDOUTকরে Popen()) একীভূত করেন । থ্রেডিং বা অ্যাসিনসিও সমাধানগুলি এখানে যুক্ত রয়েছে See
jfs

4
@ এসএলস্প্যাটজ যদি stdout=PIPEআউটপুট ক্যাপচার না করে (আপনি এখনও এটি স্ক্রিনে দেখতে পান) তবে আপনার প্রোগ্রামটি স্ট্যাডারে বা তার পরিবর্তে সরাসরি টার্মিনালে মুদ্রণ করতে পারে। স্টাডআউট এবং স্ট্ডারকে মার্জ করতে, পাস করুন stderr=subprocess.STDOUT(আমার আগের মন্তব্য দেখুন)। আপনার tty এ সরাসরি মুদ্রিত আউটপুট ক্যাপচার করার জন্য, আপনি পেপস্পেক্ট, পিটিআই সলিউশন ব্যবহার করতে পারেন। । এখানে আরও জটিল কোড উদাহরণ রয়েছে
jfs

6

আমি বিশ্বাস করি যে স্ট্রিমিং ফ্যাশনে কোনও প্রক্রিয়া থেকে আউটপুট সংগ্রহ করার সহজ উপায়টি এটির মতো:

import sys
from subprocess import *
proc = Popen('ls', shell=True, stdout=PIPE)
while True:
    data = proc.stdout.readline()   # Alternatively proc.stdout.read(1024)
    if len(data) == 0:
        break
    sys.stdout.write(data)   # sys.stdout.buffer.write(data) on Python 3.x

প্রক্রিয়াটি শেষ হয়ে যাওয়ার পরে readline()বা read()ফাংশনটি কেবল ইওএফ-এ খালি স্ট্রিংটি ফেরত পাঠানো উচিত - অন্যথায় পড়ার মতো কিছু না থাকলে এটি ব্লক হয়ে যাবে ( readline()নতুন লাইনের অন্তর্ভুক্ত রয়েছে, তাই খালি লাইনে এটি ফিরে আসে "। N")। এটি communicate()লুপের পরে কোনও বিশ্রী চূড়ান্ত কলের প্রয়োজন এড়ায় ।

read()সর্বাধিক দীর্ঘ লাইনযুক্ত ফাইলগুলিতে সর্বাধিক মেমরির ব্যবহার হ্রাস করা ভাল - এটিতে পাস সংখ্যাটি নির্বিচারে হয়, তবে এটি বাদ দিলে পুরো পাইপ আউটপুট একবারে পড়ে ফলাফল হয় যা সম্ভবত কাম্য নয়।


4
data = proc.stdout.read()সমস্ত ডেটা পড়া না হওয়া অবরুদ্ধ। আপনি এটির সাথে বিভ্রান্ত os.read(fd, maxsize)হচ্ছেন যে এর আগে ফিরে আসতে পারে (কোনও ডেটা উপলভ্য হওয়ার সাথে সাথে)।
jfs

আপনি সঠিক, আমি ভুল ছিল। তবে যদি যুক্তিসঙ্গত হিসাবে যুক্তিসঙ্গত সংখ্যক বাইটগুলি পাস করা হয় read()তবে এটি সূক্ষ্মভাবে কাজ করে, এবং একইভাবে readline()সর্বাধিক লাইনের দৈর্ঘ্য যুক্তিযুক্ত হিসাবে দীর্ঘক্ষণ কাজ করে। সেই অনুসারে আমার উত্তর আপডেট করুন।
ডি কোয়েজি

3

আপনি যদি একটি অবরুদ্ধ করার পদ্ধতি চান তবে ব্যবহার করবেন না process.communicate()। আপনি সেট করেন তাহলে subprocess.Popen()যুক্তি stdoutকরার PIPE, আপনার কাছ থেকে পড়তে পারেন process.stdoutএবং চেক যদি প্রক্রিয়া এখনও ব্যবহার রান process.poll()



2

আপনি যদি রিয়েলটাইমে কেবল আউটপুটটি পাস করার চেষ্টা করছেন তবে এর চেয়ে সহজ পাওয়া শক্ত:

import subprocess

# This will raise a CalledProcessError if the program return a nonzero code.
# You can use call() instead if you don't care about that case.
subprocess.check_call(['ls', '-l'])

সাবপ্রসেস.সিচ_ক্যাল () এর জন্য দস্তাবেজগুলি দেখুন ।

আপনার যদি আউটপুট প্রক্রিয়া করতে হয় তবে অবশ্যই এটি লুপ করুন। তবে আপনি যদি না করেন তবে এটি সহজ রাখুন।

সম্পাদনা: জেএফ সেবাস্তিয়ান উভয়ই উল্লেখ করেছেন যে স্টাডাউট এবং স্টার্ডার প্যারামিটারগুলির ডিফল্টগুলি sys.stdout এবং sys.stderr এর মধ্য দিয়ে যায় এবং sys.stdout এবং sys.stderr প্রতিস্থাপন করা হয়েছে (যদি বলি যে, আউটপুট ক্যাপচার করার জন্য) পরীক্ষা)।


প্রকৃত ফাইলনো () নেই এমন ফাইল-জাতীয় বস্তুগুলির সাথে sys.stdoutবা sys.stderrপ্রতিস্থাপন করা থাকলে এটি কাজ করবে না । তাহলে sys.stdout, sys.stderrতারপর প্রতিস্থাপিত নেই এটা এমনকি সহজ হল: subprocess.check_call(args)
jfs

ধন্যবাদ! আমি sys.stdout / stderr প্রতিস্থাপনের যোজনাগুলি বুঝতে পেরেছি, তবে কোনওরকম কখনও বুঝতে পারেনি যে আপনি যদি যুক্তি বাদ দেন তবে এটি স্টাডাউট এবং স্ট্যাডারকে সঠিক জায়গায় চলে যায়। আমি চাই call()উপর check_call()যদি না আমি চাই CalledProcessError
Nate

python -mthis: "ত্রুটিগুলি কখনই নিঃশব্দে কাটানো উচিত না less এজন্য উদাহরণ কোডটিcheck_call() বেশি পছন্দ করা উচিত call()
jfs

হি। আমি প্রচুর প্রোগ্রামগুলিকে call()নন-ত্রুটিযুক্ত পরিস্থিতিতে ননজারো ত্রুটি কোডগুলি ফিরিয়ে আনছি কারণ তারা ভয়ানক। সুতরাং আমাদের ক্ষেত্রে, একটি ননজারো ত্রুটি কোডটি আসলে ত্রুটি নয়।
Nate

হ্যাঁ. এমন কোনও প্রোগ্রাম grepরয়েছে যা ত্রুটি না থাকলেও শূন্য-বহির্গমন স্থিতি ফিরে আসতে পারে - সেগুলি ব্যতিক্রম। ডিফল্ট হিসাবে শূন্য প্রস্থান স্থিতি সাফল্য নির্দেশ করে।
jfs

1
myCommand="ls -l"
cmd=myCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r     equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:    
    print(p.stderr.readline().rstrip('\r\n'))

4
আপনার সমাধানটি কীভাবে মানুষকে আরও ভালভাবে বোঝাতে পারে তা ব্যাখ্যা করা সর্বদা ভাল
ড্যাফোস

4
shlex.split(myCommand)পরিবর্তে আপনি ব্যবহার বিবেচনা করা উচিত myCommand.split()। এটি উদ্ধৃত যুক্তিগুলিতে স্থানগুলিও সম্মান করে।
উটাহ জারহেড

0

কয়েকটি ছোট পরিবর্তন সহ আরও একটি পাইথন 3 সমাধান যুক্ত করা হচ্ছে:

  1. আপনাকে শেল প্রক্রিয়াটির প্রস্থান কোড ধরার অনুমতি দেয় ( withকনস্ট্রাক্টটি ব্যবহার করার সময় আমি প্রস্থান কোডটি পেতে পারিনি )
  2. পাইপ স্টাডার আউট রিয়েল টাইমে
import subprocess
import sys
def subcall_stream(cmd, fail_on_error=True):
    # Run a shell command, streaming output to STDOUT in real time
    # Expects a list style command, e.g. `["docker", "pull", "ubuntu"]`
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True)
    for line in p.stdout:
        sys.stdout.write(line)
    p.wait()
    exit_code = p.returncode
    if exit_code != 0 and fail_on_error:
        raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`")
    return(exit_code)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.