সাবপ্রসেস ব্যবহার করে রিয়েলটাইম আউটপুট পাওয়া


135

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

আমি অনুভব করেছি যে আমি কেবল প্রোগ্রামটি ব্যবহার করে ব্যবহার করব subprocess.Popen, ব্যবহার করব stdout=PIPE, তারপরে প্রতিটি লাইনটি আসার সাথে সাথে পড়তে হবে এবং সে অনুযায়ী কাজ করব। যাইহোক, যখন আমি নিম্নলিখিত কোডটি চালিয়েছি, আউটপুটটি কোথাও বাফার হয়ে দেখা দিয়েছে, যার ফলে এটি দুটি খণ্ডে দেখা যাচ্ছে, লাইন 1 থেকে 332 পর্যন্ত, তারপরে 333 থেকে 439 (আউটপুটের শেষ লাইন)

from subprocess import Popen, PIPE, STDOUT

p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE, 
        stderr = STDOUT, shell = True)
for line in p.stdout:
    print line.replace('\n', '')

সাবপ্রসেসে ডকুমেন্টেশনটি কিছুটা দেখার পরে আমি bufsizeপ্যারামিটারটি আবিষ্কার করেছিলাম Popen, তাই আমি বুফসাইজটি 1 (প্রতিটি লাইন বাফার) এবং 0 (কোনও বাফার) এ সেট করার চেষ্টা করেছি, তবে কোনও মানই লাইনগুলি সরবরাহ করার পদ্ধতি পরিবর্তন করার মতো মনে হয়নি।

এই মুহুর্তে আমি স্ট্রগুলি আঁকতে শুরু করেছি, সুতরাং আমি নিম্নলিখিত আউটপুট লুপটি লিখেছিলাম:

while True:
    try:
        print p.stdout.next().replace('\n', '')
    except StopIteration:
        break

তবে একই ফল পেয়েছে।

সাবপ্রসেস ব্যবহার করে কোন প্রোগ্রামের 'রিয়েলটাইম' প্রোগ্রাম আউটপুট পাওয়া সম্ভব? পাইথনে আরও কিছু অপশন রয়েছে যা ফরোয়ার্ড-সামঞ্জস্যপূর্ণ (নয় exec*)?


1
sydout=PIPEপ্যারেন্ট প্রসেসটি বাইপাস করে সাব-প্রসেসগুলি সরাসরি আপনার কনসোলটিতে লিখেছেন তাই আপনি কী বাদ দেওয়ার চেষ্টা করেছেন ?
এসলট

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

তাহলে কেন একটি "রিয়েল-টাইম" প্রদর্শন? আমি ব্যবহারের মামলা পাই না।
এস্লট লট

