174 votes

Exécution manuelle et immédiate d'une tâche cron

(J'ai déjà lu Comment puis-je tester un nouveau cron script ? .)

J'ai un problème spécifique (la tâche cron ne semble pas s'exécuter, ou s'exécuter correctement), mais le problème est général : J'aimerais déboguer les scripts qui sont cronnisés. Je suis conscient que je peux configurer une ligne * * * * * crontab, mais ce n'est pas une solution pleinement satisfaisante. J'aimerais pouvoir exécuter une tâche cron à partir de la ligne de commande comme si cron l'exécutait (même utilisateur, mêmes variables d'environnement, etc.). Existe-t-il un moyen de le faire ? Devoir attendre 60 secondes pour tester les changements de scripts n'est pas pratique.

0 votes

(désolé, je ne peux pas ajouter de commentaire) 0 30 16 20 * ? * même si vous exécutez le job comme ça, l'idée est de fournir une sortie script pour voir ce qui ne va pas à moins que le job n'écrive dans un log, c'est quuiet inutile.

111voto

Chris_O Points 392

Voici ce que j'ai fait, et cela semble fonctionner dans cette situation. Au moins, cela me montre une erreur, alors que l'exécution à partir de la ligne de commande en tant qu'utilisateur ne montre pas l'erreur.


Étape 1 : J'ai mis cette ligne temporairement dans la crontab de l'utilisateur :

* * * * *   /usr/bin/env > /home/username/tmp/cron-env

puis l'a retiré une fois le fichier écrit.

Étape 2 : Je me suis fait un petit run-as-cron bash script contenant :

#!/bin/bash
/usr/bin/env -i $(cat /home/username/tmp/cron-env) "$@"

Ainsi, en tant qu'utilisateur en question, j'ai pu

run-as-cron /the/problematic/script --with arguments --and parameters

Cette solution pourrait évidemment être étendue pour faire usage de sudo ou autre pour plus de flexibilité.

J'espère que cela aidera d'autres personnes.

9 votes

Cela ne fonctionne pas pour moi et je me demande si cela fonctionne pour tous ceux qui ont voté pour. 1) Pourquoi utilisez-vous bash ? Il n'est pas nécessaire ici et il peut ne pas se trouver dans le répertoire /usr/bin . 2) Le cat …/cron-env produit plusieurs lignes, ce qui ne fonctionne pas. Essayez simplement d'exécuter /usr/bin/env -i $(cat cron-env) echo $PATH dans le terminal, il affiche littéralement l'environnement au lieu de l'utiliser. 3) L'environnement actuel fuit dans l'environnement cron émulé. Essayez : export foo=leaked; run-as-cron echo $foo .

0 votes

@Marco Travaille en bash, qui est ce que j'utilise car c'est un environnement mieux défini que sh. J'utilise tout de pdksh, ksh (plusieurs versions), bash et dash, donc je suis très conscient des différences entre les implémentations de sh "pur", même en restant très strictement dans le sous-ensemble commun des langages. :-)

9 votes

@Marco 2. cat produit plusieurs lignes, qui fonctionnent, parce que la substitution Shell les réduit à une seule ligne, ce que vous pouvez vérifier avec echo $(cat cron-env ) | wc ; votre exemple de commande, /usr/bin/env -i $(cat cron-env) echo $PATH , les substituts $PATH de la Shell appelante ; à la place, elle devrait invoquer une subshell pour substituer le sous-environnement, par exemple /usr/bin/env -i $(cat cron-env) /bin/sh -c 'echo $PATH' . 3. Vous avez fait la même erreur, en substituant à nouveau dans l'appelant Shell au lieu du sous-environnement.

54voto

Marco Points 1491

Je présente une solution basée sur la réponse de Pistos, mais sans les défauts.

  • Ajoutez la ligne suivante à la crontab, en utilisant par exemple crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
  • Créez un Shell Shell qui exécute une commande dans le même environnement que les tâches cron :

    #!/bin/sh
    
    . "$1"
    exec /usr/bin/env -i "$SHELL" -c ". $1; $2"

Utilisez :

run-as-cron <cron-environment> <command>

par exemple

run-as-cron /home/username/cron-env 'echo $PATH'

Notez que le deuxième argument doit être cité s'il nécessite un argument. La première ligne du script charge un script POSIX comme interpréteur. La deuxième ligne source le fichier d'environnement cron. Ceci est nécessaire pour charger le script correct, qui est stocké dans la variable d'environnement SHELL . Ensuite, il charge un environnement vide (pour éviter la fuite des variables d'environnement dans le nouveau Shell), lance le même Shell qui est utilisé pour les cronjobs et charge les variables d'environnement cron. Enfin, la commande est exécutée.

