Zsh子shell循环中sleep的替代方案及窗口大小监听优化
Zsh/curses窗口大小自动调整的优化方案
问题背景
使用Zsh 5.9(macOS/Ubuntu环境)结合zsh/curses开发命令行界面时,希望窗口大小变化时自动调整视图。尝试过SIGWINCH信号陷阱但无效,改用后台shell循环检测窗口尺寸并发送SIGUSR1触发刷新,但循环中的sleep命令每次都会创建新进程,缩短睡眠时长又会导致进程频繁生成,需要更高效的实现方式。
方案一:用Zsh内置命令替代外部sleep
Zsh的read命令支持-t参数设置超时,属于内置命令,不会创建额外进程。只需把monitorwindowsize函数中的sleep 0.1替换为read -t 0.1 < /dev/null即可解决进程频繁创建的问题。
修改后的monitorwindowsize函数:
monitorwindowsize () { currentLines=$LINES currentColumns=$COLUMNS while true; do if (( currentLines != LINES || currentColumns != COLUMNS )); then currentLines=$LINES currentColumns=$COLUMNS kill -SIGUSR1 $1 fi # 用内置read替代外部sleep,无额外进程开销 read -t 0.1 < /dev/null done }
说明:< /dev/null用于避免read从标准输入读取内容,确保后台循环稳定运行。
方案二:修复SIGWINCH陷阱(更优方案)
之前SIGWINCH陷阱无效,大概率是因为没有手动更新LINES和COLUMNS变量(Zsh不会在收到SIGWINCH时自动刷新这两个值),或者陷阱设置时机不对。以下是修复后的完整代码:
#!/bin/zsh zmodload zsh/curses view () { zcurses addwin mywin $(( LINES-4 )) $(( COLUMNS-4 )) 2 2 zcurses clear mywin zcurses border mywin zcurses refresh mywin zcurses move mywin $(( (LINES-4)/2 )) $(( (COLUMNS-8)/2 )) zcurses string mywin "Box" zcurses refresh mywin } resetview () { zcurses end tput clear # 手动获取终端尺寸并更新LINES/COLUMNS eval $(stty size | awk '{print "LINES="$1"; COLUMNS="$2}') # 避免窗口不存在时的报错 zcurses clear mywin 2>/dev/null || true zcurses delwin mywin 2>/dev/null || true view } # SIGWINCH信号处理函数 TRAPWINCH() { resetview } TRAPINT() { zcurses end exit } zcurses init view # 主循环等待事件,无额外进程 while true; do read -t 1000 done zcurses end
说明:
- 在
resetview中通过stty size获取当前终端的行和列数,手动更新LINES和COLUMNS变量。 - 将SIGWINCH陷阱设置在
zcurses init之后,确保终端模式初始化完成。 - 去掉后台的
monitorwindowsize进程,直接通过系统SIGWINCH信号触发视图调整,完全消除额外进程开销。
效果演示

内容的提问来源于stack exchange,提问作者user18451290
相关产品推荐
相关产品推荐

