সময়সীমা সহ মডিউল 'সাবপ্রসেস' ব্যবহার করা হচ্ছে


325

এটির stdoutডেটা ফিরিয়ে দেওয়ার একটি স্বেচ্ছাসেবক কমান্ড চালাতে পাইথন কোডটি এখানে দেওয়া হয়েছে , বা শূন্য-বহির্গমন প্রস্থান কোডগুলিতে একটি ব্যতিক্রম বাড়াতে হবে:

proc = subprocess.Popen(
    cmd,
    stderr=subprocess.STDOUT,  # Merge stdout and stderr
    stdout=subprocess.PIPE,
    shell=True)

communicate প্রক্রিয়াটি প্রস্থান করার জন্য অপেক্ষা করতে ব্যবহৃত হয়:

stdoutdata, stderrdata = proc.communicate()

subprocessসুতরাং - একটি প্রক্রিয়া সেকেন্ড বেশি এক্স সংখ্যার জন্য চলমান হত্যা করার ক্ষমতা - মডিউল সময়সীমার সমর্থন করে নাcommunicate চালানোর জন্য সব সময় প্রবেশ করুন সময় লাগতে পারে।

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


2
সম্পর্কিত পাইথন ইস্যু ট্র্যাকার এন্ট্রি: bugs.python.org/issue5673
শ্রীধর রত্নকুমার

10
পাইথন ২.এক্সের জন্য পাইপি.পিথন.আর.পি.পি.আই / সাবপ্রসেস 32 ব্যবহার করুন । এটি পাইথন ৩.x এর ব্যাকপোর্ট। এটিতে কল () এবং অপেক্ষা () অপেক্ষা করার সময়সীমা রয়েছে।
guettli

1
pypi.python.org/pypi/subprocess32 উইন্ডোজে কাজ করে না :(
adrianX

উত্তর:


170

পাইথন ৩.৩++ এ:

from subprocess import STDOUT, check_output

output = check_output(cmd, stderr=STDOUT, timeout=seconds)

output একটি বাইট স্ট্রিং যা কমান্ডের মার্জড stdout, stderr ডেটা ধারণ করে।

check_outputপদ্ধতির CalledProcessErrorবিপরীতে প্রশ্নের পাঠ্য অনুসারে শূন্য-বহির্গমন প্রস্থান স্থিতি উত্থাপন করে proc.communicate()

আমি অপসারণ করেছি shell=Trueকারণ এটি প্রায়শই অযথা ব্যবহার করা হয়। cmdসত্যই এটি প্রয়োজন হলে আপনি সর্বদা এটি আবার যুক্ত করতে পারেন । আপনি যদি যোগ করেন shell=Trueঅর্থাত্, যদি শিশু প্রক্রিয়াটি তার নিজস্ব বংশধরদের উদ্ভব করে; check_output()সময়সীমা সূচিত হওয়ার চেয়ে অনেক পরে ফিরে আসতে পারে, সাবপ্রসেসের সময়সীমা ব্যর্থতা দেখুন

টাইমআউট বৈশিষ্ট্যটি পাইথন ২.x subprocess32এ 3.2+ সাবপ্রসেসি মডিউলটির ব্যাকপোর্টের মাধ্যমে উপলব্ধ ।


17
প্রকৃতপক্ষে, এবং সাবপ্রসেস টাইমআউট সমর্থনটি পাইপথন ২ এ ব্যবহারের জন্য আমি যে সাব-প্রসেস 32 ব্যাকপোর্টটি বজায় রেখেছি তাতে উপস্থিত রয়েছে p pypi.python.org/pypi/subprocess32
gps

8
@gps শ্রীধর ক্রস প্ল্যাটফর্ম সমাধানের জন্য জিজ্ঞাসা করলেন, যখন আপনার ব্যাকপোর্টটি কেবল পসিক্সকে সমর্থন করে: যখন আমি এটি চেষ্টা করেছিলাম তখন এমএসভিসি অভিযোগ করেছে (প্রত্যাশিত) নিখরচায় হারিয়ে যাওয়ার বিষয়ে। :)
দ্য ক্যাট

আপনার যদি আউটপুটটির প্রয়োজন না হয় তবে আপনি কেবল সাব-প্রসেসক্ল্যাব ব্যবহার করতে পারেন।
কাইল গিবসন

পাইথন ৩.৫ থেকে, ক্যাপচার_আউটপুট = সত্য সহ সাবপ্রসেস.আরুন () ব্যবহার করুন এবং ব্যবহারযোগ্য আউটপুট পেতে এনকোডিং প্যারামিটার ব্যবহার করুন।
এমকেস্পার

1
@ এমকেস্পার: ১- check_output()আউটপুট পাওয়ার জন্য পছন্দের উপায় (এটি সরাসরি আউটপুট ফেরত দেয়, ত্রুটিগুলি উপেক্ষা করে না, এটি চিরকালের জন্য উপলব্ধ)। 2- run()আরও নমনীয় তবে run()ডিফল্টরূপে ত্রুটি উপেক্ষা করে এবং আউটপুট পেতে অতিরিক্ত পদক্ষেপের প্রয়োজন 3- check_output()পদে প্রয়োগ করা হয়run() এবং তাই এটি একই আর্গুমেন্টগুলির বেশিরভাগ গ্রহণ করে। 4- নাইট: capture_output3.7 থেকে পাওয়া যায়, 3.5 নয়
জেএফএস

