Cron removing $ character from environment variable in shell script - bash

I have an environment variable in a docker container that stores a password with special characters. This particular password contains a $ in it. I output this environment variable in a shell script. If I run the script manually, everything is fine. When the cron runs the script, the $ and the following 2 characters are removed. I have tried escaping the special characters in several ways, the latest of which is below, but the outcome is the same (fine manually, missing with the cron). For this example, assume the password is blahblah$xy*blahblah, which is what I would see when running the script. If the cron runs the script, I would get blahblah*blahblah.
My script (testVars.sh):
#!/bin/bash
echo "Testing variables"
MY_PASS=$MY_PASSWORD
TEST_PASS=$(sed -e 's/[^a-zA-Z0-9,._+#%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' <<< $MY_PASSWORD)
echo ${MY_PASS}
echo ${TEST_PASS}
My cron:
BASH_ENV=/root/env_vars.sh
33 13 * * * root /opt/testVars.sh >> /opt/cron.log
I am assuming that it is actually possible to have a $ sign in a string in this way.

I solved it by added the following into the docker-entrypoint.sh file, just before my printenv command:
export MY_PASSWORD=$(sed -e 's/[^a-zA-Z0-9,._+#%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' <<<"$MY_PASSWORD")
Thanks to Ture Pålsson for pointing me in the right direction.

Related

run.sh contains activating conda environment - Calling it from bash works but crontab does not [duplicate]

