Soluzione generica per impedire l'esecuzione parallela di un lavoro cron lungo?


27

Sto cercando una soluzione semplice e generica che ti consenta di eseguire qualsiasi script o applicazione in crontab e impedirne l'esecuzione due volte.

La soluzione dovrebbe essere indipendente dal comando eseguito.

Presumo che dovrebbe apparire come il lock && (command ; unlock)blocco restituirà falso se ci fosse un altro blocco.

La seconda parte sarebbe come se avesse acquisito il blocco, esegui comando e sblocca dopo l'esecuzione del comando, anche se restituiva un errore.

Risposte:


33

Dai un'occhiata al pacchetto run-oneInstalla run-one . Dalla manpage per il run-onecomandoIcona Manpage :

run-one è uno script wrapper che non esegue più di un'istanza univoca di alcuni comandi con un set univoco di argomenti.

Questo è spesso utile con cronjobs, quando non si desidera eseguire più di una copia alla volta.

Come timeo sudo, basta anteporre al comando. Quindi un cronjob potrebbe apparire come:

  */60 * * * *   run-one rsync -azP $HOME example.com:/srv/backup

Per ulteriori informazioni e informazioni, controlla il post sul blog che lo presenta da Dustin Kirkland.


5

Un modo molto semplice di impostare una serratura:

if mkdir /var/lock/mylock; then
  echo "Locking succeeded" >&2
else
  echo "Lock failed - exit" >&2
  exit 1
fi

Uno script che vuole eseguire deve creare il blocco. Se il blocco esiste, un altro script è occupato, quindi il primo script non può essere eseguito. Se il file non esiste, nessuno script ha acquisito il blocco. Quindi lo script corrente acquisisce il blocco. Al termine dello script, è necessario rilasciare il blocco rimuovendolo.

Per ulteriori informazioni sui blocchi bash, consultare questa pagina


1
Avrai anche bisogno di una trappola EXIT che rimuova il blocco all'uscita. echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
geirha,

1
Idealmente, si desidera utilizzare il gruppo consultivo da un processo che esegue il comando desiderato come sottoattività. In questo modo, se tutti muoiono, il gregge viene rilasciato automaticamente, cosa che non funziona usando la presenza di un file di blocco. L'uso di una porta di rete funzionerebbe in modo simile, anche se questo è uno spazio dei nomi più piccolo, il che è un problema.
Alex North-Keys

3

Non è necessario installare alcun pacchetto di fantasia:

#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"

È più veloce scrivere quello script da solo che eseguire "apt-get install", non è vero? Potresti voler aggiungere "-u $ (id -u)" a pgrep per verificare le istanze eseguite solo dall'utente corrente.


2
questo non garantisce una singola istanza. due script possono passare contemporaneamente all'altro lato ||dell'operatore, prima che l'uno o l'altro abbiano ancora la possibilità di avviarlo.
Sedat Kapanoglu,

@SedatKapanoglu Concesso, quello script non è a prova di competizione, ma la domanda originale era sui cron-job di lunga durata (che vengono eseguiti al massimo una volta al minuto). Se il tuo sistema richiede più di un minuto per la creazione del processo, hai altri problemi. Tuttavia, se necessario per altri motivi, è possibile utilizzare flock (1) per proteggere lo script sopra dalle condizioni di gara.
Michael Kowhan,

L'ho usato ma per uno script bash che dovrebbe controllarsi. Il codice è questo: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && exit 2
ahofmann

3

Vedi anche Tim Kay solo, che esegue il blocco vincolando una porta su un indirizzo di loopback univoco per l'utente:

http://timkay.com/solo/

Nel caso in cui il suo sito non funzioni:

Uso:

solo -port=PORT COMMAND

where
    PORT        some arbitrary port number to be used for locking
    COMMAND     shell command to run

options
    -verbose    be verbose
    -silent     be silent

Usalo in questo modo:

* * * * * solo -port=3801 ./job.pl blah blah

script:

#!/usr/bin/perl -s
#
# solo v1.7
# Prevents multiple cron instances from running simultaneously.
#
# Copyright 2007-2016 Timothy Kay
# http://timkay.com/solo/
#
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation;
#    either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option)
#    any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or
#
# b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or
#
# c) the MIT License (http://opensource.org/licenses/MIT)
#

use Socket;

alarm $timeout                              if $timeout;

$port =~ /^\d+$/ or $noport                     or die "Usage: $0 -port=PORT COMMAND\n";

if ($port)
{
    # To work with OpenBSD: change to
    # $addr = pack(CnC, 127, 0, 1);
    # but make sure to use different ports across different users.
    # (Thanks to  www.gotati.com .)
    $addr = pack(CnC, 127, $<, 1);
    print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n"   if $verbose;

    $^F = 10;           # unset close-on-exec

    socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp'))       or die "socket: $!";
    bind(SOLO, sockaddr_in($port, $addr))               or $silent? exit: die "solo($port): $!\n";
}

sleep $sleep if $sleep;

exec @ARGV;

Per Mac OSX, ciò non funzionerà con Errore a solo(3801): Can't assign requested addressmeno che non si imponga a 0per il 3o parametro del metodo pack. Ciò che è buono per il BSD è buono anche per il Mac.
Eric Leschinski il

1

Hai bisogno di una serratura. run-onefa il lavoro, ma si può anche voler guardare in flockda util-linuxconfezione.

È un pacchetto standard fornito dagli sviluppatori del kernel, consente una maggiore personalizzazione di run-oneed è ancora molto semplice.


0

Una semplice soluzione di bash-hackers.org che ha funzionato per me stava usando mkdir . Questo è un modo semplice per assicurarsi che sia in esecuzione solo un'istanza del programma. Crea una directory con mkdir .lock che ritorna

  • vero se la creazione ha avuto successo e
  • false se esiste il file di blocco, a indicare che è attualmente in esecuzione un'istanza.

Quindi questa semplice funzione ha fatto tutta la logica di blocco dei file:

if mkdir .lock; then
    echo "Locking succeeded"
    eval startYourProgram.sh ;
else
    echo "Lock file exists. Program already running? Exit. "
    exit 1
fi


echo "Program finished, Removing lock."
rm -r .lock

0

Questa soluzione è per uno script bash che deve controllare se stesso

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.