ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

开发者进阶:scripts代码原理深度剖析——dwmblocks块协议、信号刷新机制与脚本设计模式

开发者进阶:scripts代码原理深度剖析——dwmblocks块协议、信号刷新机制与脚本设计模式

【免费下载链接】scripts

my scripts!

项目地址:https://gitcode.com/gh_mirrors/scripts58/scripts
点击查看免费下载

scripts 是一个轻量级 shell 脚本集合,其中 statusbar/ 目录下的五个块脚本完整展示了dwmblocks 状态栏的编写原理:如何用 stdout 驱动 UI、用 RTMIN 信号实现即时刷新、用 /tmp 状态文件做无状态进程间的通信。本文带你逐行拆解这些脚本背后的块协议与设计模式,帮你掌握 DIY 极简状态栏的完整方法。

📂 先认识这套状态栏脚本

statusbar/ 目录只包含 5 个短小精悍的脚本,却覆盖了状态栏的全部典型场景:

脚本功能亮点
timedate时间日期2 行代码,最简块
systemstatsCPU/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 dwmblocks

pkill -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
  1. 把"Rec •"写进/tmp/recordingicon(状态落地);
  2. 发RTMIN+3信号(通知状态栏);
  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/recordingiconrecord / guitarrec录制指示文字
/tmp/timericontimer倒计时文字
/tmp/dwmblocks_disk_infodisks当前分区序号 + 显示格式
/tmp/dwmblocks_mus_formatmusicplaying艺术家/专辑格式切换
/tmp/recpid、/tmp/guitarpidrecord / guitarrec录制进程 PID
/tmp/dwmblocks_debug.logsystemstats传感器调试日志

其中 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) || record

PID 文件存在 = 正在录制,同一个快捷键既能开始也能结束录制,无需额外的状态标记。

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环境变量覆盖,默认值 + 可覆盖是不错的配置设计。

五、上手步骤:把这套块装进你的状态栏

  1. 安装 dwmblocks:按 dwmblocks 官方 README 用make && sudo make install编译安装;
  2. 放置块脚本:把 statusbar/ 下的脚本(如 timedate、systemstats)拷贝到~/.config/dwmblocks/;
  3. 在 blocks.h 中声明块:为每个块指定名称与刷新间隔(block结构体),块序号即对应RTMIN+N信号号;
  4. 补全环境变量:在 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!

项目地址:https://gitcode.com/gh_mirrors/scripts58/scripts
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表