Runs commands at random intervals: after each run, the delay until the next one is drawn from a probability distribution.
Original idea: https://news.ycombinator.com/item?id=46401425
cargo install pcron
pcron edit # edit the tab
pcron edit backup # edit tab.d/backup.tab
pcron list # list the commands
pcron server # run themThe tab is $XDG_CONFIG_HOME/pcron/tab (or ~/.config/pcron/tab, or $PCRON_TAB) plus every *.tab in the tab.d/ next to it; either may be missing. Each file stands on its own: variables don't carry over from one to the next.
Each command is the delay before its next run, in seconds, followed by a script:
# about once a minute, on average (a Poisson process)
exp(1/60) $ echo hi >> /tmp/pcron.log
# every 30 to 40 minutes of working hours
stretch: uniform(30, 40) * 60 @ mon-fri 9-18 $ notify-send stretch
# every 23 to 25 hours
notes: 24*3600 + normal(0, 1800) $$
cd ~/notes
git commit -am "$(date)" && git push
$$- The delay is made of numbers and distributions, with
+ - * / ^and parentheses. A distribution is sampled at every run; its arguments must be constant and non-negative. name:before the delay labels the command in the logs and inpcron list(optional, unique).@ days hoursmakes the delay count only while that window is open, in local time (orPCRON_TZ): days likemon-friorsat,sun, hours like9-18or9:30-12,14-18(either can be left out).$ ...runs the rest of the line; a$$block ends at a line containing only$$.NAME=valuelines set environment variables for the commands below them (no$expansion, like cron).PCRON_*variables configure pcron for the commands below them (scripts don't get them):PCRON_MIN_DELAYandPCRON_MAX_DELAYclamp every sampled delay (default 1s and 1 year);PCRON_TIMEOUTsends SIGTERM to a script that runs longer than that (default: no limit);PCRON_SHUTDOWN_TIMEOUTsends SIGKILL that long after a SIGTERM (default 60);PCRON_TZis the time zone of the windows, likeEurope/Madrid(default: local);PCRON_ON_FAILUREruns when a script fails (exits with an error, is killed, times out or cannot start), withPCRON_NAME,PCRON_REASON,PCRON_EXIT,PCRON_OUTPUTand more set; for examplePCRON_ON_FAILURE=notify-send "pcron: $PCRON_NAME failed".
- Scripts run with
$SHELL -c, whereSHELLcan be set in the tab (/bin/shby default). A run starts even if the previous one is still going, like cron. #starts a comment.
Distributions: beta(alpha, beta), cauchy(median, scale), chi_squared(k), exp(lambda), exp1(), fisher_f(m, n), frechet(location, scale, shape), gamma(shape, scale), gumbel(location, scale), inverse_gaussian(mean, shape), log_normal(mu, sigma), normal(mean, std_dev), normal_inverse_gaussian(alpha, beta), pareto(scale, shape), pert(min, max, mode, shape), poisson(lambda), skew_normal(location, scale, shape), standard_normal(), standard_uniform(), student_t(nu), triangular(min, max, mode), uniform(min, max), weibull(scale, shape).
pcron list --simulate 7d shows when the commands would run in the next week.
- It logs to stdout, including the scripts' output (
--output discardto drop it). - It saves the next run of each command in
$XDG_STATE_HOME/pcron/(or$PCRON_STATE), so a restart resumes them; a run that was missed while it was down happens right away. SIGHUPreloads the tab: unchanged commands keep their timers, and an invalid tab is ignored.SIGTERM/SIGINTstop it: running scripts get SIGTERM, and are killed afterPCRON_SHUTDOWN_TIMEOUTor when theirPCRON_TIMEOUTruns out, whichever comes first.
contrib/systemd/pcron.service is a systemd user unit to run it, and contrib/example.tab an example tab.
See pcron <command> --help for all the options.