【问题标题】:While loop in bash script breaks systemd servicebash 脚本中的循环会破坏 systemd 服务
【发布时间】:2017-03-31 02:35:08
【问题描述】:

我在 Debian 上,我有一个调用 bash 脚本的 systemd 服务。

脚本包含一个无限的 while 循环,因为我需要它每 X 秒无限检查一次。

systemd 服务在遇到“while true; do”行时崩溃。

如果我手动执行,脚本运行良好。 为什么 systemd 不喜欢它?我该怎么办?

这里是服务和脚本。正如我所指出的,“while true; do”之前的 echo 语句会打印出来。 "while true; do" 行之后的 echo 语句不打印。

/etc/systemd/system/stream.service:

[Service]
WorkingDirectory=/home/pi/
ExecStart=/home/pi/joi_main.sh
Restart=no
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=stream_service
User=pi
Group=pi
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target

/home/pi/joi_main.sh:

#!/bin/bash -e

today=`/bin/date '+%Y_%m_%d__%H_%M_%S'`
exec 2> "/home/pi/stream_logs/$today.$RANDOM.log"
exec 1>&2

#Wait 120s for system to finish booting
sleep 120

#Initial config
export AUDIODEV=mic_mono
export AUDIODRIVER=alsa
sudo sysctl fs.pipe-max-size=1048576

echo "This line prints"

# Check if video buffer is full every minute. if full, the stream needs to restart
while true; do
    echo "This line doesn't"
    if grep "100% full" /home/pi/video_buffer_usage.txt; then
        echo "Buffer is full!"
    # Kill existing processes
        pkill -f “raspivid|rec|buffer|ffmpeg”
    # Wait 10s
         sleep 10
         ./joi_stream.sh &
    fi
    sleep 60
done

Journalctl 似乎完全没有帮助,但就是这样。没有错误。为什么“会话关闭”?

Mar 31 02:13:41 raspberrypi sudo[1369]: pi : TTY=unknown ; PWD=/home/pi ; USER=root ; COMMAND=/sbin/sysctl fs.pipe-max-size=1048576
Mar 31 02:13:41 raspberrypi sudo[1369]: pam_unix(sudo:session): session opened for user root by (uid=0)
Mar 31 02:13:41 raspberrypi sudo[1369]: pam_unix(sudo:session): session closed for user root

(请不要告诉我为这个while循环启动另一个systemd服务。我希望它成为这个主脚本的一部分,因为它需要在其他所有内容之后运行,如果我关闭主服务我也不希望 while 循环运行,因此维护两个 systemd 服务只会增加麻烦。)

【问题讨论】:

  • 请包含您的服务定义文件。
  • 我已经完成了,如果我可以添加任何其他内容,请告诉我
  • 如果改成while :; do呢?
  • : 是一个始终成功的内置命令,因此这避免了必须找到 /bin/true 才能执行。
  • Barmar:我试过了,仍然没有达到 echo "This line doesn't"。而 /bin/true;做也行不通。

标签: linux bash debian systemd


【解决方案1】:

./joi_stream.sh 的内容未共享,但我看到您的systemd 解决方案存在问题。它不能直接解释你的行为,但可能是相关的:

在您的 systemd 配置中,您将 STDOUT 和 STDERR 都重定向到 syslog,但在您的脚本中,您将 STDERR(文件描述符“2”)重定向到文件,并将 STDOUT(文件描述符“1”)重定向到 STDERR。

exec 2> "/home/pi/stream_logs/$today.$RANDOM.log"
exec 1>&2

如果您的./joi_stream.sh 期望您将这些文件描述符重定向到另一个文件能够正常工作,那么它可能不会。如果该文件仅用于日志记录,我会删除这些行并让 systemd 日志处理 - 它会用您的单元标记日志,您可以专门查看您的日志:

 journalctl -u your-unit-name.service

另外,在systemd 中,您通常不会进入睡眠状态等待 systemd 启动。相反,您将使用 .timer 单位。

.timer 文件将指示每分钟运行一次主逻辑,因此不需要“while”循环。计时器单元将包含如下指令:

#开机2分钟后第一次运行 # 之后的每一分钟 OnBootSec=120 OnUnitActiveSec=60

这将是 timer 单元,它启用 可以在启动时启动。计时器文件可以非常简单。只需在/etc/systemd/system 中创建一个.timer 文件,并将其命名为与您希望它激活的服务文件相同的名称:

[Unit]
Description=Runs my service every minute

[Timer]
# Run for the first time 2 minutes after boot 
# and every minute after that
OnBootSec=120 
OnUnitActiveSec=60

[Install]
WantedBy=timers.target

要立即启动并测试您的计时器,请运行:

sudo systemctl start my-service.timer

您可以通过以下方式查看计时器的状态:

sudo systemctl list-timers

systemd 解决方案比rc.local 解决方案更强大。如果您的rc.local 解决方案因任何原因而死机,它将不会重新启动。但是,如果您的脚本死了,将在systemd 下运行,计时器仍然会在一分钟后再次运行它。

【讨论】:

    【解决方案2】:

    仅供参考,如果我从 /etc/rc.local 调用 /home/pi/joi_main.sh 而不是使用 systemd 服务,一切正常。我将使用 rc.local 并终止该服务。

    【讨论】:

      猜你喜欢
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 2011-09-30
      • 2022-10-14
      • 2018-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多