161

How can I run a cron command with existing environmental variables?

If I am at a shell prompt I can type echo $ORACLE_HOME and get a path. This is one of my environmental variables that gets set in my ~/.profile. However, it seems that ~/.profile does not get loaded fron cron scripts and so my scripts fail because the $ORACLE_HOME variable is not set.

In this question the author mentions creating a ~/.cronfile profile which sets up variables for cron, and then he does a workaround to load all his cron commands into scripts he keeps in his ~/Cron directory. A file like ~/.cronfile sounds like a good idea, but the rest of the answer seems a little cumbersome and I was hoping someone could tell me an easier way to get the same result.

I suppose at the start of my scripts I could add something like source ~/.profile but that seems like it could be redundant.

So how can I get make my cron scripts load the variables from my interactive-shell profile?

2
  • 1
    How is adding source ~/.profile to a program redundant? Programs inherit their environment from the calling program. If that calling program is not your shell, then how will the decendant program get the environment that you want?
    – Arcege
    Dec 21, 2011 at 5:08
  • I already wrote my answer to a similar question here. It simply uses su -l to setup a normal login environment including the $PATH for either root or other specific user.
    – tasket
    Sep 14, 2018 at 18:52

11 Answers 11

196

In the crontab, before you command, add . $HOME/.profile. For example:

0 5 * * * . $HOME/.profile; /path/to/command/to/run

Cron knows nothing about your shell; it is started by the system, so it has a minimal environment. If you want anything, you need to have that brought in yourself.

