टाइमआउट के साथ मॉड्यूल 'सबप्रोसेस' का उपयोग करना


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
Python2.x के लिए pypi.python.org/pypi/subprocess32 का उपयोग करें । यह पाइथन 3.x का बैकपोर्ट है। इसमें कॉल () और प्रतीक्षा () के लिए टाइमआउट तर्क है।
गुएतली

1
pypi.python.org/pypi/subprocess32 विंडोज पर काम नहीं करता है :(
adrianX

जवाबों:


170

पायथन में 3.3+:

from subprocess import STDOUT, check_output

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

output एक बाइट स्ट्रिंग है जिसमें कमांड मर्ज किया गया स्टडआउट, स्टैडर डेटा है।

check_outputविधि के CalledProcessErrorविपरीत प्रश्न के पाठ में निर्दिष्ट गैर-शून्य निकास स्थिति पर उठता है proc.communicate()

मैंने हटा दिया है shell=Trueक्योंकि यह अक्सर अनावश्यक रूप से उपयोग किया जाता है। आप हमेशा इसे वापस जोड़ सकते हैं यदि cmdवास्तव में इसकी आवश्यकता है। यदि आप जोड़ते हैं shell=True, यदि बच्चा प्रक्रिया अपने स्वयं के वंशज पैदा करता है; check_output()टाइमआउट इंगित करने की तुलना में बहुत बाद में लौट सकते हैं, सबप्रोसेस टाइमआउट विफलता देखें ।

टाइमआउट फीचर subprocess323.2+ सबप्रोसेस मॉड्यूल के बैकपोर्ट के माध्यम से पायथन 2.x पर उपलब्ध है ।


17
वास्तव में, और सबप्रोसेस टाइमआउट समर्थन उपप्रकार 32
gps

8
@gps श्रीधर क्रॉस प्लेटफॉर्म समाधान के लिए कहा, अपने backport केवल POSIX का समर्थन करता है, जबकि: जब मैं इसे बाहर की कोशिश की, MSVC शिकायत की (उम्मीद) unistd.h लापता के बारे में :)
Shmil बिल्ली

यदि आपको आउटपुट की आवश्यकता नहीं है, तो आप बस सबप्रोसेस.का उपयोग कर सकते हैं।
काइल गिब्सन

Python3.5 के बाद से, कैप्चर_आउटपुट = ट्रू के साथ सबप्रोसेस .run () का उपयोग करें और उपयोगफॉल आउटपुट प्राप्त करने के लिए एन्कोडिंग पैरामीटर का उपयोग करें।
MKesper

1
@Mesperper: 1- check_output()आउटपुट पाने का पसंदीदा तरीका है (यह आउटपुट को सीधे लौटाता है, त्रुटियों को अनदेखा नहीं करता है, यह हमेशा के लिए उपलब्ध है)। 2- run()अधिक लचीला है, लेकिन run()डिफ़ॉल्ट रूप से त्रुटि को अनदेखा करता है और आउटपुट प्राप्त करने के लिए अतिरिक्त चरणों की आवश्यकता होती है 3- के check_output()संदर्भ में कार्यान्वित किया जाता हैrun() और इसलिए यह अधिकांश समान तर्कों को स्वीकार करता है। 4- capture_output
नाइट

205

मुझे निम्न स्तर के विवरण के बारे में ज्यादा जानकारी नहीं है; लेकिन, यह देखते हुए कि अजगर 2.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। मैंने इसे linux और windows 7 (cygwin and plain windows python) दोनों पर चलाया है - तीनों मामलों में उम्मीद के मुताबिक काम करता है।
फ़ूजी

7
मैंने आपके कोड को थोड़ा संशोधित किया है ताकि देशी पोपेन क्वार्ग्स को पास कर सकें और इसे जीस्ट पर रख सकें। यह अब बहु प्रयोजन का उपयोग करने के लिए तैयार है; gist.github.com/1306188
13