205

নিম্ন স্তরের বিশদ সম্পর্কে আমি বেশি কিছু জানি না; তবে, পাইথন ২.6 এ, এপিআই থ্রেডগুলির জন্য অপেক্ষা করার এবং প্রক্রিয়াগুলি সমাপ্ত করার ক্ষমতা সরবরাহ করে, আলাদা থ্রেডে প্রক্রিয়াটি চালানোর বিষয়ে কী বলা যায়?

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

আমার মেশিনে এই স্নিপেটের আউটপুট হল:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

যেখানে এটি দেখা যায় যে, প্রথম নির্বাহের সময়, প্রক্রিয়াটি সঠিকভাবে শেষ হয়েছিল (রিটার্ন কোড 0), যখন দ্বিতীয়টিতে প্রক্রিয়াটি সমাপ্ত হয় (রিটার্ন কোড -15)।

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


16
প্ল্যাটফর্ম স্বাধীন হওয়ার জন্য +1। আমি এটি লিনাক্স এবং উইন্ডোজ both (সাইগউইন এবং প্লেইন উইন্ডোজ পাইথন) উভয় ক্ষেত্রে চালিয়েছি - তিনটি ক্ষেত্রেই প্রত্যাশার মতো কাজ করে।
ফুজি

7
নেটিভ পোপেন কাওয়ার্গ্স পাস করার জন্য এবং এটিকে সংক্ষেপে রাখতে সক্ষম হওয়ার জন্য আমি আপনার কোডটি কিছুটা পরিবর্তন করেছি। এটি এখন বহু উদ্দেশ্য ব্যবহারের জন্য প্রস্তুত; gist.github.com/1306188
kirpit

2
@ রেডিসের যে কারও সমস্যা হচ্ছিল, এই প্রশ্নটি সাহায্য করতে পারে। সংক্ষেপে, যদি আপনি শেল = সত্য ব্যবহার করেন তবে শেলটি শিশু প্রক্রিয়াতে পরিণত হয় যা মারা যায়, এবং এর কমান্ড (শিশু প্রক্রিয়াটির শিশু) বেঁচে থাকে।
আনসন

6
এই উত্তরটি মূলটির একই কার্যকারিতা সরবরাহ করে না কারণ এটি স্টডআউট ফেরত না।
স্টেফেনবিজ

2
থ্রেড.আইস_ালাইভ একটি রেসের শর্তের দিকে নিয়ে যেতে পারে। দেখুন ostricher.com/2015/01/python-subprocess-with-timeout
ChaimKut

132

jcollado এর উত্তর থ্রেডিং ব্যবহার করে সরল করা যেতে পারে ime টাইমার ক্লাস:

import shlex
from subprocess import Popen, PIPE
from threading import Timer

def run(cmd, timeout_sec):
    proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
    timer = Timer(timeout_sec, proc.kill)
    try:
        timer.start()
        stdout, stderr = proc.communicate()
    finally:
        timer.cancel()

# Examples: both take 1 second
run("sleep 1", 5)  # process ends normally at 1 second
run("sleep 5", 1)  # timeout happens at 1 second

11
সাধারণ পোর্টেবল সমাধানের জন্য +1। আপনার প্রয়োজন নেই lambda:t = Timer(timeout, proc.kill)
jfs

3
+1 এটি গ্রহণযোগ্য উত্তর হওয়া উচিত, কারণ প্রক্রিয়াটি পরিবর্তনের জন্য যেভাবে প্রক্রিয়া শুরু করা হয় তার প্রয়োজন হয় না।
ডেভ ব্রানটন

1
এটি ল্যাম্বডা লাগবে কেন? ল্যাম্বডা ছাড়া বাউন্ড পদ্ধতি পি.কিল ব্যবহার করা যায় না?
ড্যানি স্ট্যাপল

//, আপনি কি এর ব্যবহারের উদাহরণ অন্তর্ভুক্ত করতে ইচ্ছুক হবেন?
নাথান বাসানিজ

1
@ টুক এর timer.isAlive()আগে এর timer.cancel()অর্থ হ'ল এটি স্বাভাবিকভাবে শেষ হয়েছে
চার্লস

83

আপনি যদি ইউনিক্সে থাকেন,

import signal
  ...
class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(5*60)  # 5 minutes
try:
    stdoutdata, stderrdata = proc.communicate()
    signal.alarm(0)  # reset the alarm
except Alarm:
    print "Oops, taking too long!"
    # whatever else

3
ভাল, আমি একটি ক্রস প্ল্যাটফর্ম সমাধানে আগ্রহী যা কমপক্ষে উইন / লিনাক্স / ম্যাকের জন্য কাজ করে।
শ্রীধর রত্নকুমার

1
আমি এই ইউনিক্স-ভিত্তিক পদ্ধতির পছন্দ করি। আদর্শভাবে, কেউ এটিকে উইন্ডোজ-নির্দিষ্ট পদ্ধতির সাথে (ক্রিয়েটপ্রসেস এবং জবস ব্যবহার করে) একত্রিত করতে পারে .. তবে আপাতত, নীচের সমাধানটি সহজ, সহজ এবং এতদূর কাজ করে।
শ্রীধর রত্নাকুমার

