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
相关产品推荐
相关产品推荐

