339

I have a cron job that is scheduled to run every day. Other than changing the schedule, is there any other way to do a test run of the cron job right now to see if it works as intended?

I know the command works fine when enter it in my shell, but I want to know if it works correctly when cron runs it - it could be affected by environment variables, shell-specific stuff (such as ~ expansion), ownership and permissions, or other things.

7
  • I don't understand your question? Why not simply run the command?
    – favadi
    Commented Jul 10, 2012 at 10:45
  • 3
    So why not create new cron job run every minute with same command?
    – favadi
    Commented Jul 10, 2012 at 11:12
  • 19
    This is exactly what I ended up doing, but I wondered if there is a way to tell cron you want a test run on job no 7 ! Surely others have had this problem/request/wish before!
    – Ali
    Commented Jul 10, 2012 at 13:55
  • 7
    Very late to the scene here through google but there was everything wrong about favadi's reply. It was clear he wanted to test it from cron and without editing the crontab specifically to do that. LIttle worse than someone telling you what you want is wrong when they have'nt tried to understand the use case.
    – RichieHH
    Commented Sep 13, 2018 at 9:12
  • 1
    @Ali when you put it that way, I understand your rationale (that strictly speaking there is no cron-native way to make cron itself run the task at an improvised time). The answer from Vadim-Sluzky can be useful in many cases, but for those truly-hard-to-pin-down issues, Michael-Barton's answer is more appropriate. I guess if you just "un-select" the current correct one and leave it with no "officially" correct one, then votes alone will allow other things to rise to the top. Commented Dec 11, 2019 at 23:13

11 Answers 11

145

You can force the crontab to run with following command:

run-parts /etc/cron.daily
8
  • 24
    ...on the assumption that the OP's cron job (asked 3 years ago) is in cron.daily as opposed to an individual crontab.
    – Jeff Schaller
    Commented Nov 24, 2015 at 3:49
  • 55
    This does not fully simulate the cron user's environment however, so it is highly likely that you'll still have bugs because once you run your script as an actual cron job your PATH and other envvars may be different than the user you did run-parts /etc/cron.daily as. I am battling this bug right now, as my script will run fine with run-parts but fails when actually run under the cron user.
    – ArtHare
    Commented Jun 14, 2017 at 15:41
  • 1
    how do i run cron.d/localhost which is an rsnspshot command? Commented Sep 11, 2020 at 2:20
  • 1
    It is helpful to prefix command with sudo on some installations like Ubuntu. Commented Oct 21, 2020 at 0:22
  • 3
    This does not run the jobs in the correct environment. Also, why would you want to run all the daily jobs just to test run a single job?
    – Kusalananda
    Commented Dec 26, 2020 at 22:24
111

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
3
  • 2
    Useful trick. Of course, that won't help if you have a percent sign in your command.
    – basic6
    Commented Jul 11, 2018 at 7:09
  • Make sure to put the same shell used by the cron in the shebang line of the run-as-cron script. Commented Apr 23, 2020 at 15:50
  • Parsing (i.e., reading and interpreting) the output from env is unreliable and therefore dangerous. Environment variable values can contain any character other than nul; you should handle problematic characters, such as whitespace and quotes. Commented Aug 15, 2022 at 21:45
46

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.

1
  • Parsing the output from env is unreliable and therefore dangerous.  Environment variable values can contain any character other than nul; you would need to handle the standard problematic characters, such as whitespace and quotes. Commented Aug 12, 2022 at 21:38