3
আমি একটি পোর্টেবল সমাধান যুক্ত করেছি, আমার উত্তর দেখুন
ফ্লাইবায়ার

4
এই দ্রষ্টব্যটি কেবলমাত্র _ সংকেতটিতে কাজ করবে would সিগন্যাল (সিগন্যাল.সিগ্যালআরএম, অ্যালার্ম_হ্যান্ডলার) মূল থ্রেড থেকে ডাকা হয়েছে। সংকেতের জন্য ডকুমেন্টেশন দেখুন
উদ্বোধক

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

44

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

from os import kill
from signal import alarm, signal, SIGALRM, SIGKILL
from subprocess import PIPE, Popen

def run(args, cwd = None, shell = False, kill_tree = True, timeout = -1, env = None):
    '''
    Run a command with a timeout after which it will be forcibly
    killed.
    '''
    class Alarm(Exception):
        pass
    def alarm_handler(signum, frame):
        raise Alarm
    p = Popen(args, shell = shell, cwd = cwd, stdout = PIPE, stderr = PIPE, env = env)
    if timeout != -1:
        signal(SIGALRM, alarm_handler)
        alarm(timeout)
    try:
        stdout, stderr = p.communicate()
        if timeout != -1:
            alarm(0)
    except Alarm:
        pids = [p.pid]
        if kill_tree:
            pids.extend(get_process_children(p.pid))
        for pid in pids:
            # process might have died before getting to this line
            # so wrap to avoid OSError: no such process
            try: 
                kill(pid, SIGKILL)
            except OSError:
                pass
        return -9, '', ''
    return p.returncode, stdout, stderr

def get_process_children(pid):
    p = Popen('ps --no-headers -o pid --ppid %d' % pid, shell = True,
              stdout = PIPE, stderr = PIPE)
    stdout, stderr = p.communicate()
    return [int(p) for p in stdout.split()]

if __name__ == '__main__':
    print run('find /', shell = True, timeout = 3)
    print run('find', shell = True)

3
এটি উইন্ডোতে কাজ করবে না, এবং কার্য ক্রমের বিপরীত হবে।
হামিশ গ্রুবিজন

3
এটি কখনও কখনও ব্যতিক্রম হতে পারে যখন অন্য হ্যান্ডলার সিগালআরমে নিজেকে নিবন্ধিত করে এবং এইটিকে "হত্যা" করার আগে প্রক্রিয়াটি মেরে ফেলেছে, প্রায় কাজ যুক্ত করে। বিটিডাব্লু, দুর্দান্ত রেসিপি! হ্যান্ডলিংয়ের মোড়কে জমে থাকা বা ক্র্যাশ না করে আমি এ পর্যন্ত 50,000 বগী প্রক্রিয়া চালু করতে ব্যবহার করেছি।
ইয়ারোস্লাভ বুলাটোভ

থ্রেডেড অ্যাপ্লিকেশনটিতে চালানোর জন্য এটি কীভাবে সংশোধন করা যায়? আমি শ্রমিক থ্রেড মধ্যে থেকে এটি ব্যবহার এবং পেতে চেষ্টা করছিValueError: signal only works in main thread
Wim

@ ইয়ারোস্লাভ বুলাটোভ তথ্যের জন্য ধন্যবাদ। উল্লিখিত সমস্যাটি মোকাবেলায় আপনি কীভাবে কাজ করেছেন?
jpswain

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

18

আমি সুসুদিও উত্তর পরিবর্তন করেছি । এখন আয় ফাংশন: ( returncode, stdout, stderr, timeout) - stdoutএবং stderrUTF-8 স্ট্রিং সঙ্কেতমুক্ত হয়

def kill_proc(proc, timeout):
  timeout["value"] = True
  proc.kill()

def run(cmd, timeout_sec):
  proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  timeout = {"value": False}
  timer = Timer(timeout_sec, kill_proc, [proc, timeout])
  timer.start()
  stdout, stderr = proc.communicate()
  timer.cancel()
  return proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"), timeout["value"]

18

অবাক কেউ ব্যবহারের উল্লেখ নেই timeout

timeout 5 ping -c 3 somehost

এটি প্রতিটি ব্যবহারের ক্ষেত্রে স্পষ্টতই কাজ করার জন্য কাজ করবে না, তবে আপনার যদি কোনও সাধারণ স্ক্রিপ্ট নিয়ে কাজ করা হয় তবে এটি পরাস্ত করা শক্ত।

homebrewম্যাক ব্যবহারকারীদের মাধ্যমে কোর্টিলগুলিতে গেইমআউট হিসাবেও উপলব্ধ ।


1
আপনি কি বলতে চান: proc = subprocess.Popen(['/usr/bin/timeout', str(timeout)] + cmd, ...)timeoutউইন্ডোজে ওপি যেমন জিজ্ঞাসা করছে তেমন কমান্ড আছে ?
jfs

উইন্ডোজগুলিতে, কেউ গিট ব্যাশের মতো অ্যাপ্লিকেশন ব্যবহার করতে পারে যা উইন্ডোজে ব্যাশ ইউটিলিটিগুলিকে অনুমতি দেয়।
কৌশিক আচার্য

