164

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
    Commented 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
    Commented Sep 14, 2018 at 18:52

12 Answers 12

199

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
    Commented 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
    Commented Dec 21, 2011 at 19:38
  • 12
    Depending on Linux distributions, you may need to change .profile by .bash_profile. Check which .profile file exists in the user's home directory. Commented 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
    Commented 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
    Commented 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. Commented 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
    Commented Jan 30, 2018 at 20:19
  • 5
    If $SHELL is /bin/sh, the source command does not exist. Use . instead.
    – Melle
    Commented 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
    Commented 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. Commented 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. Commented Dec 21, 2011 at 6:24
  • daemontools' envdir comes to mind, too.
    – sr_
    Commented 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
    Commented 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
    Commented 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
    Commented Jan 18, 2021 at 15:34
10

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. Commented Apr 27, 2020 at 0:54
8

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... Commented Jul 9, 2020 at 9:49
4

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.

0

This is an old Question, and it has 11 answers as of today. I'm submitting the 12th answer b/c: 1) none of the others actually worked for me, and 2) I feel this answer is different.

The OP asked:

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

The OP also stated his preference for an answer that was less cumbersome than some others he had considered. Hopefully, this answer is the least cumbersome.

A less cumbersome approach is to use the --login option of su to import the user's (nearly) complete environment. This can be done without resort to the authentication requirement that usually accompanies su - if it is run from the root crontab.

This may be illustrated with a simple example. This example uses mpg123 - an MP3 audio player. However, many other examples could be constructed from commands that (like mpg123) require various environment variables to work properly, and these environment variables are often set without user notification.

mpg123 can ordinarily be run from the CLI without much difficulty. For example, to play a file in a continuous loop (after making the Bluetooth device connection):

$ mpg123 --loop -1 ~/rainstorm.mp3

But if we try to run this as a cron job, we may run into issues :) The issues are that cron typically has an austere environment, and that mpg123 depends on environment variables that are un-specified (and unclear).

We may resolve these issues as follows (pi is user pi):

  ... open the root crontab
$ sudo crontab -e
   ... add one line:

@reboot su pi -l -c /home/pi/start-mpg123.sh > /home/pi/start-mpg123.log 2>&1

start-mpg123.sh is straightforward; it sets up the BT connection, and then starts mpg123:

#!/usr/bin/bash

sleep 5
if [ "$(/usr/bin/bluetoothctl devices Connected)" != "Device DF:45:E9:00:BE:8B OontZ Angle solo DS E8B" ]; then
    /usr/bin/bluetoothctl connect DF:45:E9:00:BE:8B
fi
/usr/bin/mpg123 --loop -1 /home/pi/rainstorm.mp3
-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
    Commented Sep 20, 2014 at 15:25

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.