web-dev-qa-db-ja.com

手動ですぐにcronジョブを実行する

(私はすでに読んだ 新しいcronスクリプトをテストするにはどうすればよいですか? 。)

特定の問題があります(cronジョブが実行されていない、または適切に実行されていないようです)が、一般的な問題です。cron化されたスクリプトをデバッグしたいのですが。 * * * * * crontab行を設定できることは承知していますが、これは完全に満足できる解決策ではありません。 cronジョブが実行されているかのように、コマンドラインからcronジョブを実行できるようにしたいと思います(同じユーザー、同じ環境変数など)。これを行う方法はありますか?スクリプトの変更をテストするために60秒待つ必要はありません。

113
Pistos

これが私がやったことであり、この状況でうまくいくようです。少なくとも、エラーは表示されますが、ユーザーがコマンドラインから実行するとエラーは表示されません。


ステップ1:この行をユーザーのcrontabに一時的に配置します。

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

ファイルが書き込まれたら、それを取り出しました。

ステップ2:以下を含む小さなrun-as-cron bashスクリプトを作成しました。

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

それで、問題のユーザーとして、私はすることができました

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

このソリューションは、Sudoなどを利用してより柔軟に拡張できることは明らかです。

これが他の人を助けることを願っています。

86
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'

2番目の引数は、引数が必要な場合は引用符で囲む必要があることに注意してください。スクリプトの最初の行は、POSIXシェルをインタープリターとしてロードします。 2行目は、cron環境ファイルのソースです。これは、環境変数Shellに格納されている正しいシェルをロードするために必要です。次に、(新しいシェルへの環境変数のリークを防ぐために)空の環境をロードし、cronjobsに使用されるのと同じシェルを起動して、cron環境変数をロードします。最後にコマンドが実行されます。

43
Marco

Crontabは仕事をしないので、その内容を操作します。

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

それがすること:

  • crontabジョブを一覧表示します
  • コメント行を削除する
  • crontab構成を削除する
  • 次に、それらを1つずつ起動します
21
Django Janny

私が見たほとんどのデフォルトのcronデーモンのデフォルトでは、現時点でここで実行するようにcronに指示する方法はありません。 anacronを使用している場合は、フォアグラウンドで別のインスタンスを実行することもできます。

スクリプトが適切に実行されていない場合、それは考慮されていません

  • スクリプトは特定のユーザーとして実行されています
  • cronには制限された環境があります(これの最も明白な兆候は別のパスです)。

Crontab(5)から:

いくつかの環境変数は、cron(8)デーモンによって自動的に設定されます。シェルは/ 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では動作しません。

5
Philip Reynolds

マルコのスクリプトは、なぜか私にはうまくいきませんでした。デバッグする時間がなかったので、Pythonスクリプトを書いて、同じことをしました。もっと長くなりましたが、最初に機能し、2つ目は、 「/ 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()
2
Noam

シェルスクリプトの場合、これでほとんどの方法が得られます。

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>

すべてではないにしても、いくつかの問題が明らかに強調されます。

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
Bill

まあ、ユーザーはcrontabエントリに入力したユーザー(またはcrontabを交互に入力したユーザー)と同じなので、簡単です。 crontab(5)を実行すると、環境変数セットのリストが表示されます。いくつかあります。

1
womble

例えばのようなほとんどのcrontabではvixie-cronでは、このようにcrontab自体に変数を配置してから、/ usr/bin/envを使用して、機能しているかどうかを確認できます。このように、run-as-cronスクリプトの問題点を見つけたら、crontabでスクリプトを機能させることができます。

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

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

次の分を開始するようにジョブをプログラムできます:)

0
AdrP

マルコの答えにうんざりしました。コードを以下に示しますが、このスクリプトは維持します here

この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

これは cronTest2は、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実行cronTest2適切な環境変数を設定:

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

@DjangoJannyの答えに触発されて、これが私が慣れているものですcrontabの5行目でジョブを起動

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

説明:

  • 次のコマンドを起動します。
  • crontabを表示します
  • 5行目を取得
  • 複数のスペースを1つのスペースに置き換えます
  • 6列目から最後まですべて取ります
0
Basj

私はcronジョブを手動で実行する方法を見つけたことがありませんが、 this write-upは、cronjobと同じ環境を設定し、スクリプトを手動で実行することをお勧めします。

0
oneodd1