@ কৌশিকআচার্য যদি আপনি গিট ব্যাশ ব্যবহার করেন, পাইথন সাব-প্রসেস কল করলে এটি উইন্ডোতে চলবে, সুতরাং এই বাইপাসটি কার্যকর হবে না।
নমন চিকারা

16

timeoutএখন সাব-প্রসেস মডিউল দ্বারা call()এবং communicate()পাইথন 3.3 হিসাবে সমর্থিত :

import subprocess

subprocess.call("command", timeout=20, shell=True)

এটি আদেশটি কল করবে এবং ব্যতিক্রম বাড়িয়ে তুলবে

subprocess.TimeoutExpired

20 সেকেন্ডের পরে যদি কমান্ডটি শেষ না হয়।

তারপরে আপনি নিজের কোডটি চালিয়ে যেতে ব্যতিক্রমটি পরিচালনা করতে পারেন, এরকম কিছু:

try:
    subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
    # insert code here

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


timeoutপ্যারামিটারের উল্লেখ রয়েছে এমন একটি বিদ্যমান উত্তর রয়েছে । যদিও এর সাথে আরও একবার উল্লেখ করলে ক্ষতি হবে না।
jfs

//, আমি মনে করি ওপি পুরানো পাইথনের সমাধান খুঁজছে।
নাথান বাসানিজ

11

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

    outFile =  tempfile.SpooledTemporaryFile() 
    errFile =   tempfile.SpooledTemporaryFile() 
    proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
    wait_remaining_sec = timeout

    while proc.poll() is None and wait_remaining_sec > 0:
        time.sleep(1)
        wait_remaining_sec -= 1

    if wait_remaining_sec <= 0:
        killProc(proc.pid)
        raise ProcessIncompleteError(proc, timeout)

    # read temp streams from start
    outFile.seek(0);
    errFile.seek(0);
    out = outFile.read()
    err = errFile.read()
    outFile.close()
    errFile.close()

অসম্পূর্ণ বলে মনে হচ্ছে - অস্থিরতা কী?
স্পাইডারপ্ল্যান্ট 0

"পপেন" কল ("শেল = ট্রু" দিয়ে সতর্ক থাকুন) এর ভিতরে "আমদানি টেম্পাইল", "আমদানির সময়" এবং "শেল = ট্রু" অন্তর্ভুক্ত করুন!
এডুয়ার্ডো লুসিও

11

আমি জানি না কেন এটা mentionned নয় কিন্তু পাইথন 3.5 যেহেতু, একটি নতুন subprocess.runসার্বজনীন কমান্ড (যে প্রতিস্থাপন বোঝানো হয় check_call, check_output...) এবং যা হয়েছে timeoutপাশাপাশি প্যারামিটার।

উপপ্রসেস.আরুন (আরগস, *, স্টিডিন = কিছুই নয়, ইনপুট = কিছুই নয়, স্টাডআউট = কিছুই নয়, স্টাডার = কিছুই নয়, শেল = মিথ্যা, সিডাব্লুড = কিছুই নয়, সময়সীমা = কিছুই নয়, চেক = মিথ্যা, এনকোডিং = কিছুই নেই, ত্রুটিগুলি = কিছুই নয়)

Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

subprocess.TimeoutExpiredসময়সীমা শেষ হওয়ার পরে এটি একটি ব্যতিক্রম উত্থাপন করে ।


6

আমার সমাধানটি এখানে দেওয়া হয়েছে, আমি থ্রেড এবং ইভেন্টটি ব্যবহার করছিলাম:

import subprocess
from threading import Thread, Event

def kill_on_timeout(done, timeout, proc):
    if not done.wait(timeout):
        proc.kill()

def exec_command(command, timeout):

    done = Event()
    proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    watcher = Thread(target=kill_on_timeout, args=(done, timeout, proc))
    watcher.daemon = True
    watcher.start()

    data, stderr = proc.communicate()
    done.set()

    return data, stderr, proc.returncode

কর্মে:

In [2]: exec_command(['sleep', '10'], 5)
Out[2]: ('', '', -9)

In [3]: exec_command(['sleep', '10'], 11)
Out[3]: ('', '', 0)

5

আমি যে সমাধানটি ব্যবহার করি তা হ'ল সময়সীমার সাথে শেল কমান্ডের উপসর্গ করা । কোমন্ড যদি খুব বেশি সময় নেয় তবে সময়সীমা এটি বন্ধ করে দেবে এবং সময়সীমার সাথে পপেনের একটি রিটার্ন কোড থাকবে ode যদি এটি> 128 হয় তবে এর অর্থ সময়সীমার প্রক্রিয়াটি নিহত হয়েছে।

সময়সীমা এবং বড় আউটপুট (> 64 কে) সহ পাইথন সাবপ্রসেসগুলিও দেখুন


আমি অনুরূপ একটি সরঞ্জাম ব্যবহার করি timeout- প্যাকেজ.বুন্টু.com / অনুসন্ধান / কিওয়ার্ড= টাইমআউট - তবে উইন্ডোজেও কাজ করে না, তাই না?
শ্রীধর রত্নকুমার

5

আমি jcolladoআমার পাইথন মডিউল ইজিপ্রসেস থেকে থ্রেডিং সহ সমাধানটি যুক্ত করেছি