2
किसी को भी समस्या हो रही थी @redice के लिए, यह प्रश्न मदद कर सकता है। संक्षेप में, यदि आप शेल = ट्रू का उपयोग करते हैं, तो शेल उस बच्चे की प्रक्रिया बन जाता है जो मारा जाता है, और इसकी कमांड (बच्चे की प्रक्रिया का बच्चा) रहता है।
अनसन

6
यह उत्तर मूल की समान कार्यक्षमता प्रदान नहीं करता है क्योंकि यह स्टडआउट वापस नहीं करता है।
Stephenbez

2
thread.is_alive एक दौड़ की स्थिति पैदा कर सकता है। देखें ostricher.com/2015/01/python-subprocess-with-time
ChaimKut

132

jcollado के जवाब का उपयोग कर सरल किया जा सकता threading.Timer वर्ग:

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)
21

3
+1 यह स्वीकृत उत्तर होना चाहिए, क्योंकि इसमें उस तरीके की आवश्यकता नहीं है जिस प्रक्रिया को बदलने के लिए लॉन्च किया गया है।
डेव ब्रैंटन

1
इसे लंबोदर की आवश्यकता क्यों है? पी.बी.एल। विधि को वहाँ लंबोदर के बिना इस्तेमाल नहीं किया जा सकता है?
डैनी स्टेपल

//, क्या आप इस के उपयोग का एक उदाहरण शामिल करने के लिए तैयार हैं?
नाथन बसानी

1
@tuk 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
यह समाधान only_if signal.signal (signal.SIGALARM, Alarm_handler) काम करेगा मुख्य थ्रेड से कहा जाता है। सिग्नल के लिए प्रलेखन देखें
volatilevoid 5

दुर्भाग्य से, जब अपाचे मॉड्यूल (जैसे mod_python, mod_perl, या mod_php) के संदर्भ में (लिनक्स पर) चल रहा है, तो मैंने संकेतों और अलार्म के उपयोग को अस्वीकार कर दिया है (संभवतः वे अपाचे के अपने आईपीसी तर्क के साथ हस्तक्षेप करते हैं)। इसलिए एक समय सीमा के लक्ष्य को प्राप्त करने के लिए मुझे "पैरेंट लूप्स" लिखने के लिए मजबूर किया गया है, जो एक बच्चे की प्रक्रिया शुरू करते हैं और फिर घड़ी को देखते हुए "स्लीप" y लूप में बैठते हैं (और संभवतः बच्चे से आउटपुट की निगरानी भी करते हैं)।
पीटर

44

यहां उचित प्रक्रिया की हत्या के साथ मॉड्यूल के रूप में एलेक्स मार्टेली का समाधान है। अन्य दृष्टिकोण काम नहीं करते हैं क्योंकि वे proc.communicate () का उपयोग नहीं करते हैं। इसलिए यदि आपके पास एक ऐसी प्रक्रिया है जो बहुत सारे आउटपुट का उत्पादन करती है, तो यह अपने आउटपुट बफर को भर देगी और तब तक ब्लॉक करेगी जब तक आप इससे कुछ नहीं पढ़ते।

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
यह कभी-कभी अपवाद के रूप में होता है जब कोई अन्य हैंडलर SIGALARM पर खुद को पंजीकृत करता है और इस प्रक्रिया को मारता है इससे पहले कि कोई "मार" पाता है, काम के चारों ओर जोड़ा जाता है। BTW, महान नुस्खा! मैंने इसे अभी तक 50,000 बगगी प्रक्रियाओं को लॉन्च करने के लिए उपयोग किया है, बिना फ्रीजर को कवर किए हुए या क्रैश किए बिना।
यारोस्लाव बुलटोव

इसे थ्रेडेड एप्लिकेशन में चलाने के लिए कैसे संशोधित किया जा सकता है? मैं कार्यकर्ता धागे के भीतर से इसका इस्तेमाल करते हैं और प्राप्त करने के लिए कोशिश कर रहा हूँValueError: signal only works in main thread
विम

