Executando um cron job manualmente e imediatamente

105

(Eu já li Como posso testar um novo script do cron? .

Eu tenho um problema específico (trabalho cron não parece ser executado ou executado corretamente), mas o problema é geral: eu gostaria de depurar scripts que são cronned. Estou ciente de que posso configurar uma linha crontab * * * * *, mas essa não é uma solução totalmente satisfatória. Eu gostaria de ser capaz de executar um cron na linha de comando como se o cron estivesse rodando (mesmo usuário, mesmas variáveis de ambiente, etc.). Existe uma maneira de fazer isso? Ter que esperar 60 segundos para testar as alterações do script não é prático.

    
por Pistos 18.11.2009 / 14:55

12 respostas

77

Veja o que eu fiz e parece funcionar nessa situação. Pelo menos, ele me mostra um erro, enquanto a execução da linha de comando como o usuário não mostra o erro.

Passo 1 : eu coloco esta linha temporariamente no crontab do usuário:

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

depois retirou-o assim que o arquivo foi escrito.

Etapa 2 : criei um pequeno script bash de execução como cron contendo:

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

Então, como usuário em questão, consegui

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

Esta solução poderia, obviamente, ser expandida para fazer uso do sudo ou de outros recursos para maior flexibilidade.

Espero que isso ajude os outros.

    
por 18.11.2009 / 15:40
41

Eu apresento uma solução baseada na resposta do Pistos, mas sem as falhas.

  • Adicione a seguinte linha ao crontab, por ex. usando crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
    
  • Crie um script de shell que execute um comando no mesmo ambiente em que as tarefas agendadas são executadas:

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

Uso:

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

por exemplo,

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

Note que o segundo argumento precisa ser citado se requer um argumento. A primeira linha do script carrega um shell POSIX como intérprete. A segunda linha origina o arquivo de ambiente cron. Isso é necessário para carregar o shell correto, que é armazenado na variável de ambiente SHELL . Em seguida, ele carrega um ambiente vazio (para evitar o vazamento de variáveis de ambiente no novo shell), lança o mesmo shell que é usado para o cronjobs e carrega as variáveis de ambiente do cron. Finalmente o comando é executado.

    
por 24.09.2013 / 20:46
16

Como o crontab não faz o trabalho, você manipula seu conteúdo:

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

O que faz:

  • lista trabalhos do crontab
  • remover linhas de comentários
  • remova a configuração do crontab
  • depois, inicie-os um por um
por 19.08.2013 / 21:45
5

Por padrão, com a maioria dos daemons do cron padrão que vi, simplesmente não há como dizer ao cron para rodar bem aqui agora. Se você estiver usando anacron, pode ser possível que eu execute uma instância separada em primeiro plano.

Se os seus scripts não estão funcionando corretamente, você não está levando em consideração que

  • o script está sendo executado como um usuário específico
  • o cron tem um ambiente restrito (a manifestação mais óbvia disso é um caminho diferente).

De crontab (5):

Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd line of the crontab’s owner. PATH is set to "/usr/bin:/bin". HOME, SHELL, and PATH may be overridden by settings in the crontab; LOGNAME is the user that the job is running from, and may not be changed.

Em geral, o PATH é o maior problema, então você precisa:

  • Defina explicitamente o PATH dentro do script, durante o teste, para / usr / bin: / bin. Você pode fazer isso no bash com export PATH="/ usr / bin: / bin"
  • Defina explicitamente o PATH adequado que você deseja no topo do crontab. por exemplo. PATH="/ usr / bin: / bin: / usr / local / bin: / usr / sbin: / sbin"

Se você precisar executar o script como outro usuário sem um shell (por exemplo, www-data), use sudo:

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

A primeira coisa a testar antes de tudo isso, é claro, é que o seu script realmente faz o que é suposto fazer na linha de comando. Se você não pode executá-lo a partir da linha de comando, obviamente não funcionará com o cron.

    
por 18.11.2009 / 15:27
2

O roteiro de Marco não funcionou para mim por algum motivo. Eu não tive tempo para depurar, então eu escrevi um script Python que faz a mesma coisa. É mais longo, mas: primeiro, funciona para mim e, segundo, acho mais fácil de entender. Altere "/ tmp / cron-env" para onde você salvou seu ambiente. Aqui está:

#!/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()
    
por 24.12.2014 / 09:48
1

Bem, o usuário é o mesmo que você colocou na entrada crontab (ou cujo crontab você colocou, alternadamente), então isso é óbvio. crontab (5) deve dar a você a lista de variáveis de ambiente definidas, há apenas algumas.

    
por 18.11.2009 / 15:04
1

Na maioria dos crontabs como, e. vixie-cron você pode colocar variáveis no próprio crontab assim e então usar / usr / bin / env para verificar se funcionou. Desta forma você pode fazer seu script funcionar no crontab assim que você descobrir o que está errado com o script run-as-cron.

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

* * * * *   /usr/bin/env > /home/username/cron-env
    
por 27.10.2014 / 07:52
1

A solução de Marco não funcionou para mim, mas o script python de Noam funcionou. Aqui está uma pequena modificação no script de Marco que fez com que funcionasse para mim:

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

As variáveis de exportação set -a adicionadas definidas no script $ 1 e disponibilizaram para o comando $ 2

p.s. O python de Noam funcionou porque "exportou" o ambiente para o processo filho.

    
por 11.05.2015 / 07:05
0

Eu nunca encontrei uma maneira de executar tarefas cron manualmente, mas este artigo sugere definir o mesmo ambiente que o cronjob teria e executando o script manualmente.

    
por 18.11.2009 / 15:07
0

você pode programar o trabalho para iniciar o próximo minuto:)

    
por 23.09.2013 / 15:52
0

Se for um script de shell, isso deve te ajudar na maior parte do caminho:

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>

Isso definitivamente destacará alguns problemas, se não tudo.

    
por 04.04.2018 / 19:16
0

Eu usei a resposta de Marco. Código é mostrado abaixo, mas vou manter este script aqui .

Dado este 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"

Sessão de uso de amostra:

$ 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

Isso é cronTest2 , que precisa ser invocado adequadamente para configurar as variáveis de ambiente da mesma maneira que o 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, 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 runs cronTest2 com as variáveis de ambiente adequadas definidas:

#!/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"
    
por 05.05.2019 / 22:32
-2

Se você precisar executá-lo imediatamente, apenas para testar, use este formato:

second, minute, hour, day, * ? *

Por exemplo, se você deseja executar hoje (9/20/2016) às 16:30.

0 30 16 20 * ? *

Certifique-se de alterar seu cron job mais tarde, pois isso será veiculado no dia 20 de cada mês.

    
por 20.09.2016 / 22:24

Tags