【免费下载链接】scripts
my scripts!
scripts 是一个轻量级 shell 脚本集合,其中 statusbar/ 目录下的五个块脚本完整展示了dwmblocks 状态栏的编写原理:如何用 stdout 驱动 UI、用 RTMIN 信号实现即时刷新、用 /tmp 状态文件做无状态进程间的通信。本文带你逐行拆解这些脚本背后的块协议与设计模式,帮你掌握 DIY 极简状态栏的完整方法。
📂 先认识这套状态栏脚本
statusbar/ 目录只包含 5 个短小精悍的脚本,却覆盖了状态栏的全部典型场景:
| 脚本 | 功能 | 亮点 |
|---|---|---|
| timedate | 时间日期 | 2 行代码,最简块 |
| systemstats | CPU/GPU 温度、内存 | 传感器回退链 + 阈值配色 |
| disks | 磁盘占用 | 滚动切换分区 + 格式切换 |
| musicplaying | 正在播放的音乐 | 播放器优先级轮询 |
| timer | 倒计时 | 信号刷新 + 信号兜底 |
在开始之前,先 clone 仓库(仓库只读,仅供阅读学习):
git clone https://link.gitcode.com/i/7a9c072e3b0fd890d4d3510f2eaf9ff6一、dwmblocks 块协议:读懂 3 条核心规则
dwmblocks 是一个 C 语言写的状态栏框架,它本身不内置任何块,而是把"块"定义为一个约定——每个块就是一个独立的 shell 脚本。这个约定只有三条:
规则 1:stdout 就是界面
块脚本每次运行,只需把要显示的一行文字打印到标准输出,dwmblocks 就会把它渲染成状态栏上的一个"按钮"。
看最简单的 timedate 全文:
#!/usr/bin/env bash printf "%(%A, %B %e %_I:%M%P )T\\n"整个块就是 2 行——没有循环、没有状态。%(…%T是 bash 内建的时间格式化,dwmblocks 按定时器反复执行它,时间就"动"起来了。无状态、一次性、可随意重跑,是块协议的第一要义。
规则 2:环境变量是输入
用户在状态栏上的操作,dwmblocks 通过环境变量传递给块脚本。最核心的是$BLOCK_BUTTON:
1左键、2中键、3右键4/5滚轮上/下6通常是"编辑本块"的快捷键约定
disks 把这套协议用得淋漓尽致:
case $BLOCK_BUTTON in 1) notify-send -t 60000 "$(dysk --color no -c free+mp+use+size)" ;; 3) echo "$INDEX $(( ($FORMAT + 1) % 2 ))" > "$DISKS_FILE" ;; 4) echo "$(( ($INDEX + 1) % ${#DISKS[@]} )) $FORMAT" > "$DISKS_FILE" ;; 5) echo "$(( ($INDEX - 1) % ${#DISKS[@]} )) $FORMAT" > "$DISKS_FILE" ;; 6) setsid -f "$TERMINAL" -e "$EDITOR" "$0" ;; esac左键弹通知、滚轮切换/与/home两个分区、右键切换"使用率/剩余空间"两种格式。注意$TERMINAL和$EDITOR也是被 dwmblocks 注入的环境变量——用户点"编辑",打开的就是当前块自己的源码($0),这是整套仓库反复出现的"自我维护"设计模式。
规则 3:信号是触发器
除了内置定时器,dwmblocks 还允许外部用实时信号唤醒某个块立即重跑:第 N 个块监听RTMIN+N信号。这就是下一节的重点。
二、信号刷新机制:一行 pkill 唤醒整块状态栏
为什么要"主动叫醒"状态栏?
定时器刷新有间隔(比如每 10 秒),但有些事件必须立刻可见:开始录屏的瞬间、倒计时每秒的跳动、切了一首歌。scripts 仓库给出的答案是:
pkill -RTMIN+3 dwmblockspkill -RTMIN+3向 dwmblocks 进程发送RTMIN+3信号,dwmblocks 收到后立刻重跑第 3 个块。信号编号与块序号一一对应,仓库中出现了三处典型用法:
| 发送方 | 信号 | 唤醒的目标块 |
|---|---|---|
| audio-video/record 开始/结束录屏 | RTMIN+3 | 录制指示块 |
| shortcuts-menus/musicpicker 切歌后 | RTMIN+6 | 音乐显示块 |
| statusbar/musicplaying 播放/暂停后 | RTMIN+11 | 自身所在块(自定义布局下) |
完整链路:以录屏为例
record 演示了"信号 + 状态文件"的经典组合拳:
echo "Rec •" > /tmp/recordingicon && pkill -RTMIN+3 dwmblocks- 把
"Rec •"写进/tmp/recordingicon(状态落地); - 发
RTMIN+3信号(通知状态栏); - 第 3 个块被唤醒,重跑时
cat /tmp/recordingicon读到新值,渲染出来。
结束时(L31)把文件清空再发一次信号,红点随之消失。整个过程不依赖定时器,毫秒级响应。
倒计时的每秒刷新
statusbar/timer 把这套机制用到了极致:
while [ $TOTAL_SECONDS -gt 0 ]; do MIN=$(printf "%02d" $((TOTAL_SECONDS / 60))) SEC=$(printf "%02d" $((TOTAL_SECONDS % 60))) echo " $MIN $SEC " > /tmp/timericon pkill -RTMIN+3 dwmblocks sleep 1 ((TOTAL_SECONDS--)) done每秒写一次/tmp/timericon并触发信号刷新——信号即心跳,倒计时因此可以精确到秒,而不受 dwmblocks 刷新间隔限制。
三、/tmp 状态文件模式:无状态进程间的"共享内存"
块脚本每次运行都是全新进程,没有内存可存状态。scripts 仓库统一用/tmp 下的文件解决,形成了一套清晰的分工:
| 文件 | 维护方 | 用途 |
|---|---|---|
/tmp/recordingicon | record / guitarrec | 录制指示文字 |
/tmp/timericon | timer | 倒计时文字 |
/tmp/dwmblocks_disk_info | disks | 当前分区序号 + 显示格式 |
/tmp/dwmblocks_mus_format | musicplaying | 艺术家/专辑格式切换 |
/tmp/recpid、/tmp/guitarpid | record / guitarrec | 录制进程 PID |
/tmp/dwmblocks_debug.log | systemstats | 传感器调试日志 |
其中 disks 的模式最值得借鉴——读写闭环:
DISKS_FILE="/tmp/dwmblocks_disk_info" [ -f "$DISKS_FILE" ] || echo "0 0" > "$DISKS_FILE" # 不存在则初始化 read INDEX FORMAT < "$DISKS_FILE" # 顶部:读 echo "$INDEX $(( ($FORMAT + 1) % 2 ))" > "$DISKS_FILE" # 点击时:写脚本开头读文件、点击时写文件,状态跨进程存活。||初始化、read一行读多变量,都是极简但健壮的写法。
四、值得抄作业的设计模式
1️⃣ 硬件探测回退链
不同机器的传感器输出千差万别。systemstats 用"逐级尝试 + 兜底 N/A"处理 GPU 温度:
if command -v nvidia-smi >/dev/null 2>&1; then GPU_TEMP=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader) else GPU_TEMP=$(sensors | awk '/^edge|^junction/ ...') fi [ -z "$GPU_TEMP" ] && GPU_TEMP="N/A"先command -v探测工具是否存在,全部失败时优雅降级为N/A而不是报错——状态栏脚本的健壮性原则。
2️⃣ 用 bash 通配模式做温度阈值配色
L54-L59 不写 if 比较,而用case的模式匹配判断温区:
case $CPU_TEMP in "N/A") COLOR="#d8dee9" ;; [7-9][6-9]|[8-9][0-9]|100) COLOR="#bf616a" ;; # >75 红 [6][6-9]|7[0-5]) COLOR="#ebcb8b" ;; # 66~75 黄 *) COLOR="#a3be8c" ;; # ≤65 绿 esac[7-9][6-9]这类 glob 模式天然适合数值区间判断,把"温度→颜色"压缩成一张模式表,可读性远胜嵌套 if。
3️⃣ PID 文件开关:一个命令切换开始/结束
record 的最后一行是全仓库最妙的单行逻辑:
([[ -f /tmp/recpid ]] && end && exit 0) || recordPID 文件存在 = 正在录制,同一个快捷键既能开始也能结束录制,无需额外的状态标记。
4️⃣ trap 信号兜底,防止状态残留
timer 在计时循环里注册了兜底:
trap cleanup SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM无论用户 Ctrl+C、关终端还是进程被杀,cleanup都会清空/tmp/timericon并触发刷新——"善后逻辑"不依赖主流程正常退出,是写常驻/计时类脚本的必修课。
5️⃣ 多播放器优先级轮询
musicplaying 支持chromium mpd mpv三类播放源:循环中谁在Playing就显示谁(网页播放 > mpv > mpd),并用/tmp文件记住"艺术家/专辑"两种格式偏好。优先级列表本身可通过MUSIC_PLAYING_PLAYERS环境变量覆盖,默认值 + 可覆盖是不错的配置设计。
五、上手步骤:把这套块装进你的状态栏
- 安装 dwmblocks:按 dwmblocks 官方 README 用
make && sudo make install编译安装; - 放置块脚本:把 statusbar/ 下的脚本(如 timedate、systemstats)拷贝到
~/.config/dwmblocks/; - 在 blocks.h 中声明块:为每个块指定名称与刷新间隔(
block结构体),块序号即对应RTMIN+N信号号; - 补全环境变量:在 dwmblocks 配置或
~/.xprofile中导出$TERMINAL与$EDITOR,让"编辑本块"快捷键可用。
⚠️ 注意:作者已在 README.md 中幽默提醒——脚本"may not work as expected, or at all",传感器字段名、块序号等需要按你的硬件与布局微调,这也正是学习它们的价值所在。
总结
scripts 仓库用不到 500 行 shell 代码,讲透了三件大事:
- 块协议:stdout 输出 +
$BLOCK_BUTTON环境变量 + 定时/信号触发,三条约定撑起整个状态栏; - 信号刷新:
pkill -RTMIN+N即时唤醒指定块,配合 /tmp 状态文件实现"写文件 → 发信号 → 重读渲染"的闭环; - 设计模式:回退链探测、glob 阈值表、PID 文件开关、trap 兜底、自编辑块——每一个都值得写自己的脚本时直接搬走。
想继续深入,可以对照阅读 audio-video/ 下的录屏脚本与 shell/stats 的命令行版统计输出,它们与状态栏块共享同一套数据获取逻辑,是理解这套"一鱼两吃"架构的最佳入口。
【免费下载链接】scripts
my scripts!
相关推荐
AboutLibraries代码架构深度剖析:理解其模块化设计与扩展机制
AboutLibraries代码架构深度剖析:理解其模块化设计与扩展机制 AboutLibraries是一个专为Android应用设计的开源库,它能自动收集并优
开源治理开发工具UI库/组件PatchELF架构深度剖析:源代码实现原理与设计模式
PatchELF架构深度剖析:源代码实现原理与设计模式 PatchELF是一个小巧但功能强大的实用程序,专门用于修改现有的ELF可执行文件和库文件。作为Linu
开发工具终极Tweakpane开发者指南:从源码到核心原理的全面解析
终极Tweakpane开发者指南:从源码到核心原理的全面解析 Tweakpane是一款功能强大的参数调整与监控工具,它提供了紧凑直观的GUI界面,让开发者能够轻
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考