13
  • 22
    What does the . before the script do? (not sure how I would man that). Why is this different from source?
    – cwd
    Dec 21, 2011 at 19:26
  • 29
    The . command is the original command for source. They are equivalent within the shell and a bit easier to type, especially within a crontab. To get more info, type help . or search for ^SHELL BUILTIN COMMANDS in the man page for bash or at the top of man zshbuiltins. Running type .` will tell you that the command is a builtin.
    – Arcege
    Dec 21, 2011 at 19:38
  • 11
    Depending on Linux distributions, you may need to change .profile by .bash_profile. Check which .profile file exists in the user's home directory. May 17, 2013 at 12:37
  • 5
    It's likely that if it isn't working, it's because the SHELL for the cron script isn't set to bash, so it's not executing the same way you might expect.
    – erik258
    Jul 10, 2015 at 18:32
  • 1
    Was under the impression that sourcing the .profile and then using ; would cause it to be run in another process that doesn't actually give them over to the command, and that it should be done as $HOME/.profile && /some/command/here
    – KuboMD
    Jul 15, 2019 at 18:44
65

Another option, which I find easier, is to run the script with cron and have the environment in the script.

In crontab -e file:

SHELL=/bin/bash

*/1 * * * * $HOME/cron_job.sh

In cron_job.sh file:

#!/bin/bash
source $HOME/.bash_profile
some_other_cmd

Any command after the source of .bash_profile will have your environment as if you logged in.

7
  • 9
    When running on an AWS Linux AMI, it didn't even occur to me that cron wouldn't be using /bin/bash as the shell. I kept wondering why things like cd /path/to/project; source .vars would work when I typed them manually but would fail (File not found) when included in a cronjob. The key line for me was setting SHELL=/bin/bash so that I could actually use familiar bash commands in each cronjob. /bin/sh/ (the default cron shell, apparently) is very limiting. Sep 9, 2016 at 14:05
  • It's too bad you can't specify Environment Files at the top of the cron like you can specify individual environment variables. Systemd has a EnvironmentFile service unit. Too bad cron doesn't have something similar.
    – radtek
    Jan 30, 2018 at 20:19
  • 5
    If $SHELL is /bin/sh, the source command does not exist. Use . instead.
    – Melle
    Jan 25, 2019 at 21:35
  • The solution is not working for SunOS 5.9. The following error is displayed: SHELL=/bin/bash crontab: error on previous line; unexpected character found in line. crontab: errors detected in input, no crontab file generated
    – birdman
    Jun 19, 2019 at 12:54
  • Hmm... I haven't used Sun in years but maybe their version of crontab doesn't support setting a default shell. In your case I would then have something along the lines of */1 * * * * /bin/bash $HOME/cron_job.sh. Jun 20, 2019 at 2:21
34

Another option, which I find easier, is to run the script with cron and tell bash to login (hence using /etc/profile.d/... environment definitions)

In crontab -e file:

*/1 * * * * bash -l -c './cron_job.sh'
*/1 * * * * bash -l -c 'php -f ./cron_job.php'

Any command after the source of .bash_profile will have your environment as if you logged in.

11

Q: How can I run a cron command with existing environmental variables?

The general practice is to specifically set all required environmental variables in a script that is to be run from a cron job.

5
  • I agree with @fpmurphy, that ways it also ensure the secured process environment. If you want to set only few variables from cron you can use /usr/bin/env command to set the variables and then can act as the environment process for the cronjob. Dec 21, 2011 at 6:24
  • daemontools' envdir comes to mind, too.
    – sr_
    Dec 21, 2011 at 9:42
  • 7
    I am all for security, but perhaps I can create a file ~/.cronvars and include that in the profile and also in my cron scripts. I don't want to hard code environmental variables in each of the scripts I run because when paths change hard coded paths in each file are not easy to maintain. Seems like that would allow a centralized place for the needed variables and still keep other variables from being loaded.
    – cwd
    Dec 21, 2011 at 23:21
  • This answer has been flagged as "not an answer" and I'm inclined to agree. The question asks "How can I run a cron command with existing environmental variables?" not whether it's a good idea or not. If you could amend your answer to show how to solve the problem (by incorporating existing env vars into a script), that would bring it on-topic. Thank you!
    – Jeff Schaller
    Jan 28, 2020 at 15:37
  • This is true for relatively simple bash scripts. As soon as your script involves other executables, each requiring a proper environment being set, including .bash_profile makes a lot more sense.
    – laurent
    Jan 18, 2021 at 15:34
9

This syntax definitely helps you. I do not understand the syntax, but it works. Oracle uses this syntax, when deploys Oracle Configuration Manager to crontab, hence, I believe that this is a right solution.

0 5 * * * SOME_ENV_VAR=some_value some_command some_parameters
1
  • I don't love this solution because it pollutes up the crontab, but yep, it works! Thanks. Apr 27, 2020 at 0:54
7

You can put global environment variables in /etc/environment. They are loaded when a program is run by the crontab. Ex :

env | grep ORACLE_HOME >> /etc/environment
service crontab restart

The drawback is that it is not restricted to a single user. Tested under CentOS7

1
  • This is a much better method when you want to apply the same environment to tens of lines, instead of editing the whole existing crontab... Jul 9, 2020 at 9:49
3

Add

BASH_ENV=/home/user/.profile

to the crontab. See How to guarantee availability of $BASH_ENV

3

I recently came across a case where I had to execute the overall cronjob as root but, at the same time, had to execute a subcommand as a different user (which required sourcing that user's environment). I went with the following approach:

# m  h  dom  mon  dow  user  command
*/5  *   *    *    *   root  (sudo -i -u <the user> command-to-be-run-as-user) && command-to-be-run-as-root

The crucial part is the argument -i that's being passed to sudo which will execute the given command in a separate login shell (which in turn means that the user's dotfiles will be sourced).

PS: Note that the user column is only available in /etc/crontab and the /etc/cron.d/* files.

2

Instead of setting profile, what helped me was setting the PATH. Some of the commands were not available in my cron scripts as the PATH is different.

ENVIRONMENT=prod
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/30 * * * * /opt/myscript1.sh
*/30 * * * * /opt/myscript2.sh
*/30 * * * * /opt/myscript3.sh

Setting PATH with the path of commands helped me. Even better if you can use a template and detemplatize later,

ENVIRONMENT={{ENVIRONMENT}}
PATH={{PATH}}
*/30 * * * * /opt/myscript1.sh
*/30 * * * * /opt/myscript2.sh
*/30 * * * * /opt/myscript3.sh

Passing variables to each item looks messy.

1

The solution, which worked for me, is described here.

You create a wrapper script, which calls . ~/.cronfile, and then does the things you want. This script is launched by cron.

In ~/.cronfile you specify the environment for your cron jobs.

-1

I put . ~/.dbus/session-bus/* at the top of my desired script :)

1
  • 2
    Welcome to U & L SE. Please expand your answer more so that it will benefit the readers.
    – Ramesh
    Sep 20, 2014 at 15:25

You must log in to answer this question.

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