Load users environment variables in a cronjob

How can I load the users environment variables in a cronjob?


I have a cronjob which should start a script every minute on my Ubuntu machine:

* * * * * /home/user/myscript.sh;

In this script I want to use environment variables like $JAVA_HOME and $M2_HOME to automatically start a build process. The problem is that those variables are set in the .bashrc via export JAVA_HOME=/../...

Therefore I source these files before executing my script:

* * * * * source ~/.bash_profile; source ~/.bashrc; /home/user/myscript.sh;

However, the variables aren't set! To test this I echo the environment variables via env to a log file:

env >> ~/env.log
echo $M2_HOME >> ~/env.log

The log file still doesn't contain the variables. Only the following are appended to the file (no $M2_HOME etc.):

LANGUAGE=en_US:en
HOME=/home/user
LOGNAME=user
PATH=/usr/bin:/bin
LANG=en_US.UTF-8
SHELL=/bin/sh
PWD=/home/user

If I run the script right in my shell all of the variables are print out to env.log.

The same happens when I put the source commands into the script file. Only if I remove the export keyword before the declaration of the variable it is visible by the echo $var command. But this is not a solution because I don't manage these declarations.


I tried the solution of @tripplee with the limited sh shell problem, but it still doesn't work. I further reduced it to a very simple cronjob without a second script to show the problematic:

* * * * * . $HOME/.bash_profile; . $HOME/.bashrc; env > $HOME/env.log;

cat /home/user/env.log:

LANGUAGE=en_US:en
HOME=/home/user
LOGNAME=user
PATH=/usr/bin:/bin
LANG=en_US.UTF-8
SHELL=/bin/sh
PWD=/home/user

Solution 1:

The cron job syntax is restricted to sh only so you cannot use Bashisms like ~. Fortunately, the fix is easy -- just replace it with $HOME. Similarly, source needs to be replaced with . (just a dot).

Of course, the scripts you source mustn't use Bash syntax either if you intend to use them from sh. Probably the easiest fix is to migrate these things to your script (or create a wrapper script for Cron to run).

Because inside your script, you can use bash if you like, provided of course that the shebang line is correctly #!/bin/bash (adjust the path if you have to) rather than #!/bin/sh.

If you don't like that for some reason, you can inline Bash in your crontab file by specifying it explicitly:

* * * * * bash -c 'source ~/.bash_profile; source ~/.bashrc; ./myscript.sh'

You should be able to see source: command not found and export: command not found in the emails sent by cron when things go wrong. Examining the error output is really important for troubleshooting.

Of course, if you have the standard .bashrc on Ubuntu, it will bail out immediately if you try to source it from a noninteractive script. Maybe better then to put the settings you want in a noninteractive use in another file (maybe .profile which will be read by sh by default)?

Solution 2:

To add on to tripleee's answer, you can cause all of your crons to be evaluated in bash (or the shell of your choice) by specifying the shell in the crontab.

SHELL=/bin/bash
* * * * * . $HOME/.bash_profile; . $HOME/.bashrc; env > $HOME/env.log;

edit and if you really want to see what it's doing, the following command should make it sufficiently verbose:

/bin/bash -x -c '. $HOME/.bash_profile; . $HOME/.bashrc; env > $HOME/env.log' > /tmp/cron.`date +\%s` 

set -x or the -x flag on the commandline will cause most shells to print every line evaluated.

To test on the commandline remove the \ from the date command. This is needed to prevent cron from evaluating %s as standard input.