I normally have several problems with how cron executes scripts as they normally don't have my environment setup. Is there a way to invoke bash(?) in the same way cron does so I could test scripts before installing them?
-
I would suggest this solution: http://unix.stackexchange.com/questions/27289/how-can-i-run-a-cron-command-with-existing-environmental-variables – rudi May 14 '13 at 12:38
-
Taking @gregseth a bit further, I gave this solution: http://unix.stackexchange.com/questions/27289/how-can-i-run-a-cron-command-with-existing-environmental-variables/89596#89596 – Robert Brisita Sep 05 '13 at 22:38
-
A Cron job has many differences with a job run in your usual shell (a modified environment being only *one* of them). I have written an answer below, including a script that mimics all of the differences I have managed to notice. – Daladim Jul 14 '20 at 21:53
14 Answers
Add this to your crontab (temporarily):
* * * * * env > ~/cronenv
After it runs, do this:
env - `cat ~/cronenv` /bin/sh
This assumes that your cron runs /bin/sh, which is the default regardless of the user's default shell.
Footnote: if env
contains more advanced config, eg PS1=$(__git_ps1 " (%s)")$
, it will error cryptically env: ": No such file or directory
.
-
6note: if adding that to the global /etc/crontab, you'll need the username too. E.g. * * * * * root env > ~/cronenv – Greg Aug 20 '12 at 14:23
-
10Good, simple idea. For the impatient use '* * * * *' to run next minute, and remember to turn off again when you are done playing ;-) – Mads Buus Mar 12 '13 at 19:26
-
-
5
-
3I saved it to `/tmp/cronenv` to avoid any confusion about what `~` meant in that context. – Max Williams Mar 23 '16 at 15:54
-
5The importance of this answer cannot be underestimated. Worthy of a paragraph inclusion in a book. – Xofo Mar 28 '16 at 05:14
-
Note that this will fail with variables that have a space in the value. Maybe `export -p > file` + `source file`? – tokland Dec 01 '16 at 20:41
-
1is `env - `cat ~/cronenv` /bin/sh` should be written as cron job also? please give an example – JavaSa May 17 '18 at 21:29
-
@JavaSa You run `env - $(cat ~/cronenv) /bin/sh`, and it opens a shell replicating the crontab environment, where you can type in your cronjob command and see if it works as expected. – mwfearnley Sep 08 '20 at 11:16
-
This works wonders, but be aware you can still run into some differences with programs that are aware of whether they're being run in a TTY or not. – Zachary Vance Aug 02 '21 at 20:34
-
1My preferred syntax: `env -i $(cat ~/cronenv) /bin/sh`. `-i` is the same as `-` (--ignore-environment), but less cryptic. `$(...)` is the same as `\`...\`` – wisbucky Aug 27 '21 at 17:53
-
Careful about cron and current working dir when exporting and importing`env`, because if `PWD=...` is set in `env`, then `cd ... && ...` will not work in cron job – Sida Zhou Oct 28 '21 at 03:12
Cron provides only this environment by default :
HOME
user's home directoryLOGNAME
user's loginPATH=/usr/bin:/usr/sbin
SHELL=/usr/bin/sh
If you need more you can source a script where you define your environment before the scheduling table in the crontab.
-
7`.` is usually not part of `PATH` anymore, for [security reasons](http://superuser.com/q/156582/2259). – l0b0 May 14 '13 at 12:55
Couple of approaches:
Export cron env and source it:
Add
* * * * * env > ~/cronenv
to your crontab, let it run once, turn it back off, then run
env - `cat ~/cronenv` /bin/sh
And you are now inside a
sh
session which has cron's environmentBring your environment to cron
You could skip above exercise and just do a
. ~/.profile
in front of your cron job, e.g.* * * * * . ~/.profile; your_command
Use screen
Above two solutions still fail in that they provide an environment connected to a running X session, with access to
dbus
etc. For example, on Ubuntu,nmcli
(Network Manager) will work in above two approaches, but still fail in cron.* * * * * /usr/bin/screen -dm
Add above line to cron, let it run once, turn it back off. Connect to your screen session (screen -r). If you are checking the screen session has been created (with
ps
) be aware that they are sometimes in capitals (e.g.ps | grep SCREEN
)Now even
nmcli
and similar will fail.

- 12,004
- 13
- 54
- 83
-
2My preferred syntax for Option1 is `env -i $(cat ~/cronenv) /bin/sh`. `-i` is the same as `-` (--ignore-environment), but less cryptic. `$(...)` is the same as `\`...\`` – wisbucky Aug 27 '21 at 18:04
You can run:
env - your_command arguments
This will run your_command with empty environment.

- 26,717
- 34
- 141
- 196
-
4
-
2gregseth identified the variables included in the environment by cron. You can include that variables on the command line. $ env - PATH="$PATH" command args – DragonFax Jan 25 '12 at 19:49
-
5@DragonFax @dimba I use `env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command arguments` which seems to do the trick – l0b0 May 14 '13 at 12:56
-
This is a great simple method for testing scripts in "hostile" or unknown environments. If you're explicit enough that it'll run in this, it'll run under cron. – Oli Dec 18 '18 at 10:51
Depending on the shell of the account
sudo su
env -i /bin/sh
or
sudo su
env -i /bin/bash --noprofile --norc
From http://matthew.mceachen.us/blog/howto-simulate-the-cron-environment-1018.html

- 14,518
- 10
- 57
- 72
Answering six years later: the environment mismatch problem is one of the problems solved by systemd
"timers" as a cron replacement. Whether you run the systemd "service" from the CLI or via cron, it receives exactly the same environment, avoiding the environment mismatch problem.
The most common issue to cause cron jobs to fail when they pass manually is the restrictive default $PATH
set by cron, which is this on Ubuntu 16.04:
"/usr/bin:/bin"
By contrast, the default $PATH
set by systemd
on Ubuntu 16.04 is:
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
So there's already a better chance that a systemd timer is going to find a binary without further hassle.
The downside with systemd timers, is there's a slightly more time to set them up. You first create a "service" file to define what you want to run and a "timer" file to define the schedule to run it on and finally "enable" the timer to activate it.

- 20,322
- 12
- 75
- 92

- 12,961
- 6
- 44
- 49
Create a cron job that runs env and redirects stdout to a file. Use the file alongside "env -" to create the same environment as a cron job.

- 101
- 2
-
-
That will give you an empty environment. When you run scripts through cron, the environment isn't empty. – Jens Carlberg Apr 07 '10 at 21:56
Don't forget that since cron's parent is init, it runs programs without a controlling terminal. You can simulate that with a tool like this:

- 7,396
- 3
- 25
- 26
The accepted answer does give a way to run a script with the environment cron would use. As others pointed out, this is not the only needed criteria for debugging cron jobs.
Indeed, cron also uses a non-interactive terminal, without an attached input, etc.
If that helps, I have written a script that enables painlessly running a command/script as it would be run by cron. Invoke it with your command/script as first argument and you're good.
This script is also hosted (and possibly updated) on Github.
#!/bin/bash
# Run as if it was called from cron, that is to say:
# * with a modified environment
# * with a specific shell, which may or may not be bash
# * without an attached input terminal
# * in a non-interactive shell
function usage(){
echo "$0 - Run a script or a command as it would be in a cron job, then display its output"
echo "Usage:"
echo " $0 [command | script]"
}
if [ "$1" == "-h" -o "$1" == "--help" ]; then
usage
exit 0
fi
if [ $(whoami) != "root" ]; then
echo "Only root is supported at the moment"
exit 1
fi
# This file should contain the cron environment.
cron_env="/root/cron-env"
if [ ! -f "$cron_env" ]; then
echo "Unable to find $cron_env"
echo "To generate it, run \"/usr/bin/env > /root/cron-env\" as a cron job"
exit 0
fi
# It will be a nightmare to expand "$@" inside a shell -c argument.
# Let's rather generate a string where we manually expand-and-quote the arguments
env_string="/usr/bin/env -i "
for envi in $(cat "$cron_env"); do
env_string="${env_string} $envi "
done
cmd_string=""
for arg in "$@"; do
cmd_string="${cmd_string} \"${arg}\" "
done
# Which shell should we use?
the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
echo "Running with $the_shell the following command: $cmd_string"
# Let's route the output in a file
# and do not provide any input (so that the command is executed without an attached terminal)
so=$(mktemp "/tmp/fakecron.out.XXXX")
se=$(mktemp "/tmp/fakecron.err.XXXX")
"$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null
echo -e "Done. Here is \033[1mstdout\033[0m:"
cat "$so"
echo -e "Done. Here is \033[1mstderr\033[0m:"
cat "$se"
rm "$so" "$se"

- 198
- 10
By default, cron
executes its jobs using whatever your system's idea of sh
is. This could be the actual Bourne shell or dash
, ash
, ksh
or bash
(or another one) symlinked to sh
(and as a result running in POSIX mode).
The best thing to do is make sure your scripts have what they need and to assume nothing is provided for them. Therefore, you should use full directory specifications and set environment variables such as $PATH
yourself.

- 346,391
- 90
- 374
- 439
-
This is exactly what I'm trying to solve. We get tons of problems with scripts that assume something by mistake. Doing full paths and setting env variables and all the junk ends up with horrible huge unmaintainable cron lines – Jorge Vargas Apr 07 '10 at 21:26
-
re *sh sorry I grew up with bash = shell so it's hard to me to remember the alternatives (and sometimes better) shells. – Jorge Vargas Apr 07 '10 at 21:29
-
1@Jorge: lines in the crontab should be fairly short. You should do all the setup you need within the script (or a wrapper script). Here's a typical line from a crontab as an example: `0 0 * * 1 /path/to/executable >/dev/null 2>&1` and then, within "executable" I would set values for `$PATH`, etc., and use full directory specs to input and output files, etc. For example: `/path/to/do_something /another/path/input_file /another/path/to/output_file` – Dennis Williamson Apr 07 '10 at 23:15
Another simple way I've found (but may be error prone, I'm still testing) is to source your user's profile files before your command.
Editing a /etc/cron.d/ script:
* * * * * user1 comand-that-needs-env-vars
Would turn into:
* * * * * user1 source ~/.bash_profile; source ~/.bashrc; comand-that-needs-env-vars
Dirty, but it got the job done for me. Is there a way to simulate a login? Just a command you could run? bash --login
didn't work. It sounds like that would be the better way to go though.
EDIT: This seems to be a solid solution: http://www.epicserve.com/blog/2012/feb/7/my-notes-cron-directory-etccrond-ubuntu-1110/
* * * * * root su --session-command="comand-that-needs-env-vars" user1 -l

- 1,675
- 2
- 20
- 33
Answer https://stackoverflow.com/a/2546509/5593430 shows how to obtain the cron environment and use it for your script. But be aware that the environment can differ depending on the crontab file you use. I created three different cron entries to save the environment via env > log
. These are the results on an Amazon Linux 4.4.35-33.55.amzn1.x86_64.
1. Global /etc/crontab with root user
MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
PWD=/
LANG=en_US.UTF-8
SHLVL=1
HOME=/
LOGNAME=root
_=/bin/env
2. User crontab of root (crontab -e
)
SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=en_US.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root
_=/usr/bin/env
3. Script in /etc/cron.hourly/
MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
_=/bin/env
PWD=/
LANG=en_US.UTF-8
SHLVL=3
HOME=/
LOGNAME=root
Most importantly PATH
, PWD
and HOME
differ. Make sure to set these in your cron scripts to rely on a stable environment.
In my case, cron was executing my script using sh, which fail to execute some bash syntax.
In my script I added the env variable SHELL
:
#!/bin/bash
SHELL=/bin/bash

- 3,352
- 3
- 32
- 38
I don't believe that there is; the only way I know to test a cron job is to set it up to run a minute or two in the future and then wait.

- 1
- 3