4

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
4

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)?

  • That is usefull. Though, the result is still the same. I updated the question. – Sebastian Barth Mar 6 '15 at 13:11
  • You forgot to fix the source: command not found error. Sorry about not mentioning it in my very initial answer. – tripleee Mar 6 '15 at 13:15
  • And I'm sorry to tell you that this doesn't work either. Same result. But thx for the hint! I updated the question again. – Sebastian Barth Mar 6 '15 at 13:19
  • 1
    Oh, if the Bash start-up files contain Bash-only syntax, that won't work either -- you'll get export: command not found instead. Better really to put these in a Bash script and call that from Cron. Updated answer. – tripleee Mar 6 '15 at 13:21
  • Still doesn't work! :/ – Sebastian Barth Mar 6 '15 at 13:42
4

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.

0

You don't have a full tty in cron. But if you run it via sudo -i you'll get one.

* * * * * sudo -u user -i bash -c '. $HOME/.bash_profile; . $HOME/.bashrc; env > $HOME/env.log;'

Or to start the script:

* * * * * sudo -u user -i bash -c '. $HOME/.bash_profile; . $HOME/.bashrc; /home/user/myscript.sh;'

If you put the source commands in a Bash script which starts the target script you'll get a neat:

* * * * * sudo -u user -i /home/user/mystarter.sh;
  • This post is partly based on serverfault.com/a/673487/98187 – Sebastian Barth Mar 6 '15 at 13:48
  • If that's the case, then Federico Sierra's comment would have worked for you. The problem is the interactive shell detection is causing your profile to short-circuit out before it gets to the export statements. – Andrew Domaszek Mar 6 '15 at 13:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.