@Yaroslav Bulatov जानकारी के लिए धन्यवाद। बताई गई समस्या से निपटने के लिए आपने क्या हल जोड़ा था?
jpswain

1
बस "प्रयास करें, पकड़ें" ब्लॉक जोड़ा गया, यह कोड के अंदर है। बीटीडब्ल्यू, लंबे समय में, यह मुझे समस्याएं देने के लिए निकला क्योंकि आप केवल एक सिगलर हैंडलर सेट कर सकते हैं, और अन्य प्रक्रियाएं इसे रीसेट कर सकती हैं। इसका एक समाधान यहाँ दिया गया है - stackoverflow.com/questions/6553423/…
यारोस्लाव बुलटोव

18

मैंने sussudio उत्तर को संशोधित किया है । अब रिटर्न काम करते हैं: ( 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मैक उपयोगकर्ताओं के लिए के माध्यम से coreutils में gtimeout के रूप में भी उपलब्ध है ।


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पैरामीटर का उल्लेख करता है । हालांकि यह उल्लेख एक बार और चोट नहीं होगा।
दोपहर

//, मुझे लगता है कि ओपी पुराने अजगर के लिए एक समाधान की तलाश कर रहा है।
नाथन बसानी

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()

अधूरा लगता है - टेम्पोफाइल क्या है?
मकड़ी

"पॉप टेम्पल", "इंपोर्ट टाइम" और "शेल = ट्रू" को "पॉपेन" कॉल ("शेल = ट्रू" से सावधान रहें) में शामिल करें!
एडुआर्डो लुसियो

11

मुझे नहीं पता कि इसका उल्लेख क्यों नहीं किया गया है, लेकिन पायथन 3.5 के बाद से, एक नया subprocess.runसार्वभौमिक आदेश है (जिसका अर्थ है प्रतिस्थापित करना check_call, check_output...) और जिसमें timeoutपैरामीटर भी है।

subprocess.run (args, *, stdin = कोई नहीं, इनपुट = कोई नहीं, stdout = कोई नहीं, stderr = कोई नहीं, शेल = गलत, cwd = कोई नहीं, टाइमआउट = कोई नहीं, चेक = गलत, एन्कोडिंग = कोई नहीं, त्रुटियां = कोई नहीं)

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

मैं जिस समाधान का उपयोग कर रहा हूं वह शेल कमांड को टाइमलीमिट के साथ उपसर्ग करना है । यदि कॉमैंड बहुत लंबा हो जाता है, तो timelimit इसे रोक देगा और Popen को timelimit द्वारा एक रिटर्नकोड सेट किया जाएगा। यदि यह> 128 है, तो इसका मतलब है कि समयबद्धता ने इस प्रक्रिया को मार दिया।

टाइमआउट और बड़े आउटपुट (> 64K) के साथ अजगर उपप्रकार भी देखें


मैं एक समान टूल का उपयोग करता हूं जिसे timeout- package.ubuntu.com/search?keywords=timeout - कहा जाता है और न ही विंडोज पर काम करता है, क्या वे करते हैं?
श्रीधर रत्नाकुमार

5

मैंने jcolladoअपने पायथन मॉड्यूल ईजीप्रोसेस से थ्रेडिंग के साथ समाधान जोड़ा ।

इंस्टॉल:

pip install easyprocess

उदाहरण:

from easyprocess import Proc

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

Easyprocess मॉड्यूल ( code.activestate.com/pypm/easyprocess ) ने मेरे लिए काम किया, यहां तक ​​कि मल्टीप्रोसेसिंग से भी इसका उपयोग किया।
iChux

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()समारोह के निशान एक धागा समाप्त, लेकिन वास्तव में धागा समाप्त नहीं करता है! मैं इसे * nix में सत्यापित कर सकता हूं, लेकिन मेरे पास परीक्षण करने के लिए Windows कंप्यूटर नहीं है।
डिटानचेन

2

एक बार जब आप * यूनिक्स में फुल प्रोसेस रनिंग मशीनरी को समझ लेते हैं, तो आपको आसानी से सरल समाधान मिल जाएगा:

इस सरल उदाहरण पर विचार करें कि टाइमआउटेबल कम्युनिकेशन () मेथ का चयन select.select () (आजकल उपलब्ध हर अलथेरे * पर उपलब्ध) का उपयोग कैसे करें। यह भी epoll / poll / kqueue के साथ लिखा जा सकता है, लेकिन select.select () संस्करण आपके लिए एक अच्छा उदाहरण हो सकता है। और आपके कार्य के लिए select.select () (गति और 1024 अधिकतम fds) की प्रमुख सीमाएँ लागू नहीं होती हैं।

यह * निक्स के तहत काम करता है, थ्रेड्स नहीं बनाता है, सिग्नल का उपयोग नहीं करता है, किसी भी धागे (केवल मुख्य नहीं) से प्रशंसित किया जा सकता है, और मेरी मशीन (i5 2.3ghz) पर stdout से डेटा के 250mb / s पढ़ने के लिए पर्याप्त पर्याप्त है।

संचार के अंत में joinding 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
यह समस्या के केवल आधे यूनिक्स को संबोधित करता है।
Spaceghost

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

मैंने विंडोज, लिनक्स और मैक पर सफलतापूर्वक किलेबलप्रोसेस का उपयोग किया है । यदि आप Cygwin Python का उपयोग कर रहे हैं, तो आपको OSAF के किलएबलप्रोसेस के संस्करण की आवश्यकता होगी क्योंकि अन्यथा देशी विंडोज प्रक्रियाओं को नहीं मारा जाएगा।


ऐसा लगता है कि Killableprocess में Popen.communicate () कॉल का टाइमआउट नहीं जोड़ा गया है।
विम कॉइनन

1

हालाँकि मैंने इसे बड़े पैमाने पर नहीं देखा है, इस तरह के डेकोरेटर को मैंने एक्टिवस्टैट पर पाया है जो इस तरह की चीज़ के लिए काफी उपयोगी है। साथ ही subprocess.Popen(..., close_fds=True), कम से कम मैं अजगर में खोल-पटकथा के लिए तैयार हूँ।


यह डेकोरेटर सिग्नल.आलार्म का उपयोग करता है, जो विंडोज पर उपलब्ध नहीं है।
dbn

1

यह समाधान शेल के मामले में प्रोसेस ट्री को मारता है = सही है, प्रक्रिया के मापदंडों को पारित करता है (या नहीं), एक टाइमआउट है और कॉल बैक का स्टडआउट, स्टैडर और प्रोसेस आउटपुट प्राप्त करता है (यह किल_प्रो_ट्री के लिए psutil का उपयोग करता है)। यह jcollado सहित SO में पोस्ट किए गए कई समाधानों पर आधारित था। Jcollado के जवाब में Anson और jradice द्वारा टिप्पणियों के जवाब में पोस्टिंग। Windows Srr 2012 और Ubuntu 14.04 में परीक्षण किया गया। कृपया ध्यान दें कि उबंटू के लिए आपको parent.children (...) कॉल को parent.get_children (...) में बदलना होगा।

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_PROCESS_TERMINATED" का मुखौटा और कार्रवाई के आधार पर SUBPROCESS2_PROCESS_KILLED हो गया है (पूर्ण विवरण के लिए प्रलेखन देखें)


0

अजगर 2.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()

time.sleep (1) बहुत बुरा विचार है - कल्पना करें कि आप कई कमांड चलाना चाहते हैं जो लगभग 0.002 सेकेंड का होगा। आप बल्कि इंतजार करना चाहिए, जबकि चुनाव () (चयन को देखने लिनक्स Epol सिफारिश :) के लिए,
ddzialak
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.