如何将asciiquarium输出定向到/dev/pts/2并在用户输入时终止进程
问题
想把asciiquarium改造成基于空闲时间触发的终端屏保,将其标准输出定向到/dev/pts/2时遇到异常(推测该程序启动了多个后台子进程)。原本打算通过记录进程PID,在检测到用户输入时终止进程,但发现一旦asciiquarium输出内容到/dev/pts/2,该tty的空闲状态检测就会失效(因为输出操作会更新tty的访问时间)。
需求:实现将asciiquarium的输出发送到/dev/pts/2,同时在/dev/pts/2检测到用户输入时,立即终止asciiquarium及其所有子进程。
当前使用的脚本:
# pts=$(who am i | awk '{print $2}') pts="pts/2" echo $pts >> test.log old=$(stat -c %Y /dev/$pts) ct=0 activated=false while true do sleep 1s new=$(stat -c %Y /dev/$pts) if [[ $new -eq $old ]] # If new and old ts are the same it means that pts is idle then ((ct++)) echo "$ct" >> test.log else ct=0 echo "Resetting count!" >> test.log activated=false fi if [[ $ct -gt 10 ]] then if ! $activated then activated=true echo "Activating!" >> test.log asciiquarium > /dev/$pts & else continue fi fi old=$new done
解决方案
- 问题根源:用
stat读取/dev/pts/2的修改时间判断空闲不可靠,因为asciiquarium的输出操作会更新该文件的时间戳,导致空闲计数被频繁重置,同时无法正确捕获用户输入。 - 核心改进点:
- 记录asciiquarium的进程组ID(PGID),确保能一次性终止所有相关子进程,而非仅主进程。
- 直接监听目标tty的输入事件,替代基于文件时间的空闲检测,避免输出操作干扰。
- 修改tty的终端属性,临时关闭输入回显,避免屏保运行时用户输入的字符破坏显示效果,退出时恢复原设置。
修改后的脚本:
#!/bin/bash TARGET_PTS="/dev/pts/2" IDLE_THRESHOLD=10 # 空闲触发秒数 activated=false aquarium_pgid=0 listener_pid=0 # 监听用户输入的函数 listen_input() { # 保存原终端属性 original_stty=$(stty -g < "$TARGET_PTS") # 临时关闭回显和规范输入模式 stty -echo -icanon < "$TARGET_PTS" # 等待输入,只要有字符就读取 read -n 1 -s < "$TARGET_PTS" # 恢复终端属性 stty "$original_stty" < "$TARGET_PTS" # 终止asciiquarium进程组 if [ $aquarium_pgid -ne 0 ]; then kill -TERM -$aquarium_pgid 2>/dev/null aquarium_pgid=0 fi activated=false } while true; do if [ "$activated" = false ]; then # 计算tty空闲时间 last_access=$(stat -c %X "$TARGET_PTS") current_time=$(date +%s) idle_time=$((current_time - last_access)) if [ $idle_time -ge $IDLE_THRESHOLD ]; then activated=true # 启动asciiquarium并记录进程组ID asciiquarium > "$TARGET_PTS" & aquarium_pid=$! aquarium_pgid=$(ps -o pgid= -p $aquarium_pid | tr -d ' ') # 后台启动输入监听 listen_input & listener_pid=$! fi else # 检查asciiquarium是否存活,若已退出则重置状态 if ! ps -p $aquarium_pid >/dev/null 2>&1; then activated=false aquarium_pgid=0 kill $listener_pid 2>/dev/null fi fi sleep 1 done
- 脚本说明:
listen_input函数负责监听目标tty的输入,一旦捕获到用户按键,立即恢复终端属性并终止asciiquarium的整个进程组。- 空闲检测通过计算当前时间与tty最后访问时间的差值实现,若asciiquarium输出仍会干扰该时间戳,X环境下可改用
xprintidle命令获取系统全局空闲时间,准确性更高。 - 启动asciiquarium后记录其PID和PGID,确保能彻底终止所有关联子进程,避免残留后台任务。
内容的提问来源于stack exchange,提问作者Sterling Butters
相关产品推荐
相关产品推荐