ইনস্টল করুন:

pip install easyprocess

উদাহরণ:

from easyprocess import Proc

# shell is not supported!
stdout=Proc('ping localhost').call(timeout=1.5).stdout
print stdout

ইজাইপ্রসেস মডিউল ( কোড.একটিভস্টেট. com/pypm/ easyprocess ) আমার পক্ষে কাজ করেছে, এমনকি এটি মাল্টিপ্রসেসিং থেকে ব্যবহার করে।
আইচুকস

5

যদি আপনি পাইথন 2 ব্যবহার করেন তবে চেষ্টা করে দেখুন

import subprocess32

try:
    output = subprocess32.check_output(command, shell=True, timeout=3)
except subprocess32.TimeoutExpired as e:
    print e

1
সম্ভবত উইন্ডোজে কাজ করছেন না, যেমন প্রাথমিক প্রশ্নে জিজ্ঞাসা করা হয়েছে
জিন-ফ্রাঙ্কোয়েস টি।

5

লিনাক্স কমান্ড প্রস্তুত timeoutকরা খারাপ কাজ নয় এবং এটি আমার পক্ষে কাজ করে।

cmd = "timeout 20 "+ cmd
subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = p.communicate()

উপ-প্রক্রিয়া সম্পাদনের সময় আমি কীভাবে আউট পুট স্ট্রিংগুলি মুদ্রণ করতে পারি? - উপ-প্রক্রিয়া দ্বারা আউট পুট বার্তাগুলি ফিরে আসে।
আম্মাদ

3

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

class Command(threading.Thread):
    def __init__(self, cmd, outFile, errFile, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd
        self.process = None
        self.outFile = outFile
        self.errFile = errFile
        self.timed_out = False
        self.timeout = timeout

    def run(self):
        self.process = subprocess.Popen(self.cmd, stdout = self.outFile, \
            stderr = self.errFile)

        while (self.process.poll() is None and self.timeout > 0):
            time.sleep(1)
            self.timeout -= 1

        if not self.timeout > 0:
            self.process.terminate()
            self.timed_out = True
        else:
            self.timed_out = False

তারপরে অন্য শ্রেণি বা ফাইল থেকে:

        outFile =  tempfile.SpooledTemporaryFile()
        errFile =   tempfile.SpooledTemporaryFile()

        executor = command.Command(c, outFile, errFile, timeout)
        executor.daemon = True
        executor.start()

        executor.join()
        if executor.timed_out:
            out = 'timed out'
        else:
            outFile.seek(0)
            errFile.seek(0)
            out = outFile.read()
            err = errFile.read()

        outFile.close()
        errFile.close()

আসলে, এটি সম্ভবত কাজ করে না। terminate()ফাংশন চিহ্ন একটি থ্রেড সমাপ্ত, কিন্তু আসলে থ্রেড বিনষ্ট করে না! আমি এটি * নিক্সে যাচাই করতে পারি, তবে পরীক্ষার জন্য আমার কাছে উইন্ডোজ কম্পিউটার নেই।
dotancohen

2

একবার * ইউনিক্সে সম্পূর্ণ প্রক্রিয়া চলমান যন্ত্রপাতিটি বুঝতে পারলে আপনি সহজেই সহজ সমাধানটি খুঁজে পাবেন:

এই সহজ উদাহরণটি বিবেচনা করুন কীভাবে সিলেক্ট সিলেক্ট () আজকাল * নিক্সের সর্বত্রই পাওয়া যায়) ব্যবহার করে সময়সীকরণযোগ্য যোগাযোগ () মেথ তৈরি করা যায়। এটি এপল / পোল / কেকিউ দিয়েও লেখা যেতে পারে তবে সিলেক্ট করুন (সিলেক্ট) () রূপটি আপনার পক্ষে ভাল উদাহরণ হতে পারে। এবং নির্বাচনের প্রধান সীমাবদ্ধতা (গতি এবং 1024 সর্বোচ্চ এফডিএস) আপনার কাজের জন্য প্রযোজ্য নয়।

এটি * নিক্সের অধীনে কাজ করে, থ্রেড তৈরি করে না, সংকেত ব্যবহার করে না, কোনও থ্রেড (কেবলমাত্র প্রধান নয়) থেকে হাসানো যেতে পারে এবং আমার মেশিনে স্ট্রাউড থেকে 250 এমবি / এস ডেটা পড়ার জন্য আগ্রহী (i5 2.3ghz)।

যোগাযোগের শেষে stdout / stderr এ যোগদান করতে সমস্যা আছে। আপনার যদি বিশাল প্রোগ্রাম আউটপুট থাকে তবে এটি বড় মেমরির ব্যবহারের দিকে নিয়ে যেতে পারে। তবে আপনি সংক্ষিপ্ত সময়ের সাথে সংযুক্তি () কয়েকবার কল করতে পারেন।

