Contents

如何在Cron工作中加载环境变量

1. 概述

cron 执行作业时,它不会从~/.bashrc~/.bash_profile/etc/profile 等文件中加载环境变量 。这是因为cron从非交互式、非登录 shell 运行作业。这可能是个问题,因为某些程序需要环境变量才能正常运行。

在本教程中,我们将讨论从cron作业加载环境变量的不同方法。

2. 设置BASH_ENV变量

crontab 文件中有bash脚本时,我们可以使用BASH_ENV变量。我们用脚本的路径设置它,bash将在运行作业之前执行该脚本。这样,我们就可以运行一个脚本,比如*/etc/profile来加载我们需要的环境变量。此外,这非常方便,因为我们可以编写任何脚本并使用BASH_ENV*调用它。

让我们在运行脚本之前加载*/etc/profilecrontab*文件中设置一个作业:

* * * * * BASH_ENV=/etc/profile /home/blogdemo/print_envs.sh

请注意,此作业每分钟运行一次。另外,让我们编写*/home/blogdemo/print_envs.sh*脚本,使用printenv 将所有环境变量打印到临时文件中:

#!/bin/bash
printenv > /tmp/print_envs_result

现在,在使用chmod +x /home/blogdemo/print_envs.sh设置脚本的执行权限后,我们将等待一分钟以查看结果:

$ wc -l /tmp/print_envs_result
38 /tmp/print_envs_result
$ grep PS1= /tmp/print_envs_result
PS1=\[[email protected]](/cdn_cgi/l/email_protection)\h:\w\$

正如我们所见,print_envs.sh脚本加载了 38 个环境变量。例如,它加载了PS1变量。

**我们还可以使用BASH_ENV来执行自定义脚本。当我们想要加载多个文件或添加更多变量时,这很有用。让我们编写脚本/home/blogdemo/preload.sh加载/etc/profile~/.bash_profile~/.bashrc,并导出 另一个变量:

#!/bin/bash
. /etc/profile
. ~/.bash_profile
. ~/.bashrc
export LEARNING_FROM=blogdemo

现在,我们将修改crontab文件以使用*/home/blogdemo/preload.sh*:

* * * * * BASH_ENV=/home/blogdemo/preload.sh /home/blogdemo/print_envs.sh

等待一分钟后,我们得到结果:

$ wc -l /tmp/print_envs_result 
41 /tmp/print_envs_result 
$ grep LEARNING_FROM /tmp/print_envs_result 
LEARNING_FROM=blogdemo

我们可以注意到,我们现在有 41 个环境变量。此外,我们还有值为“blogdemo ”的变量LEARNING_FROM。**

3. 用 Bash 包装作业

假设我们有这个crontab文件:

* * * * * printenv > /tmp/print_envs_result

**由于printenv不是bash脚本,我们不能使用BASH_ENV来加载环境变量。但是,我们可以用bash包装它。**我们通过在作业前添加bash -c并将作业括在双引号内来实现。当我们使用-c参数时,bash会从参数中读取命令并执行。

让我们在将bash -c添加到作业后使用BASH_ENV

* * * * * BASH_ENV=/etc/profile bash -c "printenv > /tmp/print_envs_result"

在cron运行作业后,我们可以看到printenv从*/etc/profile*加载了所有环境变量:

$ wc -l /tmp/print_envs_result
38 /tmp/print_envs_result
$ grep PS1= /tmp/print_envs_result
PS1=\[[email protected]](/cdn_cgi/l/email_protection)\h:\w\$

如果我们需要添加更多环境变量或加载更多文件,我们可以编写一个新脚本来包含我们需要的文件。现在,我们有两个选择。我们可以使用BASH_ENV加载新脚本,就像我们在上一节中所做的那样。或者,我们可以将原始作业从crontab文件移动到新脚本的末尾。

让我们将作业包装在一个名为*/home/blogdemo/wrap_printenv.sh*的新脚本中:

#!/bin/bash
. /etc/profile
. ~/.bash_profile
. ~/.bashrc
export LEARNING_FROM=blogdemo
#now, we run the original job
printenv > /tmp/print_envs_result

最后,我们更改crontab文件以运行新脚本:

* * * * * /home/blogdemo/wrap_printenv.sh

与上一节类似,我们可以在结果中看到环境变量:

$ wc -l /tmp/print_envs_result 
41 /tmp/print_envs_result 
$ grep LEARNING_FROM /tmp/print_envs_result 
LEARNING_FROM=blogdemo

4. 使用登录 Shell 运行作业

当我们将bash作为登录 shell 运行时,它会加载文件*/etc/profile*、~/.bash_pofile~/.bash_login和*~/.profile*。如果我们想从cron作业中模拟这个,我们可以运行一个登录 shell 来执行该作业。这样,bash将运行所有登录脚本,从这些文件加载环境变量。为此,我们必须在作业前加上bash -l -c并将作业括在双引号内。当我们使用*-l参数时,我们强制bash*作为登录 shell 运行。

让我们使用登录 shell 运行上一节中的printenv示例:

* * * * * bash -l -c "printenv > /tmp/print_envs_result"

由于这仍然是一个非交互式 shell,因此bash不会加载*~/.bashrc脚本。如果我们需要它,我们也可以通过使用BASH_ENV*来克服这个问题。

让我们用它来加载*~/.bashrc*和登录 shell:

* * * * * BASH_ENV=~/.bashrc bash -l -c "printenv > /tmp/print_envs_result"

现在,我们等待cron运行作业并看到 bash 加载了几个环境变量:

$ wc -l /tmp/print_envs_result
41 /tmp/print_envs_result

5. 在crontab文件中设置每个变量

类似于前面提到的BASH_ENV方法,我们可以**在command前设置任意变量写name=value。**如果我们指定多个变量,我们必须在它们之间用空格隔开。

让我们设置LEARNING_FROMLANG来运行print_envs.sh作业:

* * * * * LEARNING_FROM=blogdemo LANG=es_US /home/blogdemo/print_envs.sh

一分钟后,我们可以看到脚本加载了这些变量:

$ grep -E 'LANG|LEARNING_FROM' /tmp/print_envs_result 
LEARNING_FROM=blogdemo
LANG=en_US

我们可以注意到,如果我们需要设置很多变量,我们最终会得到很长的一行。如果我们这样做,crontab文件可能很难理解。

一些 Linux 发行版,如 Fedora 和 Arch,提供cron的*cronie 实现。**此实现允许我们在crontab*文件中为所有作业设置环境变量**。我们通过一次在一行中写入每个环境变量来做到这一点,其中没有任何工作。

让我们为所有作业定义LEARNING_FROMLANG

LEARNING_FROM=blogdemo
LANG=es_US
* * * * * /home/blogdemo/print_envs.sh

正如我们所见,脚本加载了这些变量:

$ grep -E 'LANG|LEARNING_FROM' /tmp/print_envs_result 
LEARNING_FROM=blogdemo
LANG=en_US