Một chút ngoài phạm vi câu hỏi của bạn ... nhưng đây là những gì tôi làm.
"Làm thế nào để tôi kiểm tra một công việc định kỳ?" câu hỏi được kết nối chặt chẽ với "làm cách nào để kiểm tra các tập lệnh chạy trong bối cảnh không tương tác được khởi chạy bởi các chương trình khác?" Trong cron, trình kích hoạt là một số điều kiện thời gian, nhưng rất nhiều cơ sở * nix khác khởi chạy các tập lệnh hoặc đoạn mã theo cách không tương tác và thường là các điều kiện trong đó các tập lệnh chạy có chứa điều gì đó bất ngờ và gây ra vỡ cho đến khi các lỗi được sắp xếp. (Xem thêm: https://stackoverflow.com/a/17805088/237059 )
Một cách tiếp cận chung cho vấn đề này là hữu ích để có.
Một trong những kỹ thuật yêu thích của tôi là sử dụng một kịch bản mà tôi đã viết có tên là ' crontest '. Nó khởi chạy lệnh đích bên trong phiên màn hình GNU từ bên trong cron, để bạn có thể đính kèm với một thiết bị đầu cuối riêng biệt để xem những gì đang diễn ra, tương tác với tập lệnh, thậm chí sử dụng trình gỡ lỗi.
Để thiết lập tính năng này, bạn sẽ sử dụng "tất cả các ngôi sao" trong mục nhập crontab của mình và chỉ định crontest là lệnh đầu tiên trên dòng lệnh, ví dụ:
* * * * * crontest /command/to/be/tested --param1 --param2
Vì vậy, bây giờ cron sẽ chạy lệnh của bạn mỗi phút, nhưng crontest sẽ đảm bảo rằng chỉ có một phiên bản chạy tại một thời điểm. Nếu lệnh mất thời gian để chạy, bạn có thể thực hiện "màn hình -x" để đính kèm và xem nó chạy. Nếu lệnh là một tập lệnh, bạn có thể đặt lệnh "đọc" ở trên cùng để làm cho nó dừng lại và chờ cho phần đính kèm màn hình hoàn tất (nhấn enter sau khi đính kèm)
Nếu lệnh của bạn là tập lệnh bash, bạn có thể thực hiện việc này thay thế:
* * * * * crontest --bashdb /command/to/be/tested --param1 --param2
Bây giờ, nếu bạn đính kèm với "screen -x", bạn sẽ phải đối mặt với phiên bashdb tương tác và bạn có thể bước qua mã, kiểm tra các biến, v.v.
#!/bin/bash
# crontest
# See https://github.com/Stabledog/crontest for canonical source.
# Test wrapper for cron tasks. The suggested use is:
#
# 1. When adding your cron job, use all 5 stars to make it run every minute
# 2. Wrap the command in crontest
#
#
# Example:
#
# $ crontab -e
# * * * * * /usr/local/bin/crontest $HOME/bin/my-new-script --myparams
#
# Now, cron will run your job every minute, but crontest will only allow one
# instance to run at a time.
#
# crontest always wraps the command in "screen -d -m" if possible, so you can
# use "screen -x" to attach and interact with the job.
#
# If --bashdb is used, the command line will be passed to bashdb. Thus you
# can attach with "screen -x" and debug the remaining command in context.
#
# NOTES:
# - crontest can be used in other contexts, it doesn't have to be a cron job.
# Any place where commands are invoked without an interactive terminal and
# may need to be debugged.
#
# - crontest writes its own stuff to /tmp/crontest.log
#
# - If GNU screen isn't available, neither is --bashdb
#
crontestLog=/tmp/crontest.log
lockfile=$(if [[ -d /var/lock ]]; then echo /var/lock/crontest.lock; else echo /tmp/crontest.lock; fi )
useBashdb=false
useScreen=$( if which screen &>/dev/null; then echo true; else echo false; fi )
innerArgs="$@"
screenBin=$(which screen 2>/dev/null)
function errExit {
echo "[-err-] $@" | tee -a $crontestLog >&2
}
function log {
echo "[-stat-] $@" >> $crontestLog
}
function parseArgs {
while [[ ! -z $1 ]]; do
case $1 in
--bashdb)
if ! $useScreen; then
errExit "--bashdb invalid in crontest because GNU screen not installed"
fi
if ! which bashdb &>/dev/null; then
errExit "--bashdb invalid in crontest: no bashdb on the PATH"
fi
useBashdb=true
;;
--)
shift
innerArgs="$@"
return 0
;;
*)
innerArgs="$@"
return 0
;;
esac
shift
done
}
if [[ -z $sourceMe ]]; then
# Lock the lockfile (no, we do not wish to follow the standard
# advice of wrapping this in a subshell!)
exec 9>$lockfile
flock -n 9 || exit 1
# Zap any old log data:
[[ -f $crontestLog ]] && rm -f $crontestLog
parseArgs "$@"
log "crontest starting at $(date)"
log "Raw command line: $@"
log "Inner args: $@"
log "screenBin: $screenBin"
log "useBashdb: $( if $useBashdb; then echo YES; else echo no; fi )"
log "useScreen: $( if $useScreen; then echo YES; else echo no; fi )"
# Were building a command line.
cmdline=""
# If screen is available, put the task inside a pseudo-terminal
# owned by screen. That allows the developer to do a "screen -x" to
# interact with the running command:
if $useScreen; then
cmdline="$screenBin -D -m "
fi
# If bashdb is installed and --bashdb is specified on the command line,
# pass the command to bashdb. This allows the developer to do a "screen -x" to
# interactively debug a bash shell script:
if $useBashdb; then
cmdline="$cmdline $(which bashdb) "
fi
# Finally, append the target command and params:
cmdline="$cmdline $innerArgs"
log "cmdline: $cmdline"
# And run the whole schlock:
$cmdline
res=$?
log "Command result: $res"
echo "[-result-] $(if [[ $res -eq 0 ]]; then echo ok; else echo fail; fi)" >> $crontestLog
# Release the lock:
9<&-
fi