Nothing
#' @title Make a simple cron job
#'
#' @description Generate a cron job, and pass it to crontab.
#'
#' The goal is to be able to translate simple English statements of intent
#' to the actual \code{cron} statement that could execute that intent. For example,
#'
#' \emph{"I want to run a job daily at 7AM."}
#'
#' is simply
#'
#' \code{cron_add(<command>, "daily", at="7AM")}
#'
#' Another example, \emph{"I want to run a job on the 15th of every month."}
#'
#' is
#'
#' \code{cron_add(<command>, "monthly", days_of_month="15th")}
#'
#' @param command A command to execute.
#' @param frequency A character string equal to one of
#' \code{"minutely"}, \code{"hourly"}, \code{"daily"},
#' \code{"monthly"}, or \code{"yearly"}. Or any complex cron schedule - see the examples.
#' @param at The actual time of day at which to execute the command.
#' When unspecified, we default to \code{"3AM"}, when the command is to
#' be run less frequently than \code{"hourly"}.
#' @param days_of_month Optional; the day(s) of the month on which we execute the
#' command.
#' @param days_of_week Optional; the day(s) of the week on which we execute the
#' command.
#' @param months Optional; the month(s) of the year on which we execute
#' the command.
#' @param id An id, or name, to give to the cronjob task, for easier
#' revision in the future.
#' @param tags A set of tags, used for easy listing and retrieval
#' of cron jobs.
#' @param description A short description of the job, and its purpose.
#' @param dry_run Boolean; if \code{TRUE} we do not submit the cron job;
#' instead we return the parsed text that would be submitted as a cron job.
#' @param user The user whose cron jobs we wish to examine.
#' @param ask Boolean; show prompt asking for validation
#' @param env Named character; set environment variables for a cron job.
#' Specify `Sys.getenv()` to inherit the variables from the current R session.
#' @export
#' @examples
#' \dontshow{if(interactive())
#' \{
#' }
#' f <- system.file(package = "cronR", "extdata", "helloworld.R")
#' cmd <- cron_rscript(f)
#' cmd
#'
#' cron_add(command = cmd, frequency = 'minutely',
#' id = 'test1', description = 'My process 1', tags = c('lab', 'xyz'))
#' cron_add(command = cmd, frequency = 'daily', at='7AM', id = 'test2')
#' cron_njobs()
#'
#' cron_ls()
#' cron_clear(ask=TRUE)
#' cron_ls()
#'
#' cmd <- cron_rscript(f, rscript_args = c("productx", "arg2", "123"))
#' cmd
#' cron_add(cmd, frequency = 'minutely', id = 'job1', description = 'Customers')
#' cron_add(cmd, frequency = 'hourly', id = 'job2', description = 'Weather')
#' cron_add(cmd, frequency = 'hourly', id = 'job3', days_of_week = c(1, 2))
#' cron_add(cmd, frequency = 'hourly', id = 'job4', at = '00:20', days_of_week = c(1, 2))
#' cron_add(cmd, frequency = 'daily', id = 'job5', at = '14:20')
#' cron_add(cmd, frequency = 'daily', id = 'job6', at = '14:20', days_of_week = c(0, 3, 5))
#' cron_add(cmd, frequency = 'daily', id = 'job7', at = '23:59', days_of_month = c(1, 30))
#' cron_add(cmd, frequency = 'monthly', id = 'job8', at = '10:30',
#' days_of_month = 'first', days_of_week = '*')
#' cron_add(cmd, frequency = '@reboot', id = 'job9', description = 'Good morning')
#' cron_add(cmd, frequency = '*/15 * * * *', id = 'job10', description = 'Every 15 min')
#' cron_ls()
#' cron_clear(ask=TRUE)
#' \dontshow{
#' \}
#' }
cron_add <- function(command, frequency="daily", at, days_of_month, days_of_week, months,
id, tags="", description="", dry_run=FALSE, user="", ask=TRUE, env=character()) {
if (length(env) > 0L && is.null(names(env))) {
stop("The argument `env` must be a named vector.")
}
crontab <- tryCatch(parse_crontab(user=user),
error=function(e) {
return( character() )
})
## make sure the id generated / used is unique
call <- match.call()
digested <- FALSE
if (missing(id)) {
digested <- TRUE
id <- digest(call)
}
if (length(crontab) && length(crontab$cronR)) {
if (id %in% sapply(crontab$cronR, "[[", "id")) {
if (digested) {
warning("This id was auto-generated by 'digest'; it is likely that ",
"you attempted to submit an identical job.")
}
stop("Can't add this job: a job with id '", id,
"' already exists.")
}
}
call_str <- paste( collapse="",
gsub(" +$", "", capture.output(call) )
)
job <- list(
min=NULL,
hour=NULL,
day_of_month=NULL,
month=NULL,
day_of_week=NULL,
command=NULL
)
frequency <- tolower(frequency)
switch( frequency,
minutely={
job[["min"]] <- "0-59"
job[["hour"]] <- "*"
job[["day_of_month"]] <- "*"
job[["month"]] <- "*"
job[["day_of_week"]] <- "*"
},
hourly={
job[["min"]] <- 0
job[["hour"]] <- "*"
job[["day_of_month"]] <- "*"
job[["month"]] <- "*"
job[["day_of_week"]] <- "*"
},
daily={
job[["min"]] <- 0
job[["hour"]] <- 0
job[["day_of_month"]] <- "*"
job[["month"]] <- "*"
job[["day_of_week"]] <- "*"
},
monthly={
job[["min"]] <- 0
job[["hour"]] <- 0
job[["day_of_month"]] <- 1
job[["month"]] <- "*"
job[["day_of_week"]] <- 1
},
yearly={
job[["min"]] <- 0
job[["hour"]] <- 0
job[["day_of_month"]] <- 1
job[["month"]] <- 1
job[["day_of_week"]] <- 1
},
message(sprintf("At your own risk: will set the cron schedule as is: '%s'", frequency))
)
if (!missing(days_of_week)) {
days_of_week <- ordinal_swap(days_of_week, adj=-1)
job[["day_of_week"]] <- paste( unique( sort(sapply(days_of_week, parse_day_of_week))), collapse=",")
}
if (!missing(days_of_month)) {
days_of_month <- ordinal_swap(days_of_month)
job[["day_of_month"]] <- paste( unique( sort(sapply(days_of_month, parse_day_of_month))), collapse=",")
}
if (!missing(months)) {
months <- ordinal_swap(months)
job[["month"]] <- paste( unique( sort(sapply(months, parse_month))), collapse=",")
}
if (!missing(at)) {
at_list <- lapply(at, parse_time)
job[["min"]] <- paste( sapply(at_list, "[[", "minutes"), collapse="," )
if(frequency != "hourly"){
job[["hour"]] <- paste( sapply(at_list, "[[", "hours"), collapse="," )
}
}
job[["command"]] <- command
if (any(is.null(job)))
stop("NULL commands in 'job!' Job is: ", paste(job, collapse=" ", sep=" "))
description <- unlist(strsplit( wrap(description), "\n" ))
if (length(description) > 1) {
description[2:length(description)] <-
paste0("## ", description[2:length(description)])
}
description <- paste(description, collapse="\n")
header <- paste( sep="\n", collapse="\n",
"## cronR job",
paste0("## id: ", id),
paste0("## tags: ", paste(tags, collapse=", ")),
paste0("## desc: ", description)
)
if (length(env) > 0L)
header <- paste0(header, "\n", paste(paste(names(env), env, sep = "="), collapse = "\n"))
if(frequency %in% c("minutely", "hourly", "daily", "monthly", "yearly")){
job_str <- paste( sep="\n", collapse="\n",
header,
paste(job, collapse=" ", sep=" ")
)
}else{
job_str <- paste( sep="\n", collapse="\n",
header,
paste(frequency, job$command, sep=" ")
)
}
if(ask){
cat(sep="", "Are you sure you want to add the specified cron job: '", job$command, "'? [y/n]: ")
input <- tolower(scan(what=character(), n=1, quiet=TRUE))
if (!input %in% "y") {
message("No action taken.")
return(invisible())
}
}
message("Adding cronjob:\n",
"---------------\n\n",
job_str
)
if (!dry_run) {
if(missing(user)){
old_crontab <- suppressWarnings(
system("crontab -l", intern=TRUE, ignore.stderr=TRUE)
)
}else{
old_crontab <- suppressWarnings(
system(sprintf("crontab -u %s -l", user), intern=TRUE, ignore.stderr=TRUE)
)
}
old_crontab[ old_crontab == " " ] <- ""
if (length(old_crontab)) {
new_crontab <- paste( sep="\n",
paste(old_crontab, collapse="\n"),
paste0(job_str, "\n")
)
} else {
new_crontab <- paste0(job_str, "\n")
}
tempfile <- tempfile()
on.exit( unlink(tempfile) )
cat(new_crontab, "\n", file=tempfile)
if(missing(user)){
system( paste("crontab", tempfile) )
}else{
system( paste("crontab -u", user, tempfile) )
}
}
return (invisible(job))
}
#' @rdname cron_add
cronjob <- cron_add
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.