I'm trying to get cron to call in the correct PATHs. When I run a Python script from shell the script runs fine as it uses the PATHs set in bashrc but when I use cron all the PATHs are not used from bashrc. Is there a file I can enter the PATHs into for cron like bashrc or a way to call the PATHs from bashrc?
Sorry I don't think I worded this correctly, I can get the correct script to run (meaning the PATH to the script in crontab is not the problem here), it's just when that script is running I run a build and this uses the PATHs set in .bashrc. When I run the script when I'm logged in, the .bashrc PATHs are pulled in. Since cron doesn't run in a shell per say it does not pull in .bashrc. Is there a way of pulling this in without having to write a bash script wrapper?
I used /etc/crontab. I used vi and entered in the PATHs I needed into this file and ran it as root. The normal crontab overwrites PATHs that you have set up. A good tutorial on how to do this.
The systemwide cron file looks like this:
This has the username field, as used by /etc/crontab.
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file.
# This file also has a username field, that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
42 6 * * * root run-parts --report /etc/cron.daily
47 6 * * 7 root run-parts --report /etc/cron.weekly
52 6 1 * * root run-parts --report /etc/cron.monthly
01 01 * * 1-5 root python /path/to/file.py
Most likely, cron is running in a very sparse environment. Check the environment variables cron is using by appending a dummy job which dumps env to a file like this:
* * * * * env > env_dump.txt
Compare that with the output of env in a normal shell session.
You can prepend your own environment variables to the local crontab by defining them at the top of your crontab.
Here's a quick fix to prepend $PATH to the current crontab:
# echo PATH=$PATH > tmp.cron
# echo >> tmp.cron
# crontab -l >> tmp.cron
# crontab tmp.cron
The resulting crontab will look similar to chrissygormley's answer, with PATH defined before the crontab rules.
You should put full paths in your crontab. That's the safest option.
If you don't want to do that you can put a wrapper script around your programs, and set the PATH in there.
e.g.
01 01 * * * command
becomes:
01 01 * * * /full/path/to/command
Also anything called from cron should be be very careful about the programs it runs, and probably set its own choice for the PATH variable.
EDIT:
If you don't know where the command is that you want execute which <command> from your shell and it'll tell you the path.
EDIT2:
So once your program is running, the first thing it should do is set PATH and any other required variable (e.g. LD_LIBRARY_PATH) to the values that are required for the script to run.
Basically instead of thinking how to modify the cron environment to make it more suitable for your program/script - make your script handle the environment it's given, by setting an appropriate one when it starts.
Adding a PATH definition into the user crontab with correct values will help...
I've filled mine with this line on top (after comments, and before cron jobs):
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
And it's enough to get all my scripts working... Include any custom path there if you need to.
Setting PATH right before the command line in my crontab worked for me:
* * * * * PATH=$PATH:/usr/local/bin:/path/to/some/thing
Make your variables work for you, this will allow access t
Define your PATH in /etc/profile.d/*.sh
System-wide environment variables
Files with the .sh extension in the /etc/profile.d directory get executed whenever a bash login shell is entered (e.g. when logging in from the console or over ssh), as well as by the DisplayManager when the desktop session loads.
You can for instance create the file /etc/profile.d/myenvvars.sh and set variables like this:
export JAVA_HOME=/usr/lib/jvm/jdk1.7.0
export PATH=$PATH:$JAVA_HOME/bin
Execute crontab with login option!
CRONTAB run script or command with Environment Variables
0 9 * * * cd /var/www/vhosts/foo/crons/; bash -l -c 'php -f ./download.php'
0 9 * * * cd /var/www/vhosts/foo/crons/; bash -l -c download.sh
Problem
Your script works when you run it from the console but fails in cron.
Cause
Your crontab doesn't have the right path variables (and possibly shell)
Solution
Add your current shell and path the crontab
Script to do it for you
#!/bin/bash
#
# Date: August 22, 2013
# Author: Steve Stonebraker
# File: add_current_shell_and_path_to_crontab.sh
# Description: Add current user's shell and path to crontab
# Source: http://brakertech.com/add-current-path-to-crontab
# Github: hhttps://github.com/ssstonebraker/braker-scripts/blob/master/working-scripts/add_current_shell_and_path_to_crontab.sh
# function that is called when the script exits (cleans up our tmp.cron file)
function finish { [ -e "tmp.cron" ] && rm tmp.cron; }
#whenver the script exits call the function "finish"
trap finish EXIT
########################################
# pretty printing functions
function print_status { echo -e "\x1B[01;34m[*]\x1B[0m $1"; }
function print_good { echo -e "\x1B[01;32m[*]\x1B[0m $1"; }
function print_error { echo -e "\x1B[01;31m[*]\x1B[0m $1"; }
function print_notification { echo -e "\x1B[01;33m[*]\x1B[0m $1"; }
function printline {
hr=-------------------------------------------------------------------------------------------------------------------------------
printf '%s\n' "${hr:0:${COLUMNS:-$(tput cols)}}"
}
####################################
# print message and exit program
function die { print_error "$1"; exit 1; }
####################################
# user must have at least one job in their crontab
function require_gt1_user_crontab_job {
crontab -l &> /dev/null
[ $? -ne 0 ] && die "Script requires you have at least one user crontab job!"
}
####################################
# Add current shell and path to user's crontab
function add_shell_path_to_crontab {
#print info about what's being added
print_notification "Current SHELL: ${SHELL}"
print_notification "Current PATH: ${PATH}"
#Add current shell and path to crontab
print_status "Adding current SHELL and PATH to crontab \nold crontab:"
printline; crontab -l; printline
#keep old comments but start new crontab file
crontab -l | grep "^#" > tmp.cron
#Add our current shell and path to the new crontab file
echo -e "SHELL=${SHELL}\nPATH=${PATH}\n" >> tmp.cron
#Add old crontab entries but ignore comments or any shell or path statements
crontab -l | grep -v "^#" | grep -v "SHELL" | grep -v "PATH" >> tmp.cron
#load up the new crontab we just created
crontab tmp.cron
#Display new crontab
print_good "New crontab:"
printline; crontab -l; printline
}
require_gt1_user_crontab_job
add_shell_path_to_crontab
Source
https://github.com/ssstonebraker/braker-scripts/blob/master/working-scripts/add_current_shell_and_path_to_crontab.sh
Sample Output
The simplest workaround I've found looks like this:
* * * * * root su -l -c command
This example invokes su as root user and starts the shell with the user's full environment, including $PATH, set as if they were logged in. It works the same on different distros, is more reliable than sourcing .bashrc (which hasn't worked for me) and avoids hardcoding specific paths which can be a problem if you're providing an example or setup tool and don't know what distro or file layout on the user's system.
You can also specify the username after su if you want a different user than root, but you should probably leave the root parameter before su command since this ensures su has sufficient privileges to switch to any user you specify.
The default environment for cron jobs is very sparse and may be very different from the environment you develop your python scripts in. For a script that might be run in cron, any environment that you depend on should be set explicitly. In the cron file itself, include full paths to python executables and to your python scripts.
On my AIX cron picks up it's environmental variables from /etc/environment ignoring what is set in the .profile.
Edit: I also checked out a couple of Linux boxes of various ages and these appear to have this file as well, so this is likely not AIX specific.
I checked this using joemaller's cron suggestion and checking the output before and after editing the PATH variable in /etc/environment.
If you don't want to have to make the same edits in various places, then roughly do this:
* * * * * . /home/username/.bashrc && yourcommand all of your args
The . space and then the path to .bashrc and the && command are the magic there to get your environment changes into the running bash shell. Too, if you really want the shell to be bash, it is a good idea to have a line in your crontab:
SHELL=/bin/bash
Hope it helps someone!
#Trevino: your answer helped me solve my problem. However, for a beginner, trying to give a step by step approach.
Get your current installation of java via $ echo $JAVA_HOME
$ crontab -e
* * * * * echo $PATH - this lets you understand whats the PATH value being used by crontab at present. Run crontab and grab $PATH value used by crontab.
Now edit crontab again to set your desired java bin path: a) crontab -e; b) PATH=<value of $JAVA_HOME>/bin:/usr/bin:/bin (its a sample path); c) now your scheduled job/script like */10 * * * * sh runMyJob.sh &; d) remove echo $PATH from crontab as its not needed now.
Set the required PATH in your cron
crontab -e
Edit: Press i
PATH=/usr/local/bin:/usr/local/:or_whatever
10 * * * * your_command
Save and exit :wq
I know this has been answered already, but I thought that his would be useful to some. I had a similar issue that I recently solved (found here) and here are the highlights of the steps I took to answer this question:
make sure that you have the variables you need in PYTHONPATH (found here and here and for more info here) inside the .profile or .bash_profile for any shell you want to test your script in to make sure it works.
edit your crontab to include the directories needed to run your script in a cron job (found here and here)
a) be sure to include the root directory in the PATH variable (.) as explained here (basically if you are running an executable with your command it needs to be able to find root or the directory where the executable is stored) and probably these (/sbin:/bin:/usr/sbin:/usr/bin)
in your crontab file, create a cronjob that will change directory to the directory where you have successfully ran the script before (i.e. Users/user/Documents/foo)
a) This will look like the following:
* * * * cd /Users/user/Documents/foo; bar -l doSomething -v
Should you use webmin then these are the steps how to set the PATH value:
System
-> Scheduled Cron Jobs
-> Create a new environment variable
-> For user: <Select the user name>
-> Variable name: PATH
-> Value: /usr/bin:/bin:<your personal path>
-> Add environment variable: Before all Cron jobs for user