8
শেল = সত্য ব্যবহার করবেন না। এটি অযথা আপনার শেলটি ডাকে। এর পরিবর্তে পি = পোপেন (['স্নানাডমিন', 'যাচাই করুন', '/ ভার / এসএনএন / রেপোস / কনফিগারেশন'],
স্টডআউট

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

উত্তর:


82

আমি এটি চেষ্টা করেছি এবং কোনও কারণে কোডটি পেয়েছি

for line in p.stdout:
  ...

আক্রমণাত্মকভাবে বাফারগুলি, বৈকল্পিক

while True:
  line = p.stdout.readline()
  if not line: break
  ...

না. স্পষ্টতই এটি একটি পরিচিত বাগ: http://bugs.python.org/issue3907 (29 ই আগস্ট, 2018 তারিখে ইস্যুটি এখন "বন্ধ")


পুরানো পাইথন আইও বাস্তবায়নের ক্ষেত্রে এটি কেবল গোলযোগ নয়। এই কারণেই পাই 2.6 এবং পাই 3 কে সম্পূর্ণ নতুন আইও লাইব্রেরি দিয়ে শেষ হয়েছে।
টিম লিন

3
সাব-প্রসেসটি খালি লাইন ফেরত দিলে এই কোডটি ভেঙে যাবে। একটি ভাল সমাধান ব্যবহার করতে হবে while p.poll() is Noneপরিবর্তে while True, এবং অপসারণif not line
exhuma

6
@ এক্সুমা: এটি ঠিক আছে। রিডলাইন খালি লাইনে "\ n" প্রদান করে, যা সত্য হিসাবে মূল্যায়ন করে না। পাইপ বন্ধ হয়ে গেলে এটি কেবল একটি খালি স্ট্রিং দেয়, যা সাবপ্রসেস সমাপ্ত হলে হবে।
এলিস পারসেল

1
@ ডেভ ভবিষ্যতের রেফের জন্য: পাইপ 2 + দিয়ে utf-8 লাইন প্রিন্ট করুন print(line.decode('utf-8').rstrip())
জোনাথন কোমার

3
প্রক্রিয়াটির আউটপুট রিয়েল রিয়েলটাইম পড়ার জন্য আপনাকে পাইথনকে বলতে হবে যে আপনি কোনও বাফারিং চান না। প্রিয় পাইথন আমাকে সরাসরি আউটপুট দিন। এবং এখানে কীভাবে: আপনার পরিবেশের পরিবর্তনশীল সেট করতে হবে PYTHONUNBUFFERED=1। এটি অসীম আউটপুটগুলির জন্য বিশেষত কার্যকর
জর্জ প্লিগোরোপল্লোস

38
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
    print line,
p.stdout.close()
p.wait()

1
@nbro সম্ভবত p.stdout.close()এটি অস্পষ্ট কারণ ।
অ্যানাটোলি টেকটোনিক

1
@nbro সম্ভবত কারণ কোডটি কোনও ব্যাখ্যা ছাড়াই দেওয়া হয়েছিল ...: /
অ্যারন হল

3
এই বি '' কি সম্পর্কে?
ম্যানুয়েলস্কিনিড 3 ই

29

আপনি সরাসরি প্রবাহগুলিতে সাব-প্রসেস আউটপুট পরিচালনা করতে পারেন। সরলীকৃত উদাহরণ:

subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)

এটি কি আপনাকে বাস্তবের পরে সামগ্রীগুলি পেতে দেয় .communicate()? বা পিতামাতার স্টাডার / স্টডআউট স্ট্রিমগুলির মধ্যে কী সামগ্রীগুলি হারিয়ে গেছে?
theferrit32

নাহ, communicate()ফেরার কোনও পদ্ধতি নেই CompletedProcess। এছাড়াও, এবং capture_outputসাথে পারস্পরিক একচেটিয়া । stdoutstderr
আইডান ফিল্ডম্যান

20

আপনি এটি চেষ্টা করতে পারেন:

import subprocess
import sys

process = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

while True:
    out = process.stdout.read(1)
    if out == '' and process.poll() != None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

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


হ্যাঁ, রিডলাইন () ব্যবহার করা মুদ্রণ বন্ধ করে দেবে (এমনকি sys.stdout.flush () কল করা সহ)
মা মা

3
এটি কি অনির্দিষ্টকালের জন্য ঝুলে থাকার কথা? প্রাথমিক সাবপ্রসেসটি সম্পন্ন হলে লুপ সম্পাদনা করার জন্য বয়লারপ্লেট কোড অন্তর্ভুক্ত করার জন্য আমি একটি প্রদত্ত সমাধানটি চাই। দুঃখিত আমি যতবার এটি অনুসন্ধান করি না কেন, সাবপ্রসেস এটেসেরা এমন একটি জিনিস যা আমি কখনও কাজ করতে পারি না।
থারস্মমনার

1
পাইথনে থাকাকালীন '' এর জন্য পরীক্ষা কেন আমরা আউট না হলে কেবল ব্যবহার করতে পারি?
গ্রেগ বেল

2
দীর্ঘমেয়াদী কাজের জন্য এটি সেরা সমাধান। তবে এটি ব্যবহার করা উচিত নয় এবং না! = কোনওটি নয়। আপনার কোনওটি ব্যবহার করা উচিত নয়!
কারি

স্ট্যাডার কি এর দ্বারা প্রদর্শিত হয়?
পিটার ভোলেলার

7

