tags:

views:

79

answers:

3

I want to using subprocess to run a program, but I need to limit the execution time. For example, if it runs more than 2 seconds, I want to kill it.

For common programs, kill() works well. But if I try to run "/usr/bin/time something", kill() can't really kill the program.

But my code below seems not works well, the program is still running. Please help me, thanks a lot.

import subprocess
import time

exec_proc = subprocess.Popen("/usr/bin/time -f \"%e\\n%M\" ./son > /dev/null", stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell = True)

max_time = 1
cur_time = 0.0
return_code = 0
while cur_time <= max_time:
    if exec_proc.poll() != None:
        return_code = exec_proc.poll()
        break
    time.sleep(0.1)
    cur_time += 0.1

if cur_time > max_time:
    exec_proc.kill()
A: 

do it like so in your command line:

perl -e 'alarm shift @ARGV; exec @ARGV' <timeout> <your_command>

this will run the command <your_command> and terminate it in <timeout> second.

a dummy example :

# set time out to 5, so that the command will be killed after 5 second 
command = ['perl', '-e', "'alarm shift @ARGV; exec @ARGV'", "5"]

command += ["ping", "www.google.com"]

exec_proc = subprocess.Popen(command)

or you can use the signal.alarm() if you want it with python but it's the same.

singularity
Answering a Python question with Perl, nice…
jleedev
It works well, but when I try "perl -e 'alarm shift @ARGV; exec @ARGV' 3 /usr/bin/time ping google.com", the program won't stop.
Xhacker Liu
So how to deal with /usr/bin/time some_command?
Xhacker Liu
why not do : __time perl -e 'alarm shift @ARGV; exec @ARGV' 3 ping google.com__ ???
singularity
@jleedev : what ??? Perl, python, ruby, shell .... programming language are just a tool, you can also comment on the answer that have been posted because it use shell !!!!!
singularity
Thanks, it works!
Xhacker Liu
+2  A: 

If you're using Python 2.6 or later, you can use the multiprocessing module.

from multiprocessing import Process

def f():
    # Stuff to run your process here

p = Process(target=f)
p.start()
p.join(timeout)
if p.is_alive():
    p.terminate()

Actually, multiprocessing is the wrong module for this task since it is just a way to control how long a thread runs. You have no control over any children the thread may run. As singularity suggests, using signal.alarm is the normal approach.

import signal
import subprocess

def handle_alarm(signum, frame):
    # If the alarm is triggered, we're still in the exec_proc.communicate()
    # call, so use exec_proc.kill() to end the process.
    frame.f_locals['self'].kill()

max_time = ...
stdout = stderr = None
signal.signal(signal.SIGALRM, handle_alarm)
exec_proc = subprocess.Popen(['time', 'ping', '-c', '5', 'google.com'],
                             stdin=None, stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
signal.alarm(max_time)
try:
    (stdout, stderr) = exec_proc.communicate()
except IOError:
    # process was killed due to exceeding the alarm
finally:
    signal.alarm(0)
# do stuff with stdout/stderr if they're not None
jamessan
Thanks, but it doesn't work :<
Xhacker Liu
# Try it. After 3 seconds, ping is still running.import subprocessimport timefrom multiprocessing import Processdef f(): exec_proc = subprocess.Popen("/usr/bin/time ping google.com", shell = True)p = Process(target=f)p.start()p.join(3)if p.is_alive(): p.terminate()time.sleep(10)
Xhacker Liu
You're right. `p.terminate()` only kills the thread, not any children that were spawned. I don't see why you're using `shell = True` so I'll update my answer with a solution using signal.alarm as singularity suggests.
jamessan
A: 

I use os.kill() but am not sure if it works on all OS. Pseudo code follows, and see link text Doug Hellman's page. (1)proc = subprocess.Popen(['google-chrome'])
(2)os.kill(proc.pid, signal.SIGUSR1)

Woooee