Debian bash script, checking gateway by ping script as cron job [duplicate]

I'm trying to get cron to call in the correct PATHs. When I run a Python script from shell the script runs fine as it uses the PATHs set in bashrc but when I use cron all the PATHs are not used from bashrc. Is there a file I can enter the PATHs into for cron like bashrc or a way to call the PATHs from bashrc?
Sorry I don't think I worded this correctly, I can get the correct script to run (meaning the PATH to the script in crontab is not the problem here), it's just when that script is running I run a build and this uses the PATHs set in .bashrc. When I run the script when I'm logged in, the .bashrc PATHs are pulled in. Since cron doesn't run in a shell per say it does not pull in .bashrc. Is there a way of pulling this in without having to write a bash script wrapper?
I used /etc/crontab. I used vi and entered in the PATHs I needed into this file and ran it as root. The normal crontab overwrites PATHs that you have set up. A good tutorial on how to do this.
The systemwide cron file looks like this:
This has the username field, as used by /etc/crontab.
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file.
# This file also has a username field, that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
42 6 * * * root run-parts --report /etc/cron.daily
47 6 * * 7 root run-parts --report /etc/cron.weekly
52 6 1 * * root run-parts --report /etc/cron.monthly
01 01 * * 1-5 root python /path/to/file.py
Most likely, cron is running in a very sparse environment. Check the environment variables cron is using by appending a dummy job which dumps env to a file like this:
* * * * * env > env_dump.txt
Compare that with the output of env in a normal shell session.
You can prepend your own environment variables to the local crontab by defining them at the top of your crontab.
Here's a quick fix to prepend $PATH to the current crontab:
# echo PATH=$PATH > tmp.cron
# echo >> tmp.cron
# crontab -l >> tmp.cron
# crontab tmp.cron
The resulting crontab will look similar to chrissygormley's answer, with PATH defined before the crontab rules.
You should put full paths in your crontab. That's the safest option.
If you don't want to do that you can put a wrapper script around your programs, and set the PATH in there.
e.g.
01 01 * * * command
becomes:
01 01 * * * /full/path/to/command
Also anything called from cron should be be very careful about the programs it runs, and probably set its own choice for the PATH variable.
EDIT:
If you don't know where the command is that you want execute which <command> from your shell and it'll tell you the path.
EDIT2:
So once your program is running, the first thing it should do is set PATH and any other required variable (e.g. LD_LIBRARY_PATH) to the values that are required for the script to run.
Basically instead of thinking how to modify the cron environment to make it more suitable for your program/script - make your script handle the environment it's given, by setting an appropriate one when it starts.
Adding a PATH definition into the user crontab with correct values will help...
I've filled mine with this line on top (after comments, and before cron jobs):
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
And it's enough to get all my scripts working... Include any custom path there if you need to.
Setting PATH right before the command line in my crontab worked for me:
* * * * * PATH=$PATH:/usr/local/bin:/path/to/some/thing
Make your variables work for you, this will allow access t
Define your PATH in /etc/profile.d/*.sh
System-wide environment variables
Files with the .sh extension in the /etc/profile.d directory get executed whenever a bash login shell is entered (e.g. when logging in from the console or over ssh), as well as by the DisplayManager when the desktop session loads.
You can for instance create the file /etc/profile.d/myenvvars.sh and set variables like this:
export JAVA_HOME=/usr/lib/jvm/jdk1.7.0
export PATH=$PATH:$JAVA_HOME/bin
Execute crontab with login option!
CRONTAB run script or command with Environment Variables
0 9 * * * cd /var/www/vhosts/foo/crons/; bash -l -c 'php -f ./download.php'
0 9 * * * cd /var/www/vhosts/foo/crons/; bash -l -c download.sh
Problem
Your script works when you run it from the console but fails in cron.
Cause
Your crontab doesn't have the right path variables (and possibly shell)
Solution
Add your current shell and path the crontab
Script to do it for you
#!/bin/bash
#
# Date: August 22, 2013
# Author: Steve Stonebraker
# File: add_current_shell_and_path_to_crontab.sh
# Description: Add current user's shell and path to crontab
# Source: http://brakertech.com/add-current-path-to-crontab
# Github: hhttps://github.com/ssstonebraker/braker-scripts/blob/master/working-scripts/add_current_shell_and_path_to_crontab.sh
# function that is called when the script exits (cleans up our tmp.cron file)
function finish { [ -e "tmp.cron" ] && rm tmp.cron; }
#whenver the script exits call the function "finish"
trap finish EXIT
########################################
# pretty printing functions
function print_status { echo -e "\x1B[01;34m[*]\x1B[0m $1"; }
function print_good { echo -e "\x1B[01;32m[*]\x1B[0m $1"; }
function print_error { echo -e "\x1B[01;31m[*]\x1B[0m $1"; }
function print_notification { echo -e "\x1B[01;33m[*]\x1B[0m $1"; }
function printline {
hr=-------------------------------------------------------------------------------------------------------------------------------
printf '%s\n' "${hr:0:${COLUMNS:-$(tput cols)}}"
}
####################################
# print message and exit program
function die { print_error "$1"; exit 1; }
####################################
# user must have at least one job in their crontab
function require_gt1_user_crontab_job {
crontab -l &> /dev/null
[ $? -ne 0 ] && die "Script requires you have at least one user crontab job!"
}
####################################
# Add current shell and path to user's crontab
function add_shell_path_to_crontab {
#print info about what's being added
print_notification "Current SHELL: ${SHELL}"
print_notification "Current PATH: ${PATH}"
#Add current shell and path to crontab
print_status "Adding current SHELL and PATH to crontab \nold crontab:"
printline; crontab -l; printline
#keep old comments but start new crontab file
crontab -l | grep "^#" > tmp.cron
#Add our current shell and path to the new crontab file
echo -e "SHELL=${SHELL}\nPATH=${PATH}\n" >> tmp.cron
#Add old crontab entries but ignore comments or any shell or path statements
crontab -l | grep -v "^#" | grep -v "SHELL" | grep -v "PATH" >> tmp.cron
#load up the new crontab we just created
crontab tmp.cron
#Display new crontab
print_good "New crontab:"
printline; crontab -l; printline
}
require_gt1_user_crontab_job
add_shell_path_to_crontab
Source
https://github.com/ssstonebraker/braker-scripts/blob/master/working-scripts/add_current_shell_and_path_to_crontab.sh
Sample Output
The simplest workaround I've found looks like this:
* * * * * root su -l -c command
This example invokes su as root user and starts the shell with the user's full environment, including $PATH, set as if they were logged in. It works the same on different distros, is more reliable than sourcing .bashrc (which hasn't worked for me) and avoids hardcoding specific paths which can be a problem if you're providing an example or setup tool and don't know what distro or file layout on the user's system.
You can also specify the username after su if you want a different user than root, but you should probably leave the root parameter before su command since this ensures su has sufficient privileges to switch to any user you specify.
The default environment for cron jobs is very sparse and may be very different from the environment you develop your python scripts in. For a script that might be run in cron, any environment that you depend on should be set explicitly. In the cron file itself, include full paths to python executables and to your python scripts.
On my AIX cron picks up it's environmental variables from /etc/environment ignoring what is set in the .profile.
Edit: I also checked out a couple of Linux boxes of various ages and these appear to have this file as well, so this is likely not AIX specific.
I checked this using joemaller's cron suggestion and checking the output before and after editing the PATH variable in /etc/environment.
If you don't want to have to make the same edits in various places, then roughly do this:
* * * * * . /home/username/.bashrc && yourcommand all of your args
The . space and then the path to .bashrc and the && command are the magic there to get your environment changes into the running bash shell. Too, if you really want the shell to be bash, it is a good idea to have a line in your crontab:
SHELL=/bin/bash
Hope it helps someone!
#Trevino: your answer helped me solve my problem. However, for a beginner, trying to give a step by step approach.
Get your current installation of java via $ echo $JAVA_HOME
$ crontab -e
* * * * * echo $PATH - this lets you understand whats the PATH value being used by crontab at present. Run crontab and grab $PATH value used by crontab.
Now edit crontab again to set your desired java bin path: a) crontab -e; b) PATH=<value of $JAVA_HOME>/bin:/usr/bin:/bin (its a sample path); c) now your scheduled job/script like */10 * * * * sh runMyJob.sh &; d) remove echo $PATH from crontab as its not needed now.
Set the required PATH in your cron
crontab -e
Edit: Press i
PATH=/usr/local/bin:/usr/local/:or_whatever
10 * * * * your_command
Save and exit :wq
I know this has been answered already, but I thought that his would be useful to some. I had a similar issue that I recently solved (found here) and here are the highlights of the steps I took to answer this question:
make sure that you have the variables you need in PYTHONPATH (found here and here and for more info here) inside the .profile or .bash_profile for any shell you want to test your script in to make sure it works.
edit your crontab to include the directories needed to run your script in a cron job (found here and here)
a) be sure to include the root directory in the PATH variable (.) as explained here (basically if you are running an executable with your command it needs to be able to find root or the directory where the executable is stored) and probably these (/sbin:/bin:/usr/sbin:/usr/bin)
in your crontab file, create a cronjob that will change directory to the directory where you have successfully ran the script before (i.e. Users/user/Documents/foo)
a) This will look like the following:
* * * * cd /Users/user/Documents/foo; bar -l doSomething -v
Should you use webmin then these are the steps how to set the PATH value:
System
-> Scheduled Cron Jobs
-> Create a new environment variable
-> For user: <Select the user name>
-> Variable name: PATH
-> Value: /usr/bin:/bin:<your personal path>
-> Add environment variable: Before all Cron jobs for user

