1. Overview

In Linux systems, we can use cron jobs to automate tasks that run periodically. However, sometimes we need to start jobs manually, e.g., for debugging purposes. In such a case, we should take into account the cron job environment. In this tutorial, we’ll learn how to start a cron job in its proper environment manually.

2. cron Job Environment Setup

To properly run a cron job by hand, we need to provide all variables that are normally available in its environment. This set usually differs from the variables available in the default shell session. Further, they might be critical for passing configuration information, settings, and other data to programs, scripts, and processes that require them. We’ll start by exploring different methods of passing new variables to a cron task and verifying the environment during normal cron execution. Next, we’ll grab the cron‘s environment variables and inject them into an empty shell. Finally, we’ll create and run a manual job inside the newly created shell.

3. Providing Variables to cron Jobs

cron runs tasks in a non-interactive shell with very few preset variables. On top of those, we can pass variables to the cron task itself in several ways. To begin with, we can set the BASH_ENV variable to point to the file that contains definitions of variables. Subsequently, these variables are set for the cron job. A second method involves setting variables directly in the crontab file. However, not all Linux distributions support this way of exporting a variable in the cron job’s line. To test our approaches, let’s create a Bash script file at ~/cron_var_test.sh that checks variables we set in the environment:

$ cat ~/cron_var_test.sh
#!/bin/bash

echo "Variable from file pointed by BASH_ENV = $BASH_ENV_VAR"
echo "Variable from cron                     = $CRON_VAR"
echo "Variable from the job line             = $JOB_VAR"
$ chmod +x ~/cron_var_test.sh

Of course, we also make the script executable. As cat shows, we check for three variables. Now, let’s edit the crontab to add our script and define the variables that the script checks for:

$ crontab -l
...
BASH_ENV=~/bash_env_file
CRON_VAR=A_cron_variable
* * * * * export JOB_VAR=A_job_variable; ~/./cron_var_test.sh > ~/cron_var_output

Here, crontab -l shows us several modifications we made to the file, which correspond to different ways of passing variables to the job:

  • BASH_ENV variable is defined to contain the path to the ~/bash_env_file file, which contains environment variable definitions
  • CRON_VAR custom variable is defined with its value set as _A_cron_variable_
  • within our cron job line, JOB_VAR is defined with its value set as _A_job_variable_ and then we call our ~/cron_var_test.sh script

Also, let’s assume that the ~/bash_env_file file contains a BASH_ENV variable:

BASH_ENV_VAR="A bash env's variable"

Finally, let’s examine the result in cron_var_output after running the job:

$ cat cron_var_output
Variable from file pointed by BASH_ENV = A bash env's variable
Variable from cron                     = A_cron_variable
Variable from the job line             = A_job_variable

The output shows a variable from Bash, crontab, and a job line variable respectively.

4. Grabbing the Cron Environment

In addition to checking specific variables, we can also store the whole environment that cron provides for our job. Let’s define a job that redirects the output of env to the ~/cron_env file:

$ crontab -l
...
BASH_ENV=~/bash_env_file
CRON_VAR=A_cron_variable
* * * * * export JOB_VAR=A_job_variable; /usr/bin/env > ~/cron_env

We’re using the >> operator to redirect the full crontab environment to ~/cron_env. In this case, env has access to the same variables that our script showed and any other variable that the cron context provides. We store all this information in ~/cron_env.

5. Running Script With a Cron Environment Outside Cron

Now, let’s start our cron script from the interactive shell but with the cron environment. We do this by again using the env command, this time to read and apply the variables from ~/cron_var:

$ env -i $(cat ~/cron_env) ~/cron_var_test.sh
Variable from file pointed by BASH_ENV = A bash env's variable
Variable from cron                     = A_cron_variable
Variable from the job line             = A_job_variable

We’re using the -i option to create an empty environment and prevent variable leaks from the current shell. Subsequently, we populate the shell with variables saved in the cron_env file.

6. Improvement and Caveat

This fairly straightforward approach with env may fail if a variable’s value contains whitespace, e.g., CRON_VAR=”A cron variable”. So, let’s attempt to remedy this behavior with the cron_test_bed.sh script:

$ cat cron_test_bed.sh
#!/bin/bash

env_file="~/cron_env"
env_command="/usr/bin/env -i "

while IFS= read -r var
do
  env_command="${env_command} \"$var\" "
done < "$env_file"

/bin/bash -c "$env_command $@"
$ chmod +x cron_test_bed.sh

In detail, we read the variables file line by line. As each line represents a variable definition, we put it in quotes, building an env invocation in env_command. Finally, we run this command with /bin/bash -c. Now, let’s check the result:

$ ./cron_test_bed.sh ~/cron_var_test
Variable from file pointed by BASH_ENV = A bash env's variable
Variable from cron                     = A cron variable
Variable from the job line             = A_job_variable

However, we should be aware that we still have a few limitations on the content and form of the variable’s value. As an example, this method will fail on a multiline variable. So, we shouldn’t rely much on completely automatic parsing of variable values. Thus, manual variable inspection is still recommended. Finally, env* doesn’t parse variables coming from the file pointed out by the *BASH_ENV.

7. Conclusion

In this article, we learned how to define, check, and simulate a cron job’s environment. First, we showed ways to define variables around a cron job of a basic script. After that, we started the same script outside cron and verified its environment. Next, we grabbed the cron‘s variables and injected them into an empty shell. Subsequently, we ran the script inside this newly-prepared shell with the env command. Finally, we talked about potential problems with fully automatic parsing of the environment.