how can I make cron run a job right now, for testing/debugging? without changing the schedule!
I have a cron job that is scheduled to run everyday, other than changing the schedule, is there any other way to do a test run of the command right now to see if it works as intended?
EDIT: (from the comments) I know the command works fine when enter it in shell (my shell), but I want to know if it works correctly when cron runs it, it could be affected by ENV or shell specific stuff (~ expansion) or ownership and permission stuff or …
As far as I know there is no way to directly do that as cron has a special purpose – running schedules commands at a specific time. So the best thing is to either to manually create a (temporary) crontab entry or write a script which removes and resets the environment.
Explanation of "removes and resets the environment":
A wrapper script could be started with env -i
(which removes the environment), which would source a saved environment (making sure to export all variables, possibly by setting set -a
first) before starting your script.
The saved environment would be the default environment of a cron job, recorded by running env
(or declare -p
depending on what shell your cron jobs use) as a cronjob, saving its output.
You can simulate the cron user environment as explained in "Running a cron job manually and immediately". This will allow you to test the job works when it would be run as the cron user.
Excerpt from link:
Step 1: I put this line temporarily in the user’s crontab:
* * * * * /usr/bin/env > /home/username/tmp/cron-env
then took it out once the file was written.
Step 2: Made myself a little run-as-cron bash script containing:
#!/bin/bash
/usr/bin/env -i $(cat /home/username/tmp/cron-env) "$@"
So then, as the user in question, I was able to
run-as-cron /the/problematic/script --with arguments --and parameters
You can force the crontab to run with following command:
run-parts /etc/cron.daily
I found a solution that seems to be a little better for my purposes (commands shown for CentOS / RHEL-like, but should be adaptable basically anywhere).
This requires libfaketime
– you can build it yourself from source at https://github.com/wolfcw/libfaketime or just use one of the many packages from https://pkgs.org/download/libfaketime.
- Stop crond service –
service crond stop
- Figure out when your service should run by – https://crontab.guru is pretty useful for this.
- Run crond in foreground mode through libfaketime’s
faketime
tool (it lets you fake out the syscall for time lookups for any child processes).- I wouldn’t run this on a production server
faketime '2019-10-17 07:59:50' /usr/sbin/crond -n -x test,sch
[root@user-crontesting-dvc-01 ~]# faketime '2019-10-17 07:59:50' /usr/sbin/crond -n -x sch
debug flags enabled: sch
[4841] cron started
log_it: (CRON 4841) INFO (Syslog will be used instead of sendmail.)
log_it: (CRON 4841) INFO (RANDOM_DELAY will be scaled with factor 34% if used.)
log_it: (CRON 4841) INFO (running with inotify support)
[4841] GMToff=0
log_it: (CRON 4841) INFO (@reboot jobs will be run at computer's startup.)
[4841] Target time=1571299200, sec-to-wait=11
user [root:0:0:...] cmd="/usr/libexec/myexc/crontesting.cron > /dev/null 2> &1"
[4841] Target time=1571299260, sec-to-wait=60
log_it: (root 4844) CMD (/usr/libexec/myexc/crontesting.cron > /dev/null 2> &1)
log_it: (root 4843) CMDOUT (/bin/bash: -c: line 0: syntax error near unexpected token `&')
log_it: (root 4843) CMDOUT (/bin/bash: -c: line 0: `/usr/libexec/myexc/crontesting.cron > /dev/null 2> &1')
CronitorCLI has a command cronitor select
that lets you select and run any cron job from the command line. You do not need to create a Cronitor account to use it.
https://cronitor.io/docs/using-cronitor-cli
Here is an example:
ubuntu@ip-10-0-0-112:~$ cronitor select
Use the arrow keys to navigate: ↓ ↑
? Select job to run:
▸ /var/runner/src/bin/batch_reports.py runner.settings.prod
/var/runner/src/bin/trigger_reports.py runner.settings.prod
... etc ...
Not very elegant, but working: here is what I use to launch the job at the 5th line of my crontab:
eval "$(crontab -l | sed -n '5p' | tr -s ' ' | cut -d' ' -f 6-)"
It displays the crontab, gets the 5th line, replaces multiple spaces by a single space, takes everything from 6th column to the end, and then launches it with eval
.
I am quoting answer (view fully) from serverfault:
Execute crontab commands in-bulk :
crontab -l | grep -v '^#' | cut -f 6- -d ' ' | while read CMD; do eval $CMD; done
to run it under another user:
sudo -H -u username bash -c "crontab... "
After having the need myself to debug cron jobs,
I wrote the following script.
It tries hard to simulate the exact same conditions as cron
before running a command (that includes a modified environment,
but it also has to do with non-interactive shells,
no attached terminal, etc.).
Call it with your command/script as argument, and you have an instant and painless way to debug your cron job.
It is also hosted (and possibly updated) on GitHub: run-as-cron.sh
:
#!/bin/bash
# Run as if it was called from cron, that is to say:
# * with a modified environment
# * with a specific shell, which may or may not be bash
# * without an attached input terminal
# * in a non-interactive shell
function usage(){
echo "$0 - Run a script or a command as it would be in a cron job,"
"then display its output"
echo "Usage:"
echo " $0 [command | script]"
}
if [ "$1" == "-h" -o "$1" == "--help" ]; then
usage
exit 0
fi
if [ $(whoami) != "root" ]; then
echo "Only root is supported at the moment"
exit 1
fi
# This file should contain the cron environment.
cron_env="/root/cron-env"
if [ ! -f "$cron_env" ]; then
echo "Unable to find $cron_env"
echo "To generate it, run "/usr/bin/env > /root/cron-env" as a cron job"
exit 0
fi
# It will be a nightmare to expand "$@" inside a shell -c argument.
# Let's rather generate a string where we manually expand-and-quote the arguments
env_string="/usr/bin/env -i "
for envi in $(cat "$cron_env"); do
env_string="${env_string} $envi "
done
cmd_string=""
for arg in "$@"; do
cmd_string="${cmd_string} "${arg}" "
done
# Which shell should we use?
the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
echo "Running with $the_shell the following command: $cmd_string"
# Let's redirect the output into files
# and provide /dev/null as input
# (so that the command is executed without an open terminal
# on any standard file descriptor)
so=$(mktemp "/tmp/fakecron.out.XXXX")
se=$(mktemp "/tmp/fakecron.err.XXXX")
"$the_shell" -c "$env_string $cmd_string" > "$so" 2> "$se" < /dev/null
echo -e "Done. Here is 33[1mstdout 33[0m:"
cat "$so"
echo -e "Done. Here is 33[1mstderr 33[0m:"
cat "$se"
rm "$so" "$se"
Daladim’s answer was useful to me. It runs the command immediately, not after up to 59 seconds later as the "* * * * *
" style answers do. It presents the output as soon as the command runs.
I have extended it a bit, adding the ability to run as any user, not just root, fixing some corner case bugs, and adding a specific message with instructions to bootstrap the environment to match cron.
I put it up on GitHub.
Link a command called run-as-cron to this file from your path.
If you have a crontab entry like this:
0 0 * * * command.sh --option
test it like this:
run-as-cron command.sh --option
or as root:
sudo run-as-cron command.sh --option
or as some other user:
sudo su otheruser
run-as-cron command.sh --option
Note, that the very first time you run this as a user you’ll need to add a command to crontab -e
and wait a minute to grab the environment.
These questions and answers might also be useful:
Yea, you type the command and options directly into the CLI and run the job…
/bin/bash /some/script.sh --options
is just
$ ./some/script.sh --options
with a timer.
If you are auto-generating your crontab you can do something like this:
cat <<EOF >/tmp/mycrontab
# Run five minutes after midnight, every day - use bash to get PATH to include sbin so that ifconfig will work
5 0 * * * bash -lc '$HOME/myscript.sh'
# run on restart - use bash to get PATH to include sbin so that ifconfig will work
@reboot bash -lc '$HOME/myscript.sh'
# Installation time +1 min $(date -d +1min +'%Y-%m-%d %H:%M:%d' ) - use bash to get PATH to include sbin so that ifconfig will work
$(date -d +1min +'%M %H %d %m %w') bash -lc '$HOME/myscript.sh'
EOF
crontab /tmp/mycrontab
This will make ‘myscript.sh’ run after midnight, at reboot and at installation time and properly at the same time 7 years from now if the month, day and weekday align again at that time.
Just change the schedule to * * * * *
.
That would be the easiest and most reliable solution.