23

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 ...
5
  • Thanks a lot for this. This is really nifty!
    – Xonshiz
    Commented Jan 6, 2020 at 16:33
  • 4
    requires some api key. 49 dollars per month. Commented Sep 4, 2020 at 9:42
  • 2
    @ЯрославРахматуллин, you can omit that step during that step (sudo cronitor configure --api-key <API-KEY>) during CronitorCLI installation and cronitor select will still work. Commented Feb 13, 2021 at 0:20
  • requires api key but free level available at the time of this writing; worked for me Commented Dec 27, 2023 at 17:51
  • Can just use empty string '' as the API Key and `select1 works.
    – Tim
    Commented Jan 3 at 22:21
7

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 \033[1mstdout\033[0m:"
cat "$so"
echo -e "Done. Here is \033[1mstderr\033[0m:"
cat "$se"
rm "$so" "$se"
6
  • The "generate a string" bit is problematic and would be solved in a safer way with the help of env.
    – Kusalananda
    Commented Dec 26, 2020 at 22:27
  • (1) If you don’t use any bash-isms, you can write your shebang as #!/bin/sh.  But see point 9.  (2) You specifically allow and handle a command and its arguments to be passed as arguments to your run-as-cron script; you should say so in your Usage message.  On the other hand, there’s no real need or reason to talk about a command or a script — scripts are commands.  (3) You test $1 for help options, which is good.  You should also check whether it is null (or missing). (4) [ condition₁ -o condition₂ ] is deprecated. … (Cont’d) Commented Aug 12, 2022 at 21:35
  • (Cont’d) … Use[ condition₁ ] || [ condition₂ ]. (Or you could use a case statement here.) (5) whoami is probably safe, but you should get into the habit of quoting all command substitutions: e.g., "$(whoami)". (6) You define a variable cron_env, but then you use the literal string /root/cron-env (needlessly) two more times after that. (7) sed is a fairly powerful program; you generally don’t need to combine it with grep. You could set the_shell with$(sed -n '/^SHELL=/ s///p' "$cron_env"). (8) And, after setting the_shell, you should verify that it’s not null. … (Cont’d) Commented Aug 12, 2022 at 21:35
  • (Cont’d) …  (9) You don’t actually need the curly braces in the env_string="${env_string} $envi " and cmd_string="${cmd_string} \"${arg}\" " commands.  (They don’t hurt, but you don’t need them.  See ${variable_name} doesn’t mean what you think it does ….)  Also, you could use cmd_string+=… — but note that this is non-POSIX, so, if you use it, you’ll need to keep the #!/bin/bash shebang.  (10) I agree with @Kusalananda: parsing "$@" is problematic and can be avoided.  … (Cont’d) Commented Aug 12, 2022 at 21:35
  • (Cont’d) …  (11) Also, parsing the output from env is unreliable and therefore dangerous.  Environment variable values can contain any character other than nul; your code does not handle the standard problematic characters, such as whitespace and quotes.  (12) Running a command with < /dev/null does not actually make it “executed without an attached terminal”.  (13) It might be useful to display the value of $? after the command runs.  … (Cont’d) Commented Aug 12, 2022 at 21:37
5

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:

2
  • (1) Please see my comments on Daladim’s answer; most of them also apply to yours.  (2) It’s good that you added attribution to your script (which is derived from Daladim’s), but it would be better if you also gave the original author credit by name. Commented Aug 13, 2022 at 21:48
  • 1
    @G-ManSays'ReinstateMonica' I've added the name Daladim to my github script. I will also look at the other comments you made when I can make time.
    – poleguy
    Commented Aug 18, 2022 at 15:41
-1

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.

1
  • 1
    That isn't what the question asks.
    – Chenmunka
    Commented Apr 22, 2022 at 17:28
-1

Just change the schedule to * * * * *.

That would be the easiest and most reliable solution.

-2

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.

  1. Stop crond service - service crond stop
  2. Figure out when your service should run by - https://crontab.guru is pretty useful for this.
  3. Run crond in foreground mode through libfaketime's faketime tool (it lets you fake out the syscall for time lookups for any child processes).
    1. I wouldn't run this on a production server
    2. 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')
-2

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... "

-3

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.

7
  • 2
    This is incorrect. First, the script to run is /some/script.sh, not ./some/script.sh. While this is probably a mere typo, a more serious problem is the fact that the interactive user's environment is not the same as cron's environment. Several suggestions have already been made how to test the command in the correct environment; please consider deleting your answer. Commented Feb 16, 2021 at 0:52
  • 1
    @berndbausch: Maybe not ‘merely’ a typo; there’s a widespread misconception that you need to use ./ to run a script, independent of location. Commented Apr 22, 2022 at 17:59
  • Wow, I have never heard of the "requirement" to put a dot at the beginning of a script's pathname. If that's true, it indicates a widespread and deep misunderstanding of UNIX filesystem principles. Let's correct it whenever we encounter it. Commented Apr 24, 2022 at 2:43
  • /some/script.sh is an absolute pathname. Meaning: The root directory contains a directory named some, where script.sh resides. ./some/script.sh on the other hand means that directory some is located in the current directory, not root. Commented Apr 24, 2022 at 2:46
  • 1
    If script.sh has execution permissions, /some/script.sh or ./some/script.sh can be run directly from the command line as-is. To complicate things a little, this assumes either that the interpreter is specified by a "hash-bang" clause in the first line of the script, or that the default shell is able to interpret the script. Commented Apr 24, 2022 at 2:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .