在 Cron 作业中加载环境变量
概述
当 crontab 运行命令时,它不会从诸如 ~/.bashrc、~/.bash_profile 等文件中读取任何环境变量。因为 cron 从非交互式、非登录 shell 运行任务,所以它不需要交互式用户。某些应用程序需要环境变量才能正常运行。
我们将讨论从 crontab 加载环境变量的不同方法。
设置 BASH_ENV 变量
我们可以使用 BASH_ENV 变量为 shell 脚本设置环境变量。我们设置它,以便当我们运行作业时,它们由 shell 脚本执行。我们可以设置 shell,以便当我们打开一个新的终端窗口时,可以获得系统正确的设置。我们可以使用 BASH_ENV 运行任何 shell 命令,而无需知道它的确切名称。
我们将在 crontab 文件中添加一行,在执行脚本之前运行 /etc/profile。
* * * * * BASH_ENV=/etc/profile /home/baeldung/print_envs.sh
请注意,此作业每分钟运行一次。此外,让我们编写 /home/baeldung/print_envs.sh 脚本,使用 printenv 将所有环境变量打印到临时文件:
#!/bin/bash printenv > /tmp/print_envs_result Now, after setting execution permission to the script with chmod +x /home/tpoint/print_envs.sh, we’ll wait one minute to see the result: $ wc -l /tmp/print_envs_result 38 /tmp/print_envs_result $ grep PS1= /tmp/print_envs_result PS1=\u@\h:\w\$
我们可以看到 print_envs 脚本加载了 38 个环境变量。例如,它加载了 PS1 值。
您还可以使用 BASH_ENV 运行自定义 shell 命令。我们可以用它来加载多个文件或添加更多变量。让我们创建一个名为 preload.sh 的 shell 文件,它加载四个不同的文件:/etc/profile、~/.bash_profile、~/.bashrc,并导出另一个变量。
#!/bin/bash . /etc/profile . ~/.bash_profile . ~/.bashrc export LEARNING_FROM=tpoint
现在,我们将修改 crontab 文件以使用 /home/tpoint/preload.sh:
* * * * * BASH_ENV=/home/tpoint/preload.sh /home/tpoint/print_envs.sh
等待一分钟后,我们得到结果:
$ wc -l /tmp/print_envs_result 41 /tmp/print_envs_result $ grep LEARNING_FROM /tmp/print_envs_result LEARNING_FROM=tpoint
我们注意到现在有 41 个环境变量。我们还有一个名为 LEARNING_FROM 的变量,其默认值为“tpoint”。
使用 Bash 包装作业
假设我们有这个 crontab 文件:
* * * * * printenv > /tmp/print_envs_result
因为 printenv 不是 shell 脚本,所以我们不能使用 BSH_ENV 来加载环境变量值。但是,我们可以使用 Bash 来包装它。我们通过在命令前添加 bash -c 并将其括在双引号中来做到这一点。
我们通过在作业前添加 bash -c 并将作业括在双引号中来实现这一点。Bash 在从传递给脚本的参数读取命令时使用 -c 选项。
我们将向脚本添加 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=\u@\h:\w\$
如果需要,我们可以添加更多环境变量,或者我们可以创建另一个 shell 文件来包含我们想要添加的文件。现在,我们有两个选择。我们可以通过设置名为 BASH_ENV 的环境变量来加载更新后的脚本。就像以前一样。相反,让我们将原始作业从其在 crontabs 文件中的当前位置移动到新 shell 脚本的末尾。
让我们创建一个名为 /home/tpoint/wrap_printenvs.sh 的新 shell 脚本:
#!/bin/bash . /etc/profile . ~/.bash_profile . ~/.bashrc export LEARNING_FROM=tpoint #now, we run the original job printenv > /tmp/print_envs_result
最后,我们将 crontab 文件更改为运行新脚本:
* * * * * /home/tpoint/wrap_printenv.sh
与上一节类似,我们可以在结果中看到环境变量:
$ wc -l /tmp/print_envs_result 41 /tmp/print_envs_result $ grep LEARNING_FROM /tmp/print_envs_result LEARNING_FROM=tpoint
使用登录 shell 运行作业
为了模拟运行 crontab 条目时的相同顺序,我们可以使用解释器(例如 bash)来运行脚本。当 Bash 作为登录 shell 工作时,它会读取诸如 ~/.bash_profile、~/.bash_login、/etc/profile 和 ~/.profile 等文件。这样,bash shell 将加载所有启动脚本,并从这些文件中加载环境变量。我们应该以 bash -l -c 开头,并确保将其括在双引号中。当我们希望命令处于登录(或交互式)模式时,-l 参数至关重要。
让我们从交互式 shell 运行上一节中的 printenv 命令。
* * * * * bash -l -c "printenv > /tmp/print_envs_result"
因为这仍然是非交互式终端,所以 bash 不会加载 ~/.bashrc 文件。如果我们也使用 BASH_ENV,我们可以克服这个问题。
我们将使用它来加载 ~/.bashrc 和登录 shell。
* * * * * BASH_ENV=~/.bashrc bash -l -c "printenv > /tmp/print_envs_result"
现在我们需要让 cron 运行脚本,然后检查是否设置了任何环境变量。
$ wc -l /tmp/print_envs_result 41 /tmp/print_envs_result
在 Crontab 文件中设置每个变量
我们可以使用 name=variable 语法在运行命令之前设置变量。如果我们想使用多个变量,我们必须用空格分隔多个变量。
我们需要设置运行 print_envs 脚本的环境变量:
* * * * * LEARNING_FROM=tpoint LANG=es_US /home/tpoint/print_envs.sh
一分钟后,我们现在可以看到脚本将这些值加载到页面中:
$ grep -E 'LANG|LEARNING_FROM' /tmp/print_envs_result LEARNING_FROM=tpoint LANG=en_US
如果我们想设置很多变量,那么我们会以非常长的一行代码结束。如果这样做,cron 作业可能难以理解。
像 Fedora 和 Arch Linux 这样的程序提供了 cron 的 cron 守护程序实现;使用这些实现,我们可以在 /etc/crontab 文件中为所有作业(包括系统范围的作业)定义环境变量。我们将每个环境变量一次写在一行中,其中不包含任何作业。
我们将为所有作业类型定义 LEARNING_FROM 和 LANG:
LEARNING_FROM=tpoint LANG=es_US * * * * * /home/baeldung/print_envs.sh
正如我们所看到的,脚本加载了这些变量:
$ grep -E 'LANG|LEARNING_FROM' /tmp/print_envs_result LEARNING_FROM=baeldung LANG=en_US
结论
我们查看了为 cron 作业设置环境变量值的各种方法。
我们之前探讨了如何使用 $BASH_ENV 环境变量在作业开始之前运行脚本。此设置允许我们包含来自其他来源的环境变量,例如 /etc/bashrc。
接下来,我们看到我们可以将一个作业包装在另一个作业中,在执行原始作业之前设置和加载所有所需的环境变量。
我们现在已经学习了如何单独设置环境变量值,并将它们直接写入 crontabs 文件。