Asterisk check script not running from crontab

Asterisk check script is not running only when run by crontab, but runs by ./script.sh and sh script.sh. Here is the script:
date
asterisk -rx "show channels"
asterisk -rx "zap show channels"
Then I >> into a log file. When I run manually via ./ or sh with >> log.log it works, just not as a crontab listed as
* * * * * /root/script.sh
I have tried adding #!/bash/sh at the top of the script and only the date is shown no matter what I try. I am a noob to bash scripts and I'm trying to learn.
Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
Have you checked your path? It's almost certainly different when run under cron. (You can set PATH=... in your crontab. From the command line, type "echo $PATH" to see what you're expecting.) It might be more standard to provide full paths to date, asterisk and your log file inside script.sh (e.g., "/bin/date /path/to/asterisk ....") – mjk

Why does using set -e cause my script to fail when called in crontab?

I have a bash script that performs several file operations. When any user runs this script, it executes successfully and outputs a few lines of text but when I try to cron it there are problems. It seems to run (I see an entry in cron log showing it was kicked off) but nothing happens, it doesn't output anything and doesn't do any of its file operations. It also doesn't appear in the running processes anywhere so it appears to be exiting out immediately.
After some troubleshooting I found that removing "set -e" resolved the issue, it now runs from the system cron without a problem. So it works, but I'd rather have set -e enabled so the script exits if there is an error. Does anyone know why "set -e" is causing my script to exit?
Thanks for the help,
Ryan
With set -e, the script will stop at the first command which gives a non-zero exit status. This does not necessarily mean that you will see an error message.
Here is an example, using the false command which does nothing but exit with an error status.
Without set -e:
$ cat test.sh
#!/bin/sh
false
echo Hello
$ ./test.sh
Hello
$
But the same script with set -e exits without printing anything:
$ cat test2.sh
#!/bin/sh
set -e
false
echo Hello
$ ./test2.sh
$
Based on your observations, it sounds like your script is failing for some reason (presumably related to the different environment, as Jim Lewis suggested) before it generates any output.
To debug, add set -x to the top of the script (as well as set -e) to show commands as they are executed.
When your script runs under cron, the environment variables and path may be set differently than when the script is run directly by a user. Perhaps that's why it behaves differently?
To test this: create a new script that does nothing but printenv and echo $PATH.
Run this script manually, saving the output, then run it as a cron job, saving that output.
Compare the two environments. I am sure you will find differences...an interactive
login shell will have had its environment set up by sourcing a ".login", ".bash_profile",
or similar script (depending on the user's shell). This generally will not happen in a
cron job, which is usually the reason for a cron job behaving differently from running
the same script in a login shell.
To fix this: At the top of the script, either explicitly set the environment variables
and PATH to match the interactive environment, or source the user's ".bash_profile",
".login", or other setup script, depending on which shell they're using.

How to simulate the environment cron executes a script with?

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?
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.
Cron provides only this environment by default :
HOME user's home directory
LOGNAME user's login
PATH=/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.
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 environment
Bring 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.
You can run:
env - your_command arguments
This will run your_command with empty environment.
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
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.
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.
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:
http://libslack.org/daemon/
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.
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"
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
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
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.

Resources