web-dev-qa-db-fra.com

Exécuter une tâche cron manuellement et immédiatement

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

J'ai un problème spécifique (le travail cron ne semble pas fonctionner ou ne fonctionne pas correctement), mais le problème est général: je voudrais déboguer des scripts qui sont cronned. Je suis conscient que je peux configurer une ligne de crontab * * * * *, mais ce n'est pas une solution entièrement satisfaisante. Je voudrais pouvoir exécuter un travail cron à partir de la ligne de commande comme si cron l'exécutait (même utilisateur, mêmes variables d'environnement, etc.). Y a-t-il un moyen de faire cela? Avoir à attendre 60 secondes pour tester les changements de script n'est pas pratique.

113
Pistos

Voici ce que j'ai fait, et cela semble fonctionner dans cette situation. Au moins, cela me montre une erreur, tandis que l'exécution à partir de la ligne de commande car l'utilisateur n'affiche pas l'erreur.


Étape 1: Je mets temporairement cette ligne dans la crontab de l'utilisateur:

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

puis je l'ai retiré une fois le fichier écrit.

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

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

Alors, 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 utiliser Sudo ou similaire pour plus de flexibilité.

J'espère que cela aide les autres.

86
Pistos

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

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

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

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

Utilisation:

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 shell POSIX comme interpréteur. La deuxième ligne source le fichier d'environnement cron. Cela est nécessaire pour charger le shell correct, qui est stocké dans la variable d'environnement Shell. Ensuite, il charge un environnement vide (pour éviter la fuite de 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.

43
Marco

Comme 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 commentaire
  • supprimer la configuration crontab
  • puis lancez-les un par un
21
Django Janny

Par défaut avec la plupart des démons cron par défaut que j'ai vus, il n'y a tout simplement aucun moyen de dire à cron de s'exécuter ici même maintenant. Si vous utilisez anacron, il peut être possible, je pense, d'exécuter une instance distincte au premier plan.

Si vos scripts ne fonctionnent pas correctement, vous ne tenez pas compte du fait que

  • le script s'exécute en tant qu'utilisateur particulier
  • cron a un environnement restreint (la manifestation la plus évidente de ceci est un chemin différent).

Depuis crontab (5):

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

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

  • Définissez explicitement le CHEMIN dans le script, pendant le test, sur/usr/bin:/bin. Vous pouvez le faire en bash avec export PATH = "/ usr/bin:/bin"
  • Définissez explicitement le chemin 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'un autre utilisateur sans Shell (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 ce qu'il est censé faire à partir de la ligne de commande. Si vous ne pouvez pas l'exécuter à partir de la ligne de commande, cela ne fonctionnera évidemment pas avec cron.

5
Philip Reynolds

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

#!/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()
2
Noam

S'il s'agit d'un script Shell, cela devrait vous aider la plupart du temps:

Sudo su  # (assuming it's run as root, if not switch to the user you want it to run as)
cd  # Switch to home folder
sh <full-path/my-Shell-script>

Cela mettra certainement en évidence certains problèmes, sinon tout.

1

La solution de Marco n'a pas fonctionné pour moi mais le script python de Noam a fonctionné. Voici une légère modification du script de Marco qui l'a fait fonctionner pour moi:

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

Les variables d'exportation set -a Ajoutées définies dans le script $ 1 et rendues disponibles pour commander $ 2

p.s. python de Noam a fonctionné car il a "exporté" l'environnement vers le processus enfant.

1
Bill

Eh bien, l'utilisateur est le même que celui que vous avez mis dans l'entrée crontab (ou dont vous avez mis la crontab, alternativement), donc c'est une évidence. crontab (5) devrait vous donner la liste des variables d'environnement définies, il n'y en a que quelques-unes.

1
womble

Dans la plupart des crontabs comme par ex. vixie-cron vous pouvez placer des variables dans le crontab lui-même comme ceci et ensuite utiliser/usr/bin/env pour vérifier si cela a fonctionné. De cette façon, vous pouvez faire fonctionner votre script dans crontab une fois que vous avez découvert ce qui ne va pas avec le script run-as-cron.

Shell=/bin/bash
LANG=en
FASEL=BLA

* * * * *   /usr/bin/env > /home/username/cron-env
1
Marc Elser

vous pouvez programmer le travail pour démarrer la minute suivante :)

0
AdrP

J'ai nouillé sur la réponse de Marco. Le code est montré ci-dessous, mais je maintiendrai ce script ici .

Compte tenu de ce crontab:

# m h  dom mon dow   command

X=Y
1 2 3 4 5 6 echo "Hello, world"
1 2 3 4 5 6 echo "Goodby, cruel world"
1 2 3 4 5 6 echo "Please spare me the drama"

Exemple de session d'utilisation:

$ cronTest
This is the crontab for  without comment lines or blank lines:
     1  X=Y
     2  echo "Hello, world"
     3  echo "Goodby, cruel world"
     4  echo "Please spare me the drama"
Which line would you like to run as  now?
55
55 is not valid, please enter an integer from 1 to 4
2

Evaluating 1: X=Y

Evaluating 2: echo "Hello, world"
Hello, world

C'est cronTest2, qui doit être correctement invoqué pour configurer les variables d'environnement de la même manière que cron:

#!/bin/bash

# Prompt user for a user crontab entry to execute

function deleteTempFile {
  rm -f $TEMP_FILE
}