class Popen(subprocess.Popen):
    def communicate(self, input=None, timeout=None):
        if timeout is None:
            return subprocess.Popen.communicate(self, input)

        if self.stdin:
            # Flush stdio buffer, this might block if user
            # has been writing to .stdin in an uncontrolled
            # fashion.
            self.stdin.flush()
            if not input:
                self.stdin.close()

        read_set, write_set = [], []
        stdout = stderr = None

        if self.stdin and input:
            write_set.append(self.stdin)
        if self.stdout:
            read_set.append(self.stdout)
            stdout = []
        if self.stderr:
            read_set.append(self.stderr)
            stderr = []

        input_offset = 0
        deadline = time.time() + timeout

        while read_set or write_set:
            try:
                rlist, wlist, xlist = select.select(read_set, write_set, [], max(0, deadline - time.time()))
            except select.error as ex:
                if ex.args[0] == errno.EINTR:
                    continue
                raise

            if not (rlist or wlist):
                # Just break if timeout
                # Since we do not close stdout/stderr/stdin, we can call
                # communicate() several times reading data by smaller pieces.
                break

            if self.stdin in wlist:
                chunk = input[input_offset:input_offset + subprocess._PIPE_BUF]
                try:
                    bytes_written = os.write(self.stdin.fileno(), chunk)
                except OSError as ex:
                    if ex.errno == errno.EPIPE:
                        self.stdin.close()
                        write_set.remove(self.stdin)
                    else:
                        raise
                else:
                    input_offset += bytes_written
                    if input_offset >= len(input):
                        self.stdin.close()
                        write_set.remove(self.stdin)

            # Read stdout / stderr by 1024 bytes
            for fn, tgt in (
                (self.stdout, stdout),
                (self.stderr, stderr),
            ):
                if fn in rlist:
                    data = os.read(fn.fileno(), 1024)
                    if data == '':
                        fn.close()
                        read_set.remove(fn)
                    tgt.append(data)

        if stdout is not None:
            stdout = ''.join(stdout)
        if stderr is not None:
            stderr = ''.join(stderr)

        return (stdout, stderr)

2
এটি কেবল ইউনিক্সের অর্ধেক সমস্যার সমাধান করে।
স্পেসহোস্ট

2

আপনি এটি ব্যবহার করে করতে পারেন select

import subprocess
from datetime import datetime
from select import select

def call_with_timeout(cmd, timeout):
    started = datetime.now()
    sp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    while True:
        p = select([sp.stdout], [], [], timeout)
        if p[0]:
            p[0][0].read()
        ret = sp.poll()
        if ret is not None:
            return ret
        if (datetime.now()-started).total_seconds() > timeout:
            sp.kill()
            return None

1

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


দেখে মনে হচ্ছে কিল্যাবলপ্রসেস পপেন.কমিউনেট () কলটিতে একটি সময়সীমা যুক্ত করে না।
উইম কোয়েন

1

যদিও আমি এ ব্যাপকভাবে লাগছিল না, এই প্রসাধক আমি ActiveState পাওয়া জিনিস এই সাজানোর জন্য বেশ উপকারী বলে মনে হয়। পাশাপাশি subprocess.Popen(..., close_fds=True), কমপক্ষে আমি পাইথনে শেল-স্ক্রিপ্টিংয়ের জন্য প্রস্তুত।


এই সাজসজ্জারটি সিগন্যালআলার্ম ব্যবহার করে যা উইন্ডোজে উপলভ্য নয়।
ডিবিএন

1

এই দ্রবণটি শেল = সত্যের ক্ষেত্রে প্রক্রিয়া গাছকে হত্যা করে, প্রক্রিয়াটির পরামিতিগুলি (অথবা না) পাস করে, একটি সময়সীমা হয় এবং কল ব্যাকের স্টাডআউট, স্টার্ডার এবং প্রক্রিয়া আউটপুট পায় (এটি কিল_প্রোক_ট্রি জন্য psutil ব্যবহার করে)। এটি jcollado সহ এসওতে পোস্ট করা বেশ কয়েকটি সমাধানের ভিত্তিতে তৈরি হয়েছিল। জ্যাকোল্যাডোর উত্তরে আনসন এবং জ্যারাডিসের মন্তব্যের জবাবে পোস্ট করা। উইন্ডোজ এসআরভিআর 2012 এবং উবুন্টু 14.04 এ পরীক্ষিত। দয়া করে মনে রাখবেন যে উবুন্টুর জন্য আপনার পিতামহ সন্তানদের ...

def kill_proc_tree(pid, including_parent=True):
  parent = psutil.Process(pid)
  children = parent.children(recursive=True)
  for child in children:
    child.kill()
  psutil.wait_procs(children, timeout=5)
  if including_parent:
    parent.kill()
    parent.wait(5)

