399 votes

Exécution d'un cron toutes les 30 secondes

D'accord, donc j'ai un cron que je dois exécuter toutes les 30 secondes.

Voici ce que j'ai:

*/30 * * * * /bin/bash -l -c 'cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\'''

Il s'exécute, mais est-ce qu'il s'exécute toutes les 30 minutes ou 30 secondes?

De plus, j'ai lu que cron pourrait ne pas être le meilleur outil à utiliser si je l'exécute aussi souvent. Y a-t-il un autre outil meilleur que je peux utiliser ou installer sur Ubuntu 11.04 qui serait une meilleure option? Y a-t-il un moyen de corriger le cron ci-dessus?

0 votes

CommaToast, et que se passe-t-il si votre application Javascript ou Java tombe en panne pour une raison quelconque et se ferme? Comment va-t-elle redémarrer? :-)

18 votes

Ajoutez une petite application NodeJS, lol. Pourquoi pas une petite application c++? Pendant que nous y sommes, nous pouvons l'appeler 'cron' et l'exécuter en tant que service.

3 votes

842voto

paxdiablo Points 341644

Vous avez */30 dans le spécificateur des minutes - cela signifie toutes les minutes mais avec un pas de 30 (en d'autres termes, toutes les demi-heures). Comme cron ne descend pas aux résolutions sous-minute, vous devrez trouver une autre solution.

Une possibilité, bien que ce soit un peu bricolage(a), est d'avoir deux tâches, une décalée de 30 secondes:

# Besoin de les exécuter toutes les 30 secondes, garder les commandes synchronisées.
* * * * *              /path/to/executable param1 param2
* * * * * ( sleep 30 ; /path/to/executable param1 param2 )

Vous pouvez remarquer que j'ai ajouté des commentaires et formaté pour s'assurer qu'ils restent synchronisés.

Les deux tâches de cron s'exécutent en réalité chaque minute, mais la seconde tâche attendra une demi-minute avant d'exécuter la partie essentielle de la tâche, /path/to/executable.

Pour d'autres options (non basées sur cron), consultez les autres réponses ici, en particulier celles mentionnant fcron et systemd. Ce sont probablement à privilégier si votre système a la capacité de les utiliser (comme installer fcron ou avoir une distribution avec systemd dedans).


Si vous ne voulez pas utiliser la solution bricolage, vous pouvez utiliser une solution basée sur une boucle avec une petite modification. Vous devrez toujours gérer la façon de maintenir votre processus actif d'une certaine manière, mais une fois que c'est réglé, le script suivant devrait fonctionner:

#!/bin/env bash

# Code de débogage pour démarrer sur une minute précise
# et augmenter progressivement la durée maximale de charge
# pour voir ce qui se passe lorsque la charge dépasse 30 secondes.

((maxtime = 20))
while [[ "$(date +%S)" != "00" ]]; do true; done

while true; do
    # Démarrer un minuteur en arrière-plan AVANT que la charge s'exécute.

    sleep 30 &

    # Exécutez la charge, avec une durée aléatoire jusqu'à la limite.
    # Ligne vide supplémentaire si la charge est excédentaire.

    ((delay = RANDOM % maxtime + 1))
    ((maxtime += 1))
    echo "$(date) En train de dormir pendant ${delay} secondes (max ${maxtime})."
    [[ ${delay} -gt 30 ]] && echo
    sleep ${delay}

    # Attendre la fin du minuteur avant le prochain cycle.

    wait
done

Le truc consiste à utiliser un sleep 30 mais de le démarrer en arrière-plan avant que votre charge s'exécute. Ensuite, après que la charge soit terminée, attendez simplement que le sleep en arrière-plan se termine.

Si la charge prend n secondes (où n <= 30), l'attente après la charge sera alors de 30 - n secondes. Si elle prend plus de 30 secondes, alors le prochain cycle sera retardé jusqu'à la fin de la charge, mais pas plus.

Vous remarquerez que j'ai inclus du code de débogage pour démarrer sur une minute précise afin de faciliter le suivi initial de la sortie. J'augmente également progressivement le temps maximum de charge pour que vous puissiez éventuellement voir la charge dépasser le cycle de 30 secondes (une ligne vide supplémentaire est affichée pour que l'effet soit évident).

Un exemple d'exécution suit (où les cycles commencent normalement 30 secondes après le cycle précédent):

Mar 26 Mai 20:56:00 AWST 2020 En train de dormir pendant 9 secondes (max 21).
Mar 26 Mai 20:56:30 AWST 2020 En train de dormir pendant 19 secondes (max 22).
Mar 26 Mai 20:57:00 AWST 2020 En train de dormir pendant 9 secondes (max 23).
Mar 26 Mai 20:57:30 AWST 2020 En train de dormir pendant 7 secondes (max 24).
Mar 26 Mai 20:58:00 AWST 2020 En train de dormir pendant 2 secondes (max 25).
Mar 26 Mai 20:58:30 AWST 2020 En train de dormir pendant 8 secondes (max 26).
Mar 26 Mai 20:59:00 AWST 2020 En train de dormir pendant 20 secondes (max 27).
Mar 26 Mai 20:59:30 AWST 2020 En train de dormir pendant 25 secondes (max 28).
Mar 26 Mai 21:00:00 AWST 2020 En train de dormir pendant 5 secondes (max 29).
Mar 26 Mai 21:00:30 AWST 2020 En train de dormir pendant 6 secondes (max 30).
Mar 26 Mai 21:01:00 AWST 2020 En train de dormir pendant 27 secondes (max 31).
Mar 26 Mai 21:01:30 AWST 2020 En train de dormir pendant 25 secondes (max 32).
Mar 26 Mai 21:02:00 AWST 2020 En train de dormir pendant 15 secondes (max 33).
Mar 26 Mai 21:02:30 AWST 2020 En train de dormir pendant 10 secondes (max 34).
Mar 26 Mai 21:03:00 AWST 2020 En train de dormir pendant 5 secondes (max 35).
Mar 26 Mai 21:03:30 AWST 2020 En train de dormir pendant 35 secondes (max 36).

Mar 26 Mai 21:04:05 AWST 2020 En train de dormir pendant 2 secondes (max 37).
Mar 26 Mai 21:04:35 AWST 2020 En train de dormir pendant 20 secondes (max 38).
Mar 26 Mai 21:05:05 AWST 2020 En train de dormir pendant 22 secondes (max 39).
Mar 26 Mai 21:05:35 AWST 2020 En train de dormir pendant 18 secondes (max 40).
Mar 26 Mai 21:06:05 AWST 2020 En train de dormir pendant 33 secondes (max 41).

Mar 26 Mai 21:06:38 AWST 2020 En train de dormir pendant 31 secondes (max 42).

Mar 26 Mai 21:07:09 AWST 2020 En train de dormir pendant 6 secondes (max 43).

Si vous voulez éviter la solution bricolage, c'est probablement mieux. Vous aurez toujours besoin d'une tâche de cron (ou équivalent) pour détecter périodiquement si ce script est en cours d'exécution et, si ce n'est pas le cas, le démarrer. Mais le script lui-même gère ensuite le timing.


(a) Certains de mes collègues diraient que les bricolages sont ma spécialité :-)

38 votes

C'est une excellente solution de contournement, tellement que je pense qu'elle transcende son bricolage.

0 votes

Ne fonctionnerait-ce pas aussi ? * * * * * ( /chemin/vers/exécutable param1 param2; sleep 30 ; /chemin/vers/exécutable param1 param2 )

16 votes

@rubo77, seulement si cela a pris moins d'une seconde pour s'exécuter :-) S'il a fallu 29 secondes, cela se produirait à 0:00:00, 0:00.59, 0:01:00, 0:01:59 et ainsi de suite.

