Running a cron job manually and immediately

Solution 1:

Here's what I did, and it seems to work in this situation. At least, it shows me an error, whereas running from the command line as the user doesn't show the error.


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

This solution could obviously be expanded to make use of sudo or such for more flexibility.

Hope this helps others.

Solution 2:

I present a solution based on Pistos answer, but without the flaws.

  • Add the following line to the crontab, e.g. using crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
    
  • Create a shell script which executes a command in the same environment as cron jobs run:

    #!/bin/sh
    
    . "$1"
    exec /usr/bin/env -i "$SHELL" -c ". $1; $2"
    

Use:

run-as-cron <cron-environment> <command>

e.g.

run-as-cron /home/username/cron-env 'echo $PATH'

Note that the second argument needs to be quoted if it requires an argument. The first line of the script loads a POSIX shell as interpreter. The second line sources the cron environment file. This is required to load the correct shell, which is stored in the environment variable SHELL. Then it loads an empty environment (to prevent leaking of environment variables into the new shell), launches the same shell which is used for cronjobs and loads the cron environment variables. Finally the command is executed.


Solution 3:

As crontab don't do the job, you'll to manipulate it's content :

crontab -l | grep -v '^#' | cut -f 6- -d ' ' | while read CMD; do eval $CMD; done

What it does :

  • lists crontab jobs
  • remove comment lines
  • remove the crontab configuration
  • then launch them one by one

Solution 4:

By default with most default cron daemons that I have seen, there is simply no way of telling cron to run right here right now. If you're using anacron, it may be possible I think to run a separate instance in the foreground.

If your scripts aren't running properly then you are not taking into account that

  • the script is running as a particular user
  • cron has a restricted environment (the most obvious manifestation of this is a different path).

From crontab(5):

Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd line of the crontab’s owner. PATH is set to "/usr/bin:/bin". HOME, SHELL, and PATH may be overridden by settings in the crontab; LOGNAME is the user that the job is running from, and may not be changed.

In general PATH is the biggest problem, so you need to:

  • Explicitly set the PATH within the script, while testing, to /usr/bin:/bin. You can do this in bash with export PATH="/usr/bin:/bin"
  • Explicitly set the proper PATH you want at the top of the crontab. e.g. PATH="/usr/bin:/bin:/usr/local/bin:/usr/sbin:/sbin"

If you need to run the script as another user without a shell (e.g. www-data), use sudo:

sudo -u www-data /path/to/crontab-script.sh

The first thing to test before all of that, of course, is that your script actually does what it is supposed to do from the command line. If you can't run it from the command line, it will obviously not work from with cron.


Solution 5:

Marco's script didn't work for me for some reason. I didn't have time to debug, so I wrote a Python script which does the same thing. It's longer, but: first, it works for me, and second, I find it easier to understand. Change "/tmp/cron-env" to where you saved your environment. Here it is:

#!/usr/bin/env python
from __future__ import division, print_function

import sys
import os

def main():
    if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'):
        print("Usage: {} CMD\n"
              "Run a command as cron would. Note that CMD must be quoted to be only one argument."
              .format(sys.argv[0]))
        sys.exit(1)
    _me, cmd = sys.argv
    env = dict(line.strip().split('=', 1) for line in open('/tmp/cron-env'))
    sh = env['SHELL']
    os.execvpe(sh, [sh, '-c', cmd], env)

if __name__ == '__main__':
    main()

Tags:

Cron