Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running python script with cron only if not running

Tags:

python

cron

flock

I need to run a python script (job.py) every minute. This script must not be started if it is already running. Its execution time can be between 10 seconds and several hours.

So I put into my crontab:

* * * * * root cd /home/lorenzo/cron && python -u job.py 1>> /var/log/job/log 2>> /var/log/job/err

To avoid starting the script when it is already running, I use flock().

This is the script (job.py):

import fcntl
import time
import sys

def doIncrediblyImportantThings ():
    for i in range (100):
        sys.stdout.write ('[%s] %d.\n' % (time.strftime ('%c'), i) )
        time.sleep (1)

if __name__ == '__main__':
    f = open ('lock', 'w')
    try: fcntl.lockf (f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except:
        sys.stderr.write ('[%s] Script already running.\n' % time.strftime ('%c') )
        sys.exit (-1)
    doIncrediblyImportantThings ()

This approach seems to work.

Is there anything I am missing? Are there any troubles I can run into using this approach?

Are there more advised or "proper" ways of achieving this behaviour?

I thank you for any suggestion.

like image 628
Hyperboreus Avatar asked May 27 '11 00:05

Hyperboreus


People also ask

Can I run python script from cron?

You'll learn in a bit what this table refers to. You should use Cron any time you want to automate something, like an OS job or a Python script. Needless to say, but an automated Python script can do basically anything. On Linux and macOS, the Crontab consists of six fields.

How do I stop a cron job after a certain time?

You could use a second cron job at 09pm to start a second program that tells the first program to terminate. There are a number of ways to do this. One of the easiest might be to have the second program touch terminate. txt in a convenient place.

How do you stop a python script from crontab?

Print a process tree by ps axjf and look over the "COMMAND" column for the script name respectively the "PID" column to get its PID. kill the corresponding process: kill placeholder_for_pid_of_your_script.


2 Answers

The only suggestion I would make is to make your exception handling a little more specific. You don't want to accidentally delete the fcntl import one day and hide the NameError that results. Always try to catch the most specific exception you want to handle. In this case, I suggest something like:

import errno

try:
    fcntl.lock(...)
except IOError, e:
    if e.errno == errno.EAGAIN:
        sys.stderr.write(...)
        sys.exit(-1)
    raise

This way, any other cause of the lock being unobtainable shows up (probably in your email since you're using cron) and you can decide if it's something for an administrator to look at, another case for the program to handle, or something else.

like image 184
Jean-Paul Calderone Avatar answered Oct 26 '22 13:10

Jean-Paul Calderone


You're in trouble when the machine reboots or freezes with the script running (and thus an active lock). Simple way to counter this is to use the @reboot cron timestamp to run rm /path/to/lock.

like image 3
Mel Avatar answered Oct 26 '22 14:10

Mel