Shell 脚本作为 systemd 单元崩溃,否则可以正常运行

Shell 脚本作为 systemd 单元崩溃,否则可以正常运行

swaybg我编写了一个简单的 shell 脚本,通过按间隔运行并终止旧进程来随机更改壁纸。此脚本应与 POSIX 兼容。

#!/bin/sh
set -e

interval="$1"
chance="$2"
img_dir="$3"

test -z "$interval" && exit 1
test -z "$chance" && exit 1
test -z "$img_dir" && exit 1

echo "Interval: $interval"
echo "Chance: $chance"
echo "Image Directory: $img_dir"

set_img() {
  old_pids="$(pidof 'swaybg')"
  echo "Found old swaybg pids: $old_pids"

  new_img="$(find "$img_dir" -type f | shuf -n1)"
  echo "Selected new image: $new_img"
  
  if test "$new_img" = "$old_img"; then
    echo "Duplicate, re-rolling..."
    set_img
  else
    echo "Setting the selected image..."
    swaybg -i "$new_img" -m fill &  

    if test ! -z "$old_pids"; then
      sleep 2
      echo "Killing old swaybg pids..."
      # shellcheck disable=SC2086
      kill -s 9 $old_pids
    fi

    old_img="$new_img"
  fi
}

echo "Setting the first image!"
set_img
sleep "$interval"

while true; do
  echo "Deciding if the wallpaper should be changed..."
  if test "$(shuf -i0-100 -n1)" -le "$chance"; then
    echo "Lucky roll, resetting!"
    set_img
  else
    echo "Entropy rejects you."
  fi

  echo "Finished, waiting for next loop..."
  sleep "$interval"
done

systemd 服务单元如下所示:

# /home/jacob/.config/systemd/user/random-wallpaper.service
[Install]
WantedBy=hyprland-session.target

[Service]
ExecStart=/nix/store/xl6r3w5mb99xs63hk2n9nlvr37rgvvvx-wallpaper.sh 3600 25 '/home/jacob/Pictures/Wallpapers'
Restart=on-failure
RestartSec=5

[Unit]
Description=wayland random wallpaper utility
PartOf=graphical-session.target

使用以下命令检查它,结果systemctl --user status显示它已启用并正在激活(自动重启)。每次运行该命令时,自上次退出代码以来的时间都会发生变化。退出代码始终为 127,而且据我所知,没有 STDOUT。

当使用与行上所示完全相同的命令运行脚本时ExecStart,即使使用sh -c,我的壁纸也能完美地改变。

可能是因为我使用的是 NixOS,所以某些必要的环境(即可执行文件)不可用。但我不会被告知吗?我该如何发现这些类型的错误?

答案1

问题在于 的组合set -e和使用pidof

set -e告诉 shell 在任何命令运行中第一次出现非零退出代码时退出。

当您的脚本第一次运行时,pidof 'swaybg'将没有结果,并返回退出代码1

除外man pidof

EXIT STATUS
      0      At least one program was found with the requested name.

      1      No program was found with the requested name.

缓解此问题的一些方法:

  • set -e从脚本顶部删除。
  • 使用$(pidof 'swaybg' | printf '')
  • 改用 no-op ( :):$(pidof 'swaybg' | :)

此外,考虑如果有另一个用户登录会发生什么。当您的脚本运行该时kill,每个实例swaybg都将被终止;您自己和其他用户的。

看一下pgrep,有一个选项可以限制命令以仅查找以用户的真实组 ID 运行的进程(-U)。

$(pgrep -U "$USER" -x 'swaybg' || :)

-x表示精确匹配,仅限名称为确切地 swaybg将被匹配。

请记住,出于某些目的,您可能还需要使用-d ' '来设置分隔符(默认pgrep使用\n),但在这种情况下这并不重要,因为它kill会在任何空格边界处拆分标识符。

无论如何,最好不要终止其他用户的进程 - 如果您在意外尝试时没有首先以非零退出代码退出。

相关内容