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


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.