假设我有一个输出某些应用程序状态的命令。此命令将永远运行(或直到抄送),并在应用程序内部发生某些情况时输出一行。它输出以下格式
State N: Stuff M happened
我想要一个程序,我们称之为它collapse_prog
,它根据我提供给它的正则表达式获取命令输出的行来覆盖以前的状态。例如,假设输入正则表达式是 State (\d+),我运行以下命令
my_command | collapse_prog
和my_command
输出
State 1: Stuff A happened
State 2: Stuff B happened
那么collapse_prog
应该按原样显示,但在my_command
输出另一行之后
State 1: Stuff C happened
那么collapse_prog
应该有以下输出
State 1: Stuff C happened
State 2: Stuff B happened
已经有这样的程序了吗?
答案1
非常基本的解决方案TXR口齿不清:
$ program | txr stcoll.tl
哪里stcoll.tl
:
(let ((states (hash)))
(whilet ((line (get-line)))
(when-match `State @s: @status` line
(set [states s] status)
(dohash (s status states)
(put-line `State @s: @status`))
(put-string `\e[@(len states)A`)
(flush-stream *stdout*))))
它的工作原理是,每个状态的最新状态都保存在哈希表中,并以状态 ID 为键。每次我们获得状态行时,我们都会更新哈希值,然后打印它。打印后,我们使用ESC[<n>A
VT100/ANSI 转义序列将光标向后移动那么多行。这假设终端窗口对于所有状态都足够高,这对于应该是非常简单的交互式观看工具来说是公平的。
更复杂的程序会在超时(例如一秒)的情况下轮询标准输入。或者可配置。然后每次通过输入循环,是否收到一行或者是否超时,检查hash是否改变,如果改变,则自上次刷新以来的刷新周期是否已过期。在这种情况下,请刷新显示。
当输入快速到达时,这样的程序将更有效地更新显示。
这里是:
(let* ((states (hash))
(last-time 0)
(hash-touched nil)
(unbuf-stdin (open-fileno (fileno *stdin*) "u"))
(poll-list (vec (cons unbuf-stdin poll-in))))
(while t
(if (poll poll-list 1000)
(let ((line (get-line unbuf-stdin)))
(when-match `State @s: @status` line
(set [states s] status
hash-touched t))))
(let ((now (time)))
(when (and hash-touched (>= (- now last-time) 1))
(dohash (s status states)
(put-line `State @s: @status`))
(put-string `\e[@(len states)A`)
(flush-stream *stdout*)
(set hash-touched nil
last-time now)))))
即使程序正在喷涌,它也会以一秒的间隔更新。我正在使用这个程序来生成测试输入。它随机递增 20 个状态之一并打印更新,并带有随机暂停:
(let* ((nstates 20)
(states (vector nstates 0)))
(while t
(let ((choice (rand nstates)))
(inc [states choice])
(put-line `State @choice: @[states choice]`)
(flush-stream *stdout*)
(if (zerop (rand 4) )
(usleep (rand 500000))))))