স্ট্রীমিং subprocess stdin এবং stdout- এ সঙ্গে পাইথন মধ্যে asyncio দ্বারা ব্লগ পোস্ট কেভিন ম্যাকার্থি দেখায় কিভাবে asyncio সঙ্গে এটা করতে:

import asyncio
from asyncio.subprocess import PIPE
from asyncio import create_subprocess_exec


async def _read_stream(stream, callback):
    while True:
        line = await stream.readline()
        if line:
            callback(line)
        else:
            break


async def run(command):
    process = await create_subprocess_exec(
        *command, stdout=PIPE, stderr=PIPE
    )

    await asyncio.wait(
        [
            _read_stream(
                process.stdout,
                lambda x: print(
                    "STDOUT: {}".format(x.decode("UTF8"))
                ),
            ),
            _read_stream(
                process.stderr,
                lambda x: print(
                    "STDERR: {}".format(x.decode("UTF8"))
                ),
            ),
        ]
    )

    await process.wait()


async def main():
    await run("docker build -t my-docker-image:latest .")


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

এটি পোস্ট করা
কোডটিতে

হাই @ জিফ আপনি কি ঠিক মতো ইঙ্গিত করতে পারেন যাতে আমি উত্তরটি আপডেট করতে পারি?
পাবলো

হাই, এটি আমার পক্ষে কাজ করেছে তবে কিছু ত্রুটি বার্তাগুলি থেকে মুক্তি পাওয়ার জন্য আমাকে নিম্নলিখিতগুলি যুক্ত করতে হয়েছিল: import nest_asyncio; nest_asyncio.apply()এবং শেল কমান্ডটি ব্যবহার করার process = await create_subprocess_shell(*command, stdout=PIPE, stderr=PIPE, shell=True)পরিবর্তে, এর পরিবর্তে process = await create_subprocess_exec(...)। চিয়ার্স!
ব্যবহারকারী319436

4

রিয়েল টাইম আউটপুট ইস্যুটি সমাধান করা হয়েছে: সি প্রোগ্রাম থেকে রিয়েল টাইম আউটপুট ক্যাপচার করার সময় পাইথনেও আমি একই ধরণের সমস্যার মুখোমুখি হয়েছি। আমি " fflush (stdout) যুক্ত করেছি ;" আমার সি কোডে এটা আমার জন্য কাজ করেছে। এখানে কোডটি স্নিপ করুন

<< সি প্রোগ্রাম >>

#include <stdio.h>
void main()
{
    int count = 1;
    while (1)
    {
        printf(" Count  %d\n", count++);
        fflush(stdout);
        sleep(1);
    }
}

<< পাইথন প্রোগ্রাম >>

#!/usr/bin/python

import os, sys
import subprocess


procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

while procExe.poll() is None:
    line = procExe.stdout.readline()
    print("Print:" + line)

<< আউটপুট >> মুদ্রণ: গণনা 1 মুদ্রণ: গণনা 2 মুদ্রণ: গণনা 3

আশা করি এটা সাহায্য করবে.

~ sairam


1
এটিই ছিল একমাত্র জিনিস যা আসলে সাহায্য করেছিল। আমি flush(stdout)সি ++ তে একই কোড ( ) ব্যবহার করেছি । ধন্যবাদ!
জেরহার্ড হ্যাজারার

পাইথন স্ক্রিপ্টটির সাথে একই রকম সমস্যা হচ্ছিল, অন্য একটি পাইথন স্ক্রিপ্টটিকে একটি সাবপ্রসেস বলে calling সাবপ্রসেস প্রিন্টগুলিতে পাইথন 3 তে "ফ্লাশ" প্রয়োজনীয় ছিল (মুদ্রণ ("হ্যালো", ফ্লাশ = ট্রু))) এছাড়াও,
অজস্র

3

আমি কিছুক্ষণ আগে একই সমস্যায় পড়েছি। আমার সমাধানটি ছিল readপদ্ধতির জন্য পুনরাবৃত্তি খনন করা , যা আপনার সাবপ্রসেসটি সম্পাদন শেষ না করা ইত্যাদির সাথে সাথেই ফিরে আসবে etc.


3

