You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash脚本通过systemd启动时出现xset: unable to open display错误

问题:Caps/Num Lock状态通知脚本通过systemd启动失败

我编写了一个Bash脚本,用于检测Caps Lock和Num Lock的状态变更并发送通知,脚本在终端运行正常,但通过systemd启动时,执行systemctl status返回如下错误:

xset:  unable to open display ""

脚本源码

#!/bin/bash
source /usr/lib/kbled-notifier/delay.conf

echo "kbled-notifier.service: ## Starting ##" | systemd-cat -p info


#Validate the $delay parameter
case $delay in
    ''|*[!0-9]*) delay=2000 ;;
    *) ;;
esac


nofity-it () {
gdbus call --session \
    --dest=org.freedesktop.Notifications \
    --object-path=/org/freedesktop/Notifications \
    --method=org.freedesktop.Notifications.Notify \
    '' $IDSET "$icon" "$title" "$text" \
    '[]' '{"urgency": <1>}' $delay
}

parse-id () {
    sed 's/(uint32 \([0-9]\+\),)/\1/g'
}

IDSET=0
wasoffc="true"
wasoffn="true"

while [ true ]
do

#Retrieves the current status of Caps Lock and Num Lock
capstat=$( xset -q | sed -n 's/^.*Caps Lock:\s*\(\S*\).*$/\1/p' )
numstat=$( xset -q | sed -n 's/^.*Num Lock:\s*\(\S*\).*$/\1/p' )

if [[ $capstat == "on" ]] && [ ${wasoffc} = "true" ];
then
    title="Caps Lock"
    text="ON"
    icon="/usr/share/icons/kbled-indicator/capson.png"
    IDSET=$(nofity-it | parse-id)
    wasoffc="false"
fi
if [[ ${wasoffc} == "false" ]] && [[ $capstat == "off" ]]; 
then
    title="Caps Lock"
    text="OFF"
    icon="/usr/share/icons/kbled-indicator/capsoff.png"
    IDSET=$(nofity-it | parse-id)
    wasoffc="true"
fi

if [[ $numstat == "on" ]] && [[ ${wasoffn} == "true" ]]; 
then
    title="Num Lock"
    text="ON"
    icon="/usr/share/icons/kbled-indicator/numon.png"
    IDSET=$(nofity-it | parse-id)
    wasoffn="false"
fi
if [[ $numstat == "off" ]] && [[ ${wasoffn} == "false" ]]; 
then
    title="Num Lock"
    text="OFF"
    icon="/usr/share/icons/kbled-indicator/numoff.png"
    IDSET=$(nofity-it | parse-id)
    wasoffn="true"
fi
sleep 0.05
done

systemd服务文件

[Unit]
Description=A Notifier For Status of Caps Lock & Num Lock

[Service]
Type=simple
ExecStart=/usr/lib/kbled-notifier/kbled-notifier-daemon
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

解决方案

问题根源

原服务是系统级服务(绑定multi-user.target),运行在无图形会话的环境中,缺少DISPLAY和XAUTHORITY环境变量:

  • xset需要连接X11显示服务器才能读取键盘状态,没有DISPLAY就无法建立连接;
  • gdbus调用会话级通知服务也需要用户图形会话的环境变量支持。

修复步骤

1. 调整systemd服务配置

将服务改为用户级服务,并配置依赖图形会话,添加必要的环境变量:

创建或修改服务文件为~/.config/systemd/user/kbled-notifier.service:

[Unit]
Description=Caps Lock & Num Lock状态通知器
After=graphical-session.target

[Service]
Type=simple
ExecStart=/usr/lib/kbled-notifier/kbled-notifier-daemon
Restart=on-failure
RestartSec=10
KillMode=process
# 传递X显示和权限验证文件路径
Environment=DISPLAY=:0
Environment=XAUTHORITY=%h/.Xauthority

[Install]
WantedBy=graphical-session.target

2. 重新加载并启用服务

执行以下命令使配置生效:

systemctl --user daemon-reload
systemctl --user enable --now kbled-notifier.service

3. 脚本性能优化(可选)

原脚本每次循环调用两次xset -q,可以改为一次性获取输出再解析,减少系统调用:

# 替换原脚本中获取capstat和numstat的代码块
xset_output=$(xset -q)
capstat=$(echo "$xset_output" | sed -n 's/^.*Caps Lock:\s*\(\S*\).*$/\1/p')
numstat=$(echo "$xset_output" | sed -n 's/^.*Num Lock:\s*\(\S*\).*$/\1/p')

关键说明

  • 用户级服务(--user)运行在当前用户的会话环境中,自动继承图形界面的环境变量,更适合这类和用户交互的工具;
  • After=graphical-session.target确保服务在图形会话完全启动后再运行,避免X服务器未就绪的问题;
  • XAUTHORITY变量用于验证X服务器的访问权限,防止因权限不足导致的连接失败。

内容的提问来源于stack exchange,提问作者Amirreza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:10:03