POSIX电池提醒脚本通过cron运行时DBUS地址重启变动如何解决?
使用notify-send和cron实现电池电量提醒时的DBUS动态地址问题
问题背景
我使用i3窗口管理器,没有内置电池状态通知功能,曾多次出现笔记本电量耗尽的情况,因此编写了一个POSIX shell脚本,结合notify-send实现电量提醒,手动调用可正常运行。
脚本代码
#!/bin/sh percent=`upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep percentage:` notify-send "battery life" "$percent"
现有定时配置
我通过crontab设置每10分钟执行一次脚本,配置如下:
*/10 * * * * export DISPLAY=:0 ; export DBUS_SESSION_BUS_ADDRESS=a; batterystatus.sh
遇到的问题
去掉DBUS_SESSION_BUS_ADDRESS配置项后notify-send无法正常运行,而该地址每次开机都会变动,需要每次开机手动修改crontab配置才能正常使用,需要找到规避该问题的方法。
解决方案
这个问题的本质是cron运行在非会话上下文,默认不会继承桌面会话的DBUS_SESSION_BUS_ADDRESS环境变量,该变量是notify-send调用桌面通知服务的必需参数,且每次桌面会话启动都会生成新的地址,硬编码无法长期生效。
你可以选择以下任意一种方案解决:
方案1:修改脚本自动获取DBUS地址(适配现有cron配置)
在batterystatus.sh开头添加环境变量自动获取逻辑,无需硬编码地址:
#!/bin/sh # 单显卡桌面环境DISPLAY默认固定为:0 export DISPLAY=:0 # systemd发行版通用DBus地址获取逻辑 export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus" # 建议将反引号替换为$()写法,兼容性更好 percent=$(upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep percentage:) notify-send "battery life" "$percent"
如果你的发行版未使用systemd,可以用以下代码替换DBUS变量导出逻辑,从运行的i3进程中读取环境变量:
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u $LOGNAME i3)/environ | cut -d= -f2-)
修改完成后可将crontab简化为:
*/10 * * * * /绝对路径/到/batterystatus.sh
注意需要给脚本添加执行权限:chmod +x /绝对路径/到/batterystatus.sh,crontab中需填写脚本的完整绝对路径。
方案2:使用i3内置自启循环(i3用户首选,无需额外配置)
直接在i3配置文件(默认路径~/.config/i3/config)中添加以下内容,i3启动时会自动运行定时循环,天然带有完整的会话环境变量,完全不需要处理DBUS相关问题:
exec --no-startup-id while true; do /绝对路径/到/batterystatus.sh; sleep 600; done
修改后重载i3配置(默认快捷键Mod+Shift+C)即可生效,开机自动运行。
方案3:使用systemd用户定时器(替代cron,更易管理)
如果需要更规范的定时任务管理,可以使用systemd用户级定时器,用户级服务默认运行在会话上下文,自动携带所需的环境变量:
- 新建服务文件
~/.config/systemd/user/batterystatus.service:
[Unit] Description=电池电量通知服务 [Service] ExecStart=/绝对路径/到/batterystatus.sh
- 新建定时器文件
~/.config/systemd/user/batterystatus.timer:
[Unit] Description=每10分钟运行一次电量检查 [Timer] OnCalendar=*:0/10 Persistent=true [Install] WantedBy=timers.target
- 执行命令启用定时器:
systemctl --user enable --now batterystatus.timer
设置完成后定时器会开机自启,运行日志可通过journalctl --user -u batterystatus.service查看。
内容的提问来源于stack exchange,提问作者octo-
相关产品推荐
相关产品推荐