ব্যবহারের ক্ষেত্রে উপর নির্ভর করে আপনি সাব-প্রসেসে নিজেই বাফারিংটি অক্ষম করতে চাইতে পারেন।

যদি সাবপ্রসেসটি পাইথন প্রক্রিয়া হয়, আপনি কল করার আগে এটি করতে পারেন:

os.environ["PYTHONUNBUFFERED"] = "1"

অথবা বিকল্পভাবে envযুক্তিতে এটি পাস করুন Popen

অন্যথায়, আপনি যদি লিনাক্স / ইউনিক্সে থাকেন তবে আপনি stdbufসরঞ্জামটি ব্যবহার করতে পারেন । যেমন:

cmd = ["stdbuf", "-oL"] + cmd

অন্যান্য বিকল্প সম্পর্কে এখানেও দেখুন stdbuf

( একই উত্তরের জন্য এখানেও দেখুন ))


2

সাব-প্রসেসে রিয়েলটাইম আউটপুট পেতে আমি এই সমাধানটি ব্যবহার করেছি। প্রক্রিয়াটি ব্রেক স্টেটমেন্ট বা সম্ভাব্য অসীম লুপের প্রয়োজন ছাড়ার সাথে সাথে এই লুপটি থামবে।

sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while sub_process.poll() is None:
    out = sub_process.stdout.read(1)
    sys.stdout.write(out)
    sys.stdout.flush()

5
এটি কি সম্ভব যে স্টাডআউট বাফারটি খালি না রেখে লুপটি প্রস্থান করবে?
জয়

আমি একটি উপযুক্ত উত্তরের জন্য অনেক তাকিয়েছি যা সম্পূর্ণ হওয়ার পরে স্থির হয় না! আমি সমাধান হিসাবে এর if out=='': breakপরে যোগ করে খুঁজে পেয়েছিout = sub_process...
SOS

2

এই "প্লাগ-এন্ড-প্লে" ফাংশনটি এখানে পেয়েছে । মোহন মত কাজ!

import subprocess

def myrun(cmd):
    """from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html
    """
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    stdout = []
    while True:
        line = p.stdout.readline()
        stdout.append(line)
        print line,
        if line == '' and p.poll() != None:
            break
    return ''.join(stdout)

1
stderr=subprocess.STDOUTআসলে সংযোজন স্ট্রিমিং ডেটা ক্যাপচারে অনেক সহায়তা করে helps আমি এটি upvoting করছি।
খান

1
এখানে প্রধান গরুর মাংস গৃহীত উত্তর
ট্রিপলি

2

সাবপ্রসেসের আউটপুটে আপনি প্রতিটি বাইটের উপরে একটি ইেটরেটর ব্যবহার করতে পারেন। এটি সাবপ্রসেস থেকে ইনলাইন আপডেট ('\ r' দিয়ে শেষ লাইনগুলি পূর্ববর্তী আউটপুট লাইনে ওভাররাইট করে) মঞ্জুরি দেয়:

from subprocess import PIPE, Popen

command = ["my_command", "-my_arg"]

# Open pipe to subprocess
subprocess = Popen(command, stdout=PIPE, stderr=PIPE)


# read each byte of subprocess
while subprocess.poll() is None:
    for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''):
        c = c.decode('ascii')
        sys.stdout.write(c)
sys.stdout.flush()

if subprocess.returncode != 0:
    raise Exception("The subprocess did not terminate correctly.")

2

পাইথন 3.x এ প্রক্রিয়াটি স্তব্ধ হতে পারে কারণ আউটপুট স্ট্রিংয়ের পরিবর্তে বাইট অ্যারে হয়। আপনি এটি একটি স্ট্রিং মধ্যে ডিকোড নিশ্চিত করুন।

পাইথন ৩.6 থেকে শুরু করে encodingআপনি পপেন কনস্ট্রাক্টরের প্যারামিটার ব্যবহার করে এটি করতে পারেন । সম্পূর্ণ উদাহরণ:

process = subprocess.Popen(
    'my_command',
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    shell=True,
    encoding='utf-8',
    errors='replace'
)

