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.