function debug {
  if [ "$DEBUG" ]; then >&2 printf "$1\n"; fi
}

function isValidLineNumber {
  # $1 - number of lines
  # $2 - requested line number
  if [[ -n "${2//[0-9]+/}" ]] && (( $2 <= $1 )); then echo true; else echo false; fi
}

function isVariableAssignment {
  [[ "$( echo "$1" | grep "=" )" ]]
}

function makeTempCrontab {
  local -r ASTERISK=\\*
  local -r NUMBER='[[:digit:]]{1,2}'
  local -r NUMBERS="$NUMBER(,$NUMBER)+"
  local -r CRON="^(($ASTERISK|$NUMBER|$NUMBERS)[[:space:]]+)"
  local -r CRON5_REGEX="$CRON{5}"
  local -r CRON6_REGEX="$CRON{6}"

  rm -f "$TEMP_FILE"

  local -r ALL_LINES="$( crontab -l )"

  # Ignore empty lines and lines starting with # (comment lines)
  local -r LINES="$( 
    echo "$ALL_LINES" | \
    grep -v '^[[:space:]]*#' | \
    grep -v '^[[:space:]]*$'
  )"

  if [[ -z "$LINES" ]]; then
    echo "Your crontab is empty, nothing to do"
    exit 1
  fi

  IFS=$'\n' 
  for LINE in $LINES; do
    LINE="$( echo "$LINE" | sed 's/\s\+$//e' )" # remove trailing space
    if [ "$( echo "$LINE" | grep "^$" )" ]; then  
      debug ""  # ignore empty line
    Elif [ "$( echo "$LINE" | egrep "$CRON6_REGEX" )" ]; then
      debug "6 field date/time specifier: $LINE"
      # strip out when to run debug, leaving just the command to execute
      echo "$LINE" | cut -f 7- -d ' ' >> "$TEMP_FILE"
    Elif [ "$( echo "$LINE" | egrep "$CRON5_REGEX" )" ]; then
      debug "5 field date/time specifier: $LINE"
      # strip out when to run debug, leaving just the command to execute
      echo "$LINE" | cut -f 6- -d ' ' >> "$TEMP_FILE"
    Elif [ "$( echo "$LINE" | grep '^@' )" ]; then
      debug "@declaration: $LINE"
      # strip out @declaration, leaving just the command to execute
      echo "$LINE" | cut -f 2- -d ' ' >> "$TEMP_FILE"
    Elif [ "$( echo "$LINE" | grep '=' )" ]; then
      debug "Variable assignment: $LINE"
      echo "$LINE"  >> "$TEMP_FILE"
    else
      debug "Ignored: $LINE"
    fi
  done
  unset IFS
}

function runUpToLine {
  # Scans up to given line number in $TEMP_FILE
  # Evaluates variable assignment
  # Executes specified line
  # Ignores remainder of file
  # Function definitions are not supported
  #
  # $1 - line number to run

  readarray CONTENTS < "$TEMP_FILE"
  for (( i=0; i<=$1; i++ )); do
    # >&2 echo "\$i=$i, \$1=$1, isVariableAssignment: $( isVariableAssignment $CONTENTS[$i] ), CONTENTS[$i]=${CONTENTS[$i]}"
    if isVariableAssignment ${CONTENTS[$i]} || (( $i == $1 )); then
      printf "\nEvaluating $(( i+1 )): ${CONTENTS[$i]}"
      eval "${CONTENTS[$i]}"
    fi
  done
}

function selectLine {
  >&2 echo "This is the crontab for $USER without comment lines or blank lines:"
  cat -n "$TEMP_FILE" >&2
  >&2 echo "Which line would you like to run as $USER now?"

  local -r NUM_LINES=$( cat "$TEMP_FILE" | wc -l )
  read LINE_NUMBER
  # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER;  valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )"
  while [[ $( isValidLineNumber $NUM_LINES $LINE_NUMBER ) == false ]]; do
    >&2 echo "$LINE_NUMBER is not valid, please enter an integer from 1 to $NUM_LINES"
    read LINE_NUMBER
    # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER;  valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )"
  done
  (( LINE_NUMBER-- ))
  echo ${LINE_NUMBER}
}

function doIt {
  export USER=$1
  local -r TEMP_FILE="$( mktemp crontabTest.XXX )"
  trap deleteTempFile EXIT

  makeTempCrontab
  local -r LINE_NUMBER="$( selectLine )"
  runUpToLine $LINE_NUMBER
}

doIt "$1" 

cronTest exécute cronTest2 avec les variables d'environnement appropriées définies:

#!/bin/bash

# Execute a user crontab entry with the proper environment

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

env -i bash --noprofile --norc -c "$DIR/cronTest2 $USER"
0
Mike Slinn

Inspiré par la réponse de @ DjangoJanny, voici ce que j'utilise pour lancer le travail à la 5ème ligne de mon crontab:

 eval "$(crontab -l | sed -n '5p' | tr -s ' ' | cut -d' ' -f 6-)"

Explication:

  • lance la commande donnée par:
  • affiche la crontab
  • obtient la 5ème ligne
  • remplace plusieurs espaces par un seul espace
  • prend tout de la 6e colonne à la fin
0
Basj

Je n'ai jamais trouvé de moyen d'exécuter les tâches cron manuellement mais this l'écriture suggère de définir le même environnement que le cronjob aurait et d'exécuter le script manuellement.

0
oneodd1