크론 작업을 수동으로 즉시 실행


108

(나는 이미 새로운 cron 스크립트를 어떻게 테스트 할 수 있습니까?를 읽었습니다 .)

특정 문제가 있습니다 (크론 작업이 실행되지 않거나 제대로 실행되지 않는 것 같습니다). 그러나 문제는 일반적입니다. 크론 된 스크립트를 디버깅하고 싶습니다. * * * * * crontab 줄을 설정할 수 있지만 완전히 만족스러운 해결책은 아닙니다. cron이 실행중인 것처럼 명령 줄에서 cron 작업을 실행할 수 있기를 원합니다 (같은 사용자, 동일한 환경 변수 등). 이 방법이 있습니까? 스크립트 변경을 테스트하기 위해 60 초를 기다려야하는 것은 실용적이지 않습니다.


(죄송합니다 코멘트를 추가 할 수 없습니다) 0 30 16 20 *? 당신이 그와 같은 작업을 실행하는 경우에도 * 전체 아이디어는 작업이 로그에 기록하지 않는 잘못된 무슨 일이 일어나고 있는지보고 스크립트 출력을 제공하는 것입니다,이 quuiet 쓸모가

답변:


80

여기 내가 한 일이 있으며이 상황에서는 효과가있는 것 같습니다. 적어도 사용자에게 오류가 표시되지 않으므로 명령 줄에서 실행하는 동안 오류가 표시됩니다.


1 단계 :이 줄을 사용자의 crontab에 임시로 넣었습니다.

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

그런 다음 파일이 작성되면 꺼 냈습니다.

2 단계 : 나 자신을 크론 bash 스크립트로 만들었습니다.

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

그래서 문제의 사용자로서

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

이 솔루션은 분명히 유연성을 높이기 위해 sudo 등을 사용하도록 확장 될 수 있습니다.

이것이 다른 사람들을 돕기를 바랍니다.


8
이것은 나를 위해 작동하지 않으며 나는 그것이 upvoted 누군가를 위해 있는지 궁금합니다. 1) 왜 bash를 사용하고 있습니까? 여기에 필요하지 않으며에 없을 수도 있습니다 /usr/bin. 2) cat …/cron-env여러 줄을 출력하지만 작동하지 않습니다. /usr/bin/env -i $(cat cron-env) echo $PATH터미널에서 실행하려고 하면 환경을 사용하지 않고 그대로 출력합니다. 3) 현재 환경이 에뮬레이트 된 크론 환경으로 누출됩니다. 시도하십시오 : export foo=leaked; run-as-cron echo $foo.
— Marco

@Marco는 bash에서 작동합니다 .sh보다 더 잘 정의 된 환경이므로 사용합니다. 나는 pdksh, ksh (다중 버전), bash 및 dash의 모든 것을 사용하므로 언어의 공통 하위 집합에 매우 엄격하게 머무를 때도 sh의 "순수한"구현 간의 차이점을 매우 잘 알고 있습니다. :-)
— Max Murphy

7
@Marco 2. cat여러 줄을 출력합니다.이 줄은 쉘 대체가 한 줄로 접혀서 확인할 수 있기 때문에 작동합니다 echo $(cat cron-env ) | wc. 예제 명령 은 호출 쉘을 /usr/bin/env -i $(cat cron-env) echo $PATH대체 $PATH합니다. 대신 하위 환경을 대체 할 하위 쉘을 호출해야합니다 (예 :) /usr/bin/env -i $(cat cron-env) /bin/sh -c 'echo $PATH'. 3. 당신은 같은 실수를했고, 다시 하위 환경 대신에 호출 쉘을 대체했습니다
— John Freeman

41

Pistos 답변을 기반으로 한 솔루션을 제시하지만 결함은 없습니다.

  • crontab에 다음 줄을 추가하십시오. crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
    
  • cron 작업 실행과 동일한 환경에서 명령을 실행하는 쉘 스크립트를 작성하십시오.

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

사용하다:

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

예 :

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

