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.

2voto

Bill Points 121

La solution de Marco n'a pas fonctionné pour moi mais le script de Noam en Python 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"

L'ajout set -a exporter les variables définies dans script $1 et les rendre disponibles à la commande $2

p.s. Le Python de Noam fonctionne parce qu'il "exporte" l'environnement vers le processus enfant.

0 votes

Vous pouvez vérifier si vous en avez besoin en exécutant ./run-as-cron path/to/cron-env 'env' . Si cela ne montre pas la même chose que path/to/cron-env contient, la commande (dans ce cas : env lui-même, ne fonctionne pas réellement dans le même environnement que celui dans lequel env est en cours.

2voto

Mike Slinn Points 121

Je me suis inspiré de la réponse de Marco. Le code est montré ci-dessous, mais je vais maintenir ce script. aquí .

Compte tenu de cette 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 invoqué correctement 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 :

#!/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"

1voto

Ryan Sampson Points 2898

Eh bien, l'utilisateur est le même que celui que vous avez mis dans l'entrée crontab (ou dans la crontab de qui vous l'avez mis, 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.

0 votes

En d'autres termes, vous dites qu'il n'y a aucun moyen de le faire ? Seulement des solutions de contournement "assez proches" ?

0 votes

Non, je dis que vous pouvez le faire, en utilisant les informations que j'ai fournies dans ma réponse.

1voto

Marc Elser Points 11

Dans la plupart des crontabs comme par exemple 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 le crontab une fois que vous avez trouvé ce qui ne va pas avec le script du run-as-cron.

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

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

1voto

Matthew Wilcoxson Points 111

Si c'est un Shell Shell, cela devrait vous permettre de faire une bonne partie du chemin :

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 va certainement mettre en lumière certains problèmes, si ce n'est tout.

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