You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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的输出操作会更新该文件的时间戳,导致空闲计数被频繁重置,同时无法正确捕获用户输入。
  • 核心改进点:
    1. 记录asciiquarium的进程组ID(PGID),确保能一次性终止所有相关子进程,而非仅主进程。
    2. 直接监听目标tty的输入事件,替代基于文件时间的空闲检测,避免输出操作干扰。
    3. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 15:42:23