인수가 필요한 경우 두 번째 인수를 인용해야합니다. 스크립트의 첫 번째 줄은 POSIX 셸을 인터프리터로로드합니다. 두 번째 줄은 cron 환경 파일을 제공합니다. 이는 환경 변수에 저장된 올바른 쉘을로드하는 데 필요합니다 SHELL. 그런 다음 빈 환경을로드하고 (환경 변수가 새 쉘로 누출되는 것을 방지하기 위해) cronjobs에 사용되는 동일한 쉘을 실행하고 cron 환경 변수를로드합니다. 마지막으로 명령이 실행됩니다.


이것은 루비 관련 스핑크스 로딩 오류를 재현하는 데 도움이되었습니다.
— cweiske

1
@reboot cron 옵션을 사용하여 cron-env 파일을 작성했습니다. 그런 다음 crontab에 그대로두고 시스템이 시작될 때만 다시 작성됩니다. 줄을 추가 / 제거 할 필요가 없으므로 조금 더 단순 해집니다.
— Michael Barton

그렇습니다. Pistos 솔루션은 저에게는 효과가 없었습니다.
— Stack Underflow

19

crontab이 작업을 수행하지 않으므로 내용을 조작해야합니다.

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

그것이하는 일 :

  • crontab 작업 목록
  • 주석 줄 제거
  • crontab 구성 제거
  • 그런 다음 하나씩 시작

5
그러나 cron과 동일한 환경에서 반드시 그렇게 할 필요는 없으며 그 중 하나만 테스트하고 싶다고 생각했습니다.
— 팔콘 Momot

2
맞습니다, 나는 착각했습니다 ... 그것은 단지 작업을 실행하지만 크론처럼하지 않습니다!
— Django Janny

5
여전히 멋진 솔루션 +1
— Eric Uldall

1
sudo -H -u otheruser bash -c 'crontab..." 다른 사용자 btw의 crontab을 실행할 수 있습니다
— Freedo

5

내가 본 대부분의 기본 크론 데몬은 기본적으로 크론에게 지금 당장 실행하도록 지시하는 방법이 없습니다. anacron을 사용하는 경우 포 그라운드에서 별도의 인스턴스를 실행하는 것이 가능할 수 있습니다.

스크립트가 제대로 실행되지 않으면 다음을 고려하지 않은 것입니다.

  • 스크립트가 특정 사용자로 실행 중입니다
  • cron에는 제한된 환경이 있습니다 (가장 명백한 표현은 다른 경로입니다).

crontab (5)에서 :

cron (8) 데몬이 여러 환경 변수를 자동으로 설정합니다. SHELL은 / bin / sh로 설정되고 LOGNAME 및 HOME은 crontab 소유자의 / etc / passwd 행에서 설정됩니다. PATH가 "/ usr / bin : / bin"으로 설정되어 있습니다. HOME, SHELL 및 PATH는 crontab의 설정으로 대체 될 수 있습니다. LOGNAME은 작업을 실행중인 사용자이며 변경할 수 없습니다.

일반적으로 PATH는 가장 큰 문제이므로 다음을 수행해야합니다.

  • 테스트하는 동안 스크립트 내에서 PATH를 명시 적으로 / usr / bin : / bin으로 설정하십시오. export PATH = "/ usr / bin : / bin" 으로 bash에서이를 수행 할 수 있습니다 .
  • crontab 상단에 원하는 적절한 PATH를 명시 적으로 설정하십시오. 예 : PATH = "/ usr / bin : / bin : / usr / local / bin : / usr / sbin : / sbin"

쉘없이 다른 사용자로 스크립트를 실행해야하는 경우 (예 : www-data) sudo를 사용하십시오.

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

물론 모든 것을 테스트하기 전에 가장 먼저 테스트해야 할 것은 스크립트가 실제로 명령 행에서 수행해야하는 작업을 수행한다는 것입니다. 명령 행에서 실행할 수 없으면 cron에서는 작동하지 않습니다.


