10

How can I block a cron job when the previous run was not finished. Its a cron job which is running every 5 minutes, but sometimes it needs more than 5 minutes to run.

Edit The script which is called, crashes sometimes! So it can not delete lock file.

1

7 Answers 7

18

Use flock(1):

NAME
       flock - manage locks from shell scripts

SYNOPSIS
       flock [options] <file> -c <command>
       flock [options] <directory> -c <command>
       flock [options] <file descriptor number>
[...]

You can have it block or immediately exit. Pre-existence (or absence) of the lock file doesn't make a difference. It creates the file if needed and uses a flock() system call to lock the file. This lock is automatically released at process death.

For example, in cron:

*/5 * * * * /usr/bin/flock /tmp/my.lock /usr/local/bin/myjob

If your jobs don't always finish in 5 minutes, you might consider using --timeout so that your jobs don't queue up:

flock --timeout=300

Or use --nonblock to exit immediately -- it would be similar to --timeout=0

If your script is a shell script, you can use some clever redirection tricks to use flock within your script itself:

(
  flock -n 9 || exit 1
  # ... commands executed under lock ...
) 9>/var/lock/mylockfile            

Or, the manual also suggests making your script recursive by putting this at the top (it uses the script itself as its lock file):

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

See the flock(1) man page for more information

2
  • 2
    I suggest always using --nonblock to avoid queue of process waiting for execution.
    – vitorafsr
    May 3, 2020 at 16:48
  • This is genius. Thank you, thank you, thank you.
    – oemb1905
    Jun 14, 2023 at 4:17
4

Pid files are the way to go, however, much like init scripts don't just give up when they see a pid file, you should check to ensure that the pid in the file still exists.

Something like this would do the trick:

PIDFILE=/var/run/myscript.pid

if [ -e "$PIDFILE" ] ; then
    # our pidfile exists, let's make sure the process is still running though
    PID=`/bin/cat "$PIDFILE"`
    if /bin/kill -0 "$PID" > /dev/null 2>&1 ; then
        # indeed it is, i'm outta here!
        /bin/echo 'The script is still running, forget it!'
        exit 0
    fi
 fi

 # create or update the pidfile
 /bin/echo "$$" > $PIDFILE

 ... do stuff ...

 /bin/rm -f "$PIDFILE"
1
  • +1 Yours is a good approach, but it's still possible (however rare) that the both the pidfile and the PID could exist but not be related. Instead of kill -0 you could do if [ "$(ps -p $PID o cmd=)" = "$scriptname" ] or if [ "$(pgrep "$scriptname")" = "$PID" ]. Nov 9, 2010 at 14:09
1

Checking for pid or lock file can fail when the cron job fails to clean the file after exit. I see the better option is to check for the process itself such as:

ps -ef | grep script_name | grep -v grep | wc -l

This will give you the number of processes that have the name 'script_name'. You can check this count at the beginning of your script execution.

3
  • Ok, this solution will work when the script crashes?!
    – powtac
    Nov 9, 2010 at 13:22
  • pgrep is less likely to give false positives. Or you could use ps -e -o cmd= | grep "^$scriptname". Nov 9, 2010 at 14:14
  • Better yet is to wrap the first letter of the grep search term in square braces, such as 'ps ax | grep [f]oobar'. The process list will duplicate the braces, but will filter out on the grep as not matching the regex.
    – Magellan
    Jul 2, 2014 at 5:47
0

You can't, cronjobs don't come with this functionality. Put something in your script to check for it, or have it create a file when finished and use something such as inotifywait to block.

0

Look for a file in e.g. /var/run. If the file is found then exit. Otherwise, create the file, run your routine, and then remove the file.

0

There are many ways. Popular one is to use a lock file.

1) When the script starts, it checks if a lock file (like /var/lock/powtac.loc) already presents.

2) If it does exist, script aborts, assuming the previous script is still running *).

3) If it does not exist, script creates the file and continues to do whatever it needs to do.

4) Upon exit, script deletes the lock file.

*) It would also be possible to check the process list at this point, search for matches for your cron job and if found, see when they were started. If recently, then assume they are still running, if loooooooooong time ago, then assume they were stuck.

-2

I wouldn't have two cron jobs I'd have one.

* * * * * /some/command && /some/other/command

It will only run if the first command completes successfully, and only after it's done.

( note: if you need it to be a few minutes after use sleep )

4
  • question implies it's the same task but it sometimes takes longer than the delay to run. Doing it your way, he'd just have your command running every 10 minutes instead of his command twice every 5 minutes.
    – James L
    Nov 9, 2010 at 13:13
  • bagh...then he's just running it too often, imo. I read it wrong... but I'm still leaving my answer. Nov 9, 2010 at 13:15
  • 1
    Not all cronjobs run in deterministic lengths of time, particularly ones like rsyncing from a mirror. Nov 9, 2010 at 13:35
  • @jgoldshrafe and you think rsyncing a mirror every 5 minutes is a good idea? nothing runs in a deterministic length of time, everything is variable Nov 9, 2010 at 13:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .