就像我登录一样从 systemd 运行 bash 脚本

就像我登录一样从 systemd 运行 bash 脚本

我有一个以 Bash 脚本形式实现的服务。它内部实际上是一个 node.js 应用程序,但就这个问题而言,它可以是任何东西。

基本用例是我们在交互式 shell 中开发/调试脚本,然后将其作为服务启用。

如果我通过 ssh 连接到我的设备(登录),我的整个环境就在那里,我可以在命令行启动我的脚本,没有问题。我所在的 shell 已从 、 等处获取正确的环境,.bashrc一切.profile正常。

但是如果我从 systemd 服务启动它,它source就会像交互式 shell 一样设置环境。有一些规定可以将环境变量添加到单元,但对于我在这里所做的,这只是重复了我的交互式环境所做的工作,并且很容易忘记某些事情。

对于真正的生产服务,我确信有正确的方法来做到这一点,但这是一个实验室应用程序,我需要让它变得简单,以便脚本上的任何人都不需要知道 systemd 是如何工作的。

所以问题是:我需要做什么才能让编写的脚本在交互式 Bash shell 下运行并从 systemd 运行?

我尝试使用 启动 bash --login,但不起作用。source .bashrc在外部脚本中明确执行 也不起作用。

下面是我为了强调这个问题而使用的一个例子:

核心脚本(job.sh):

#!/bin/bash
cd
while [ 1 ]
do
    pwd
    echo USER is $USER
    echo PATH is $PATH
    which node
    node --version
    sleep 1
done

我从另一个脚本调用它(wrapper.sh):

#!/bin/bash
echo starting wrapper...
bash --login /home/droid/job.sh

这是我的job.service单位/etc/systemd/system/job.service

[Unit]
Description=Job Daemon

[Service]
User=droid
ExecStart=/home/droid/wrapper.sh

[Install]
WantedBy=multi-user.target

我对 systemd 有哪些不理解之处,导致我无法让它正常工作?

答案1

通过运行脚本和直接运行脚本的区别systemd在于环境。你可以像这样测试它。在你的 Unit 文件中,将其添加到 [Service] 部分,以进行测试:

StandardOutput=console

然后在你的 bash 脚本的顶部添加此行来转储环境:

env

现在在 systemd 内部和外部运行脚本并比较转储的环境变量。

systemd 的一个特性就是可以严格控制环境。这既提高了安全性,又提供了一致性。

您可以在以下位置阅读有关 systemd 如何管理环境的更多信息systemd.exec

一旦按照 systemd 的意愿运行,通过 CLI 和系统运行相同程序就很容易了。通过 CLI 运行它,如下所示:

systemctl start your-unit-name

然后 systemd 将在完全相同的环境下运行。

您要求的是相反的:让 systemd 使用您的“bash”环境运行服务。但这是一个混乱、不断变化的环境,可能会导致不一致的结果。相比之下,systemd 运行时环境受到严格控制,可产生一致、可重现的结果。这就是为什么应该彻底改变这个问题,并询问如何使用 systemd 使用的相同、一致、受控的执行环境在 CLI 上运行您的服务。

答案2

.profile,则被读取任何shell 的登录实例,.bashrc明确仅供以下人员使用:交互的非登录 shell。事实上,根据你的发行版,默认的.bashrc顶部可能有这样的内容,以便额外的确保文件仅由交互式 shell 处理:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

这可能就是为什么手动采购.bashrc不起作用的原因。

您可以通过指定并确保在而不是--login中设置所需的环境变量来使其工作。不过,更好的办法可能是专门为该服务创建一个单独的环境变量文件,并确保该文件既来自 systemd 服务,又来自手动运行它。.profile.bashrc

相关内容