我有一个脚本包含:
#!/bin/bash
printenv
当我从命令行运行它时:
env testscript.sh
bash testscript.sh
sh testscript.sh
每次,它都会输出SHELL=/bin/bash
.然而,当它从 cron 运行时,它总是输出SHELL=/bin/sh
.为什么是这样?我怎样才能让 cron 应用 shebang?
我已经检查了 cron 路径;它确实包含/bin。
答案1
shebang 正在工作,而 cron 与此无关。当执行一个文件时,如果该文件的内容以 开头#!
,内核将执行该#!
行指定的文件并将原始文件作为参数传递给它。
您的问题是您似乎相信SHELL
shell 脚本中反映了正在执行脚本的 shell。不是这种情况。事实上,在大多数情况下,SHELL
意味着用户的偏好交互的shell,它的作用是让终端仿真器等应用程序决定执行哪个 shell。在 cron 中,SHELL
是一个变量,告诉 cron 使用什么程序来运行 crontab 条目(时间指示之后的行部分)。
Shell 不会设置该SHELL
变量,除非在启动时未设置该变量。
事实SHELL
很/bin/sh
可能是无关紧要的。您的脚本有#!/bin/bash
一行,因此它由 bash 执行。如果您想说服自己,请ps $$
在脚本中添加以ps
显示有关执行脚本的 shell 的信息。
答案2
SHELL - shell 的完整路径名保存在此环境变量中。如果 shell 启动时未设置,Bash 会为其分配当前用户登录 shell 的完整路径名。
几个环境变量由 cron(8) 守护进程自动设置。 SHELL 设置为 /bin/sh,LOGNAME 和 HOME 从 crontab 所有者的 /etc/passwd 行设置。 PATH 设置为“/usr/bin:/bin”。 HOME、SHELL 和 PATH 可能会被 crontab 中的设置覆盖
因此该SHELL
变量是在 Bash 启动时设置的。
尝试SHELL=/bin/awesome/shell bash testcript.sh
。你应该看到SHELL=/bin/awesome/shell
舍邦有效。你的行为有记录:)
答案3
然而,当它从 cron 运行时,它总是输出 SHELL=/bin/sh。为什么是这样?
这是因为 cron 设置SHELL
为/bin/sh
,因为这是 cron 用于运行 crontab 条目的默认程序。从man 5 crontab
:
几个环境变量由 cron(8) 守护进程自动设置。 SHELL 设置为 /bin/sh...
正如 Evgeny Vereshchagin 的答案已经指出的那样,如果该变量已经设置,则稍后 Bash 不会对其进行修改。
我怎样才能让 cron 应用 shebang?
Cron 已经在应用它了为您自己的脚本,正如您从以下示例中看到的那样。
假设您有以下内容display_process_tree.sh
:
#!/bin/bash
pid=$$
depth=0
while
[ "$pid" -gt 0 ] &&
read -r ppid name < <(ps -o ppid= -o comm= -p "$pid")
do
eval $(echo printf '" %0.s"' {0..$depth})
if [[ -r /proc/$pid/exe ]]; then
echo -n "$name - "
readlink -f /proc/$pid/exe
else
echo $name
fi
pid=$ppid
depth=$((depth+1))
done
还有一个像这样的 crontab:
* * * * * /path/to/display_process_tree.sh > /tmp/display_process_tree.log
现在,如果您等待输出到达/tmp/display_process_tree.log
,您将发现如下所示的进程树:
display_process - /bin/bash
sh - /bin/dash
cron
cron
systemd
这表明 cron 实际上使用/bin/sh
(链接到我的系统中)为您的脚本/bin/dash
打开一个新进程。/bin/bash
答案4
将 shebang 更改为
#!/bin/env bash
或者执行crontab -e
让脚本使用您自己的环境变量运行,因为 cron 自己的环境变量是不同的。
作为旁注,根据这篇文章在askubuntu上cron
,您需要在脚本顶部 给出一个 PATH:
PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# rest of script follows
但是,鉴于您所说的 PATH 似乎有正确的搜索目录,您可以排除这一点。