78voto

wildplasser Points 17900

Vous ne pouvez pas. Cron a une granularité de 60 secondes.

* * * * * cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\''
* * * * * sleep 30 && cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\''

0 votes

Ce syntaxe est-elle équivalente à celle de paxdiablo? Ou y a-t-il des différences subtiles?

0 votes

La différence est : j'ai utilisé le chemin d'accès original vers le binaire. @paxdiablo a utilisé une sorte de méta-syntaxe. (et invoque un sous-shell)

2 votes

Je voulais dire, utiliser && au lieu de ( ; ).

39voto

Marvin Pinto Points 8292

La granularité de Cron est en minutes et n'a pas été conçue pour se réveiller toutes les x secondes pour exécuter quelque chose. Exécutez votre tâche répétitive dans une boucle et elle devrait faire ce dont vous avez besoin:

#!/bin/env bash
while [ true ]; do
 sleep 30
 # faites ce dont vous avez besoin ici
done

64 votes

Gardez à l'esprit que ce n'est pas tout à fait la même chose. Si le travail prend 25 secondes (par exemple), il démarrera toutes les 55 secondes au lieu de toutes les 30 secondes. Cela peut ne pas être important, mais vous devriez être conscient des conséquences possibles.

7 votes

Vous pourriez exécuter le travail en arrière-plan, puis il se déroulera en presque exactement 30 secondes.

1 votes

While [true]do sleep 30 # do what you need to here done --------- done should be in small case

16voto

Pankaj Shinde Points 124

La tâche Cron ne peut pas être utilisée pour planifier une tâche à intervalles de secondes. C'est-à-dire que vous ne pouvez pas planifier une tâche Cron pour s'exécuter toutes les 5 secondes. La solution alternative est d'écrire un script shell qui utilise la commande sleep 5.

Créez un script shell every-5-seconds.sh en utilisant une boucle while bash comme indiqué ci-dessous.

$ cat every-5-seconds.sh
#!/bin/bash
while true
do
 /home/ramesh/backup.sh
 sleep 5
done

Ensuite, exécutez ce script shell en arrière-plan en utilisant nohup comme indiqué ci-dessous. Cela continuera à exécuter le script même après la déconnexion de votre session. Cela exécutera votre script shell backup.sh toutes les 5 secondes.

$ nohup ./every-5-seconds.sh &

8 votes

Le temps va dériver. Par exemple, si backup.sh prend 1,5 secondes pour s'exécuter, il s'exécutera toutes les 6,5 secondes. Il existe des moyens d'éviter cela, par exemple sleep $((5 - $(date +%s) % 5))

0 votes

Je suis nouveau dans nohup, en exécutant votre exemple, nohup renvoie 'aucun fichier ou dossier de ce type'. Après quelques recherches, vous semblez avoir oublié 'sh' après nohup. Comme ceci: $ nohup sh ./every-5-seconds.sh &

0 votes

C'est plus qu'une simple dérive temporelle, c'est une exécution double et triple car crontab démarrera un nouveau script tandis que l'autre est toujours en cours d'exécution.

14voto

shlomia Points 151

Vous pouvez consulter ma réponse à cette question similaire

Fondamentalement, j'ai inclus là un script bash nommé "runEvery.sh" que vous pouvez exécuter avec cron toutes les 1 minute et passer comme arguments la commande réelle que vous souhaitez exécuter et la fréquence en secondes à laquelle vous souhaitez l'exécuter.

quelque chose comme ça

* * * * * ~/bin/runEvery.sh 5 myScript.sh

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X