1 votes

Cela m'a aidé à reproduire mon erreur de chargement de sphinx liée à ruby.

1 votes

J'ai utilisé l'option @reboot cron pour écrire le fichier cron-env. Vous pouvez ensuite le laisser dans la crontab et il ne sera réécrit que lorsque le système sera démarré. Cela rend les choses un peu plus simples puisque vous n'avez pas à ajouter/supprimer des lignes.

1 votes

Oui, la solution de Pistos n'a pas fonctionné pour moi, mais celle-ci l'a fait.

29voto

Django Janny Points 405

Comme la crontab ne fait pas le travail, vous devrez manipuler son contenu :

crontab -l | grep -v '^#' | cut -f 6- -d ' ' | while read CMD; do eval $CMD; done

Ce qu'il fait :

  • liste les tâches crontab
  • supprimer les lignes de commentaires
  • supprimer la configuration de la crontab
  • puis les lancer un par un

6 votes

Mais cela ne se fait pas nécessairement dans le même environnement que cron, et je pensais qu'il ne voulait en tester qu'un seul.

2 votes

Correct, je me suis trompé... Il exécute seulement les tâches mais pas comme le ferait cron !

7 votes

Toujours une solution géniale +1

6voto

David Pokluda Points 4284

Par défaut, avec la plupart des démons cron que j'ai vus, il n'y a tout simplement aucun moyen de dire à cron de s'exécuter ici et maintenant. Si vous utilisez anacron, il est possible, je pense, d'exécuter une instance séparée en avant-plan.

Si vos scripts ne s'exécutent pas correctement, alors vous ne tenez pas compte du fait que

  • le script est exécuté sous un utilisateur particulier
  • cron a un environnement restreint (la manifestation la plus évidente de ceci est un chemin différent).

De crontab(5) :

Plusieurs variables d'environnement sont définies automatiquement par le démon cron(8) daemon. Shell est défini comme /bin/sh, et LOGNAME et HOME sont définis à partir de la ligne /etc/passwd de l'utilisateur de la crontab. propriétaire. PATH est défini à "/usr/bin:/bin". HOME, Shell, et PATH peuvent être remplacés par des paramètres dans la crontab ; LOGNAME est l'utilisateur à partir duquel la tâche travail s'exécute, et ne peut pas être être modifié.

En général, PATH est le plus gros problème, donc vous devez :

  • Définissez explicitement le PATH dans le script, pendant le test, à /usr/bin:/bin. Vous pouvez le faire dans bash avec export PATH="/usr/bin:/bin"
  • Définissez explicitement le PATH approprié que vous souhaitez en haut de la crontab, par exemple PATH="/usr/bin:/bin:/usr/local/bin:/usr/sbin:/sbin".

Si vous devez exécuter le script en tant qu'autre utilisateur sans script (par exemple www-data), utilisez sudo :

sudo -u www-data /path/to/crontab-script.sh

La première chose à tester avant tout cela, bien sûr, est que votre script fait effectivement ce qu'il est censé faire à partir de la ligne de commande. Si vous ne pouvez pas l'exécuter depuis la ligne de commande, il ne fonctionnera évidemment pas depuis avec cron.

0 votes

Merci pour cette réponse détaillée. Je suis conscient des deux problèmes que pose l'exécution en tant qu'utilisateur particulier et dans un environnement particulier. En tant que tel, j'ai formulé ma propre réponse, que je vais maintenant poster...

0 votes

Les caractères d'échappement sont une raison valable pour laquelle le travail ne fonctionne pas.

2voto

Noam Points 121

Le script de Marco n'a pas fonctionné pour moi pour une raison quelconque. Je n'avais pas le temps de déboguer, alors j'ai écrit un script en Python qui fait la même chose. C'est plus long, mais : premièrement, cela fonctionne pour moi, et deuxièmement, je trouve cela plus facile à comprendre. Changez "/tmp/cron-env" pour l'endroit où vous avez enregistré votre environnement. C'est ici :

#!/usr/bin/env python
from __future__ import division, print_function

import sys
import os

def main():
    if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'):
        print("Usage: {} CMD\n"
              "Run a command as cron would. Note that CMD must be quoted to be only one argument."
              .format(sys.argv[0]))
        sys.exit(1)
    _me, cmd = sys.argv
    env = dict(line.strip().split('=', 1) for line in open('/tmp/cron-env'))
    sh = env['SHELL']
    os.execvpe(sh, [sh, '-c', cmd], env)

if __name__ == '__main__':
    main()

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X