철저한 답변 감사합니다. 특정 사용자 및 특정 환경에서 실행하는 두 가지 문제를 알고 있습니다. 이와 같이, 나는 내 자신의 답변을 공식화
— Pistos

이스케이프 문자는 작업이 실행되지 않는 유효한 이유입니다.
— Joe Phillips

2

마르코의 대본은 어떤 이유로 든 작동하지 않았습니다. 디버깅 할 시간이 없었으므로 동일한 작업을 수행하는 Python 스크립트를 작성했습니다. 더 길지만 첫째, 그것은 나를 위해 일하고, 둘째는 이해하기 쉽다는 것을 알았습니다. "/ tmp / cron-env"를 환경을 저장 한 위치로 변경하십시오. 여기있어:

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

1

글쎄, 사용자는 crontab 항목에 입력 한 것과 동일하므로 (또는 crontab을 번갈아 가며 입력하는 것과 동일), 이는 쉬운 일이 아닙니다. crontab(5) 환경 변수 세트 목록을 제공해야하며 몇 가지만 있습니다.


다시 말해, 할 수있는 방법이 없다고 말하는 것입니까? "충분히 닫는"해결 방법 만?
— Pistos

아니요, 제 답변에 제공된 정보를 사용하여 할 수 있다고 말하고 있습니다.
— womble

1

vixie-cron과 같은 대부분의 crontab에서는 이와 같이 crontab 자체에 변수를 배치 한 다음 / usr / bin / env를 사용하여 작동하는지 확인할 수 있습니다. 이 방법으로 run-as-cron 스크립트의 문제점을 발견하면 crontab에서 스크립트를 작동시킬 수 있습니다.

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

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

1

Marco의 솔루션은 나를 위해 작동하지 않았지만 Noam의 Python 스크립트는 작동했습니다. 다음은 Marco의 스크립트를 약간 수정하여 나를 위해 일한 것입니다.

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

set -a스크립트 $ 1에 정의 된 내보내기 변수가 추가 되어 $ 2 명령에 사용할 수 있습니다.

ps Noam의 python은 자식 프로세스로 환경을 '내 보냈기 때문에 작동했습니다.


1

쉘 스크립트 인 경우 다음과 같은 방법으로 얻을 수 있습니다.

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>

모든 것이 아니라면 분명히 몇 가지 문제를 강조 할 것입니다.


0

나는 cron 작업을 수동으로 실행하는 방법을 찾지 못했지만 이 글은 cronjob과 동일한 환경을 설정하고 수동으로 스크립트를 실행하는 것을 제안합니다.


OP가 원하는 방법을 알고 싶은 것이 아닌가?
— womble

그렇기 때문에 글쓰기 방법을 설명하는 글쓰기 링크를 포함 시켰습니다. 여기에 모든 것을 복사하여 붙여 넣을 필요는 없다고 생각했습니다.
— oneodd1

0

다음 분을 시작하도록 작업을 프로그래밍 할 수 있습니다. :)


7
59 초는 많은 시간입니다.
— Stéphane Bruckert

OP는이 가능성을 다음과 같이 언급했다. "이 작업을 수행 할 수있는 방법이 있습니까? 스크립트 변경을 테스트하기 위해 60 초 동안 기다려야하는 것은 실용적이지 않습니다."
— Andrew Grimm

59 초는 다른 제안 된 (작동하지 않을 수도있는) 솔루션 중 하나를 선택하여 구현하는 데 걸리는 시간보다 적을 수 있습니다. 이러한 단점을 볼 때 Linux가 사실상 표준 서버 OS가 된 방법이 궁금합니다. 심각한 sysadmin이 작업을 테스트하고 싶지 않습니까?
— Rolf

0

나는 마르코의 대답에 국수를 썼다. 코드는 아래에 나와 있지만이 스크립트를 여기에서 유지합니다 .

이 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"

샘플 사용 세션 :

$ 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

이것은 cronTest2cron과 동일한 방식으로 환경 변수를 설정하기 위해 올바르게 호출되어야합니다.

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

cronTestcronTest2적절한 환경 변수 세트로 실행됩니다 .

#!/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"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.