systemctl 找不到可执行文件,而 root 可以?

systemctl 找不到可执行文件,而 root 可以?

奇怪的事情:我创建了一个服务,它应该启动一个 springboot fat archive webserver。服务:

#!/bin/bash
[Unit]
Description=JalouWeb

[Service]
Type=simple
WorkingDirectory=/opt/jaloucontrol
ExecStart=/bin/bash jalouweb.sh

TimeoutStartSec=0
RestartSec=60
Restart=always
SyslogIdentifier=jalouweb

[Install]
WantedBy=multi-user.target

jalouweb.sh:

#! /bin/sh
#
java -Duser.timezone=Europe/Berlin -Dfile.encoding=UTF-8 -jar jcweb-0.0.1-SNAPSHOT.jar

以用户身份启动 jalouweb.sh 即可。执行 sudo -i 并以 root 身份启动 jalouweb.sh 。用户和 root 都可以成功调用 java -version。

但是当通过 systemctl 启动该服务时,我收到

... jalouweb.sh: Zeile 3: java: Kommando nicht gefunden.(<- command not found)
...  systemd[1]: jalouweb.service: Main process exited, code=exited, status=127/n/a

一旦我将路径添加到 jalouweb.sh 中:

/opt/openjdk17/bin/java -Duser.timezone=Europe/Berlin...

systemctl 也成功了。但为什么? Afaik 在没有用户的情况下运行服务意味着以 root 身份运行它。 root 可以直接启动 sh 和 java。为什么systemctl需要显式路径?


编辑:我遵循了wenshuo的建议:尽管服务运行成功,但没有创建tmplog文件,但journalctl显示

systemd[1]: Started JalouWeb. 
jalouweb[11189]:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin > /tmp/tmplog  
systemd[1]: jalouweb.service: Succeeded.

看起来是这样,好像 echo 命令并不真正正确。但核心信息显示:No java in the path of the non-interactive root。


编辑2 @Stephane:查找或执行脚本没有问题。问题是,没有找到脚本中调用的java。我必须弄清楚,如何为 systemctl 的执行者提供路径或 JAVA_HOME。

答案1

bash some-path

在哪里some-path不包含/字符应该不是使用。这适用于其他几个类似 Korn 的 shell1 以及.sourcebuiltins2 或许多类似 Bourne 的 shell,尽管具体情况更糟bash

bash是 Bash 编程语言的解释器,就像perl是编程语言的解释器一样Perl

bash /path/to/some/Bash/file就像perl /path/to/some/Perl/file你如何告诉bashperl解释给定文件中的 Bash/Perl 代码而不是标准输入上的代码。

例如,它用于:

#! /bin/bash -

执行后,系统将其转换为/bin/bash - path/to/the/filebash解释文件的内容。

与,正在执行,但未执行/bin/bash some-pathsystemdbashbash执行 some-path³,它只是读取它并解释其中的 Bash 代码。

现在,当some-path不包含任何/,行为发生变化bashbash首先将其解释为普通路径,因此这里是相对于其当前工作目录的路径,但如果找不到该文件,它会在环境$PATH变量的目录组件中查找它(或者如果未设置)在某些默认搜索列表中)。

这种行为是允许虽然 POSIX 不要求,但 POSIX 只允许可执行文件在该过程中找到的文件。 Bash(至少是 5.2 版)甚至可以找到不可执行的文件,即使在 POSIX 模式下也是如此,这使得它不合规。

bash在这里,您需要从该脚本所在的目录中解释该脚本,然后您需要:

WorkingDirectory=/path/to/that/dir
ExecStart=/bin/bash ./the-script

(请注意,./确保路径包含 a 的前缀/,并在当前工作目录中$PATH由于某种原因丢失时禁用查找)。the-script

如果WorkingDirectory未指定,则默认/为系统服务,用户的主目录为用户服务。

或者您需要指定脚本的完整路径:

ExecStart=/bin/bash /path/to/that/dir/the-script

如果您缩进the-script为可执行文件,并且由查找解释bash并找到$PATH,那么请确保它有一个#! /bin/bash -shebang,它具有执行权限 ( chmod a+x) 并且它存储$PATHsystemd.

并使用:

ExecStart=the-script

如果该文件是可执行的,我建议删除该.sh扩展名。如果不是,并且它是用 Bash 语言编写的,我建议使用扩展,.bash而不是.sh如果它不是标准sh语法,则会产生误导。


¹ 这是 zsh 在 sh 或 ksh 模拟中的情况,而 ksh 则可能是该错误功能的根源。

² 对于.特殊的内置函数,这实际上比 POSIX 更糟糕需要 /-less 路径被查找$PATH(并回退到当前工作目录)。一些 shell 忽略的要求(包括不处于 POSIX 模式时的 bash)。

³ 这会有所不同,因为此处的bash -c the-script文本the-script作为内联 shell 代码供 bash 解释,bash 将其解释为一个简单的命令,导致它$PATH查找可执行文件命令(脚本或非脚本)让它执行

答案2

为了查找可执行文件,shell 将搜索$PATH变量中列出的目录。如果$PATH包含目录的路径名,则可以调用该目录中的可执行文件,而无需给出可执行文件的完整路径名。

echo "$PATH"可以在 shell 中看到当前的路径列表。就您而言,$PATH所使用的systemctl可能与交互式 shell 所使用的不同root

您可以使用以下命令查看$PATHwith中使用的值systemctl

ExecStart=/bin/echo "$PATH" > /tmp/tmplog

...在[Service]单元文件的部分中。这会将$PATHto的值写入/tmp/tmplog

相关内容