def run_with_timeout(cmd, current_dir, cmd_parms, timeout):
  def target():
    process = subprocess.Popen(cmd, cwd=current_dir, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

    # wait for the process to terminate
    if (cmd_parms == ""):
      out, err = process.communicate()
    else:
      out, err = process.communicate(cmd_parms)
    errcode = process.returncode

  thread = Thread(target=target)
  thread.start()

  thread.join(timeout)
  if thread.is_alive():
    me = os.getpid()
    kill_proc_tree(me, including_parent=False)
    thread.join()

1

পোপেন ক্লাসটি সাবক্লাস করার এবং কিছু সাধারণ পদ্ধতির সজ্জকার দ্বারা এটি প্রসারিত করার একটি ধারণা রয়েছে। আসুন একে এক্সপায়ারপোপেন বলি।

from logging import error
from subprocess import Popen
from threading import Event
from threading import Thread


class ExpirablePopen(Popen):

    def __init__(self, *args, **kwargs):
        self.timeout = kwargs.pop('timeout', 0)
        self.timer = None
        self.done = Event()

        Popen.__init__(self, *args, **kwargs)

    def __tkill(self):
        timeout = self.timeout
        if not self.done.wait(timeout):
            error('Terminating process {} by timeout of {} secs.'.format(self.pid, timeout))
            self.kill()

    def expirable(func):
        def wrapper(self, *args, **kwargs):
            # zero timeout means call of parent method
            if self.timeout == 0:
                return func(self, *args, **kwargs)

            # if timer is None, need to start it
            if self.timer is None:
                self.timer = thr = Thread(target=self.__tkill)
                thr.daemon = True
                thr.start()

            result = func(self, *args, **kwargs)
            self.done.set()

            return result
        return wrapper

    wait = expirable(Popen.wait)
    communicate = expirable(Popen.communicate)


if __name__ == '__main__':
    from subprocess import PIPE

    print ExpirablePopen('ssh -T git@bitbucket.org', stdout=PIPE, timeout=1).communicate()

1

আমার একটি সমস্যা ছিল যে আমি একটি মাল্টিথ্রেডিং সাবপ্রসেসটি শেষ করতে চেয়েছি যদি এটি একটি নির্দিষ্ট সময়সীমা চেয়ে বেশি সময় নেয়। আমি একটি সময়সীমা সেট করতে চেয়েছিলাম Popen(), কিন্তু এটি কার্যকর হয়নি। তারপরে, আমি বুঝতে পেরেছিলাম যে Popen().wait()এটি সমান call()এবং তাই .wait(timeout=xxx)পদ্ধতিটির মধ্যে একটি সময়সীমা নির্ধারণ করার আমার ধারণা ছিল যা শেষ পর্যন্ত কাজ করেছিল। সুতরাং, আমি এটি এইভাবে সমাধান করেছি:

import os
import sys
import signal
import subprocess
from multiprocessing import Pool

cores_for_parallelization = 4
timeout_time = 15  # seconds

def main():
    jobs = [...YOUR_JOB_LIST...]
    with Pool(cores_for_parallelization) as p:
        p.map(run_parallel_jobs, jobs)

def run_parallel_jobs(args):
    # Define the arguments including the paths
    initial_terminal_command = 'C:\\Python34\\python.exe'  # Python executable
    function_to_start = 'C:\\temp\\xyz.py'  # The multithreading script
    final_list = [initial_terminal_command, function_to_start]
    final_list.extend(args)

    # Start the subprocess and determine the process PID
    subp = subprocess.Popen(final_list)  # starts the process
    pid = subp.pid

    # Wait until the return code returns from the function by considering the timeout. 
    # If not, terminate the process.
    try:
        returncode = subp.wait(timeout=timeout_time)  # should be zero if accomplished
    except subprocess.TimeoutExpired:
        # Distinguish between Linux and Windows and terminate the process if 
        # the timeout has been expired
        if sys.platform == 'linux2':
            os.kill(pid, signal.SIGTERM)
        elif sys.platform == 'win32':
            subp.terminate()

if __name__ == '__main__':
    main()

0

দুর্ভাগ্যক্রমে, আমি আমার নিয়োগকর্তার দ্বারা উত্স কোড প্রকাশের বিষয়ে খুব কঠোর নীতি দ্বারা আবদ্ধ, সুতরাং আমি প্রকৃত কোড সরবরাহ করতে পারি না। তবে আমার স্বাদের জন্য সবচেয়ে ভাল সমাধান হ'ল Popen.wait()অনির্দিষ্টকালের জন্য অপেক্ষা করার পরিবর্তে পোল দেওয়ার জন্য একটি সাবক্লাস ওভাররাইডিং তৈরি করা এবং Popen.__init__একটি সময়সীমা পরামিতি গ্রহণ করা। একবার আপনি এটি করেন, অন্যান্য সমস্ত Popenপদ্ধতি (যা কল wait) প্রত্যাশিত হিসাবে কাজ করবে, সহ অন্তর্ভুক্ত communicate


0

https://pypi.python.org/pypi/python-subprocess2 সাবপ্রসেস মডিউলটির জন্য এক্সটেনশন সরবরাহ করে যা আপনাকে একটি নির্দিষ্ট সময় পর্যন্ত অপেক্ষা করতে দেয়, অন্যথায় অবসান হয়।

সুতরাং, প্রক্রিয়াটি শেষ হতে 10 সেকেন্ড অবধি অপেক্ষা করুন, অন্যথায় হত্যা করুন:

pipe  = subprocess.Popen('...')

timeout =  10

results = pipe.waitOrTerminate(timeout)

এটি উভয় উইন্ডো এবং ইউনিক্সের সাথে সামঞ্জস্যপূর্ণ। "ফলাফল" একটি অভিধান, এটিতে "রিটার্নকোড" রয়েছে যা অ্যাপ্লিকেশনটির রিটার্ন (বা এটি মেরে ফেলতে হত তবে কিছুই নয়) পাশাপাশি "অ্যাকশনটেকন"। প্রক্রিয়াটি সাধারণত সম্পন্ন হলে "SUBPROCESS2_PROCESS_COMPLETED" হবে বা গৃহীত পদক্ষেপের ভিত্তিতে "SUBPROCESS2_PROCESSQLMMATATED" এবং SUBPROCESS2_PROCESS_KILLED এর মুখোশ হবে (সম্পূর্ণ বিবরণের জন্য ডকুমেন্টেশন দেখুন)


0

পাইথন ২.6++ এর জন্য জেনভেন্ট ব্যবহার করুন

 from gevent.subprocess import Popen, PIPE, STDOUT

 def call_sys(cmd, timeout):
      p= Popen(cmd, shell=True, stdout=PIPE)
      output, _ = p.communicate(timeout=timeout)
      assert p.returncode == 0, p. returncode
      return output

 call_sys('./t.sh', 2)

 # t.sh example
 sleep 5
 echo done
 exit 1

0

অজগর 2.7

import time
import subprocess

def run_command(cmd, timeout=0):
    start_time = time.time()
    df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    while timeout and df.poll() == None:
        if time.time()-start_time >= timeout:
            df.kill()
            return -1, ""
    output = '\n'.join(df.communicate()).strip()
    return df.returncode, output

-1
import subprocess, optparse, os, sys, re, datetime, threading, time, glob, shutil, xml.dom.minidom, traceback

class OutputManager:
    def __init__(self, filename, mode, console, logonly):
        self.con = console
        self.logtoconsole = True
        self.logtofile = False

        if filename:
            try:
                self.f = open(filename, mode)
                self.logtofile = True
                if logonly == True:
                    self.logtoconsole = False
            except IOError:
                print (sys.exc_value)
                print ("Switching to console only output...\n")
                self.logtofile = False
                self.logtoconsole = True

    def write(self, data):
        if self.logtoconsole == True:
            self.con.write(data)
        if self.logtofile == True:
            self.f.write(data)
        sys.stdout.flush()

def getTimeString():
        return time.strftime("%Y-%m-%d", time.gmtime())

def runCommand(command):
    '''
    Execute a command in new thread and return the
    stdout and stderr content of it.
    '''
    try:
        Output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
    except Exception as e:
        print ("runCommand failed :%s" % (command))
        print (str(e))
        sys.stdout.flush()
        return None
    return Output

def GetOs():
    Os = ""
    if sys.platform.startswith('win32'):
        Os = "win"
    elif sys.platform.startswith('linux'):
        Os = "linux"
    elif sys.platform.startswith('darwin'):
        Os = "mac"
    return Os


def check_output(*popenargs, **kwargs):
    try:
        if 'stdout' in kwargs: 
            raise ValueError('stdout argument not allowed, it will be overridden.') 

        # Get start time.
        startTime = datetime.datetime.now()
        timeoutValue=3600

        cmd = popenargs[0]

        if sys.platform.startswith('win32'):
            process = subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True) 
        elif sys.platform.startswith('linux'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 
        elif sys.platform.startswith('darwin'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 

        stdoutdata, stderrdata = process.communicate( timeout = timeoutValue )
        retcode = process.poll()

        ####################################
        # Catch crash error and log it.
        ####################################
        OutputHandle = None
        try:
            if retcode >= 1:
                OutputHandle = OutputManager( 'CrashJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
                print (stdoutdata)
                print (stderrdata)
                sys.stdout.flush()
        except Exception as e:
            print (str(e))

    except subprocess.TimeoutExpired:
            ####################################
            # Catch time out error and log it.
            ####################################
            Os = GetOs()
            if Os == 'win':
                killCmd = "taskkill /FI \"IMAGENAME eq {0}\" /T /F"
            elif Os == 'linux':
                killCmd = "pkill {0)"
            elif Os == 'mac':
                # Linux, Mac OS
                killCmd = "killall -KILL {0}"

            runCommand(killCmd.format("java"))
            runCommand(killCmd.format("YouApp"))

            OutputHandle = None
            try:
                OutputHandle = OutputManager( 'KillJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
            except Exception as e:
                print (str(e))
    except Exception as e:
            for frame in traceback.extract_tb(sys.exc_info()[2]):
                        fname,lineno,fn,text = frame
                        print "Error in %s on line %d" % (fname, lineno)

এটি একটি ঘৃণা
কোরি গোল্ডবার্গ

-2

সহজ কিছু লেখার চেষ্টা করছিলাম।

#!/usr/bin/python

from subprocess import Popen, PIPE
import datetime
import time 

popen = Popen(["/bin/sleep", "10"]);
pid = popen.pid
sttime = time.time();
waittime =  3

print "Start time %s"%(sttime)

while True:
    popen.poll();
    time.sleep(1)
    rcode = popen.returncode
    now = time.time();
    if [ rcode is None ]  and  [ now > (sttime + waittime) ] :
        print "Killing it now"
        popen.kill()

সময়.স্লিপ (1) খুব খারাপ ধারণা - কল্পনা করুন যে আপনি অনেকগুলি কমান্ড চালাতে চান যা প্রায় 0.002 সেকেন্ড লাগে। পোল করার সময় আপনার অপেক্ষা করা উচিত () (দেখুন নির্বাচন করুন, লিনাক্স এপোলের পুনরায় সংশোধনের জন্য :)
ddzialak
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.