while True:
    realtime_output = process.stdout.readline()

    if realtime_output == '' and process.poll() is not None:
        break

    if realtime_output:
        print(realtime_output.strip(), flush=True)

নোট করুন যে এই কোডটি আউটপুট ত্রুটিগুলিতে পুনঃনির্দেশ stderr করে stdoutএবং পরিচালনা করে


1

ব্লকবিহীন রিডলাইনগুলির সাথে পেরেক্সপেক্ট [ http://www.noah.org/wiki/Pexpect ] ব্যবহার করা এই সমস্যার সমাধান করবে। পাইপগুলি বাফার হয়ে গেছে এবং এটি আপনার অ্যাপের আউটপুটটি পাইপের সাহায্যে বাফার করছে, ফলে বাফার পূরণ না হওয়া বা প্রক্রিয়াটি মারা না যাওয়া পর্যন্ত আপনি সেই আউটপুটটিতে পৌঁছাতে পারবেন না।


0

সম্পূর্ণ সমাধান:

import contextlib
import subprocess

# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            out = []
            last = stream.read(1)
            # Don't loop forever
            if last == '' and proc.poll() is not None:
                break
            while last not in newlines:
                # Don't loop forever
                if last == '' and proc.poll() is not None:
                    break
                out.append(last)
                last = stream.read(1)
            out = ''.join(out)
            yield out

def example():
    cmd = ['ls', '-l', '/']
    proc = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        # Make all end-of-lines '\n'
        universal_newlines=True,
    )
    for line in unbuffered(proc):
        print line

example()

1
যেহেতু আপনি ব্যবহার করছেন universal_newlines=Trueউপর Popen()বিকল্পের পুরো পয়েন্ট যে - কল, আপনি সম্ভবত এ ছাড়া, এও তাদের আপনার নিজের হ্যান্ডলিং করা প্রয়োজন হবে না।
মার্টিনিউ

1
এটি অপ্রয়োজনীয় জটিল বলে মনে হচ্ছে। এটি বাফারিংয়ের সমস্যাগুলি সমাধান করে না। আমার উত্তরে লিঙ্কগুলি দেখুন ।
jfs

রিয়েলটাইমের (- আউটবুফ = এল) এ আরএসসিএনসি অগ্রগতি আউটপুট পেতে পারার একমাত্র উপায় এটি! ধন্যবাদ
মোহাম্মদহ্জপি

0

এটি হ'ল মূল কঙ্কাল যা আমি সর্বদা এটির জন্য ব্যবহার করি। সময়সীমা বাস্তবায়ন করা এটি সহজ করে তোলে এবং অনিবার্য ঝুলন্ত প্রক্রিয়াগুলি মোকাবেলা করতে সক্ষম।

import subprocess
import threading
import Queue

def t_read_stdout(process, queue):
    """Read from stdout"""

    for output in iter(process.stdout.readline, b''):
        queue.put(output)

    return

process = subprocess.Popen(['dir'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT,
                           bufsize=1,
                           cwd='C:\\',
                           shell=True)

queue = Queue.Queue()
t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
t_stdout.daemon = True
t_stdout.start()

while process.poll() is None or not queue.empty():
    try:
        output = queue.get(timeout=.5)

    except Queue.Empty:
        continue

    if not output:
        continue

    print(output),

t_stdout.join()

0

(এই সমাধানটি পাইথন
২.7.১৫ দিয়ে পরীক্ষা করা হয়েছে ) প্রতিটি লাইনে পড়ার / লেখার পরে আপনাকে কেবল sys.stdout.flush () প্রয়োজন:

while proc.poll() is None:
    line = proc.stdout.readline()
    sys.stdout.write(line)
    # or print(line.strip()), you still need to force the flush.
    sys.stdout.flush()

0

অজগর ৩.x বা পাইথন ২.x পরামর্শ দেয় এমন কয়েকটি উত্তর, নীচের কোড উভয়ের জন্য কাজ করবে।

 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,)
    stdout = []
    while True:
        line = p.stdout.readline()
        if not isinstance(line, (str)):
            line = line.decode('utf-8')
        stdout.append(line)
        print (line)
        if (line == '' and p.poll() != None):
            break
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.