在Python中读取stdin时响应键盘事件

1 投票
1 回答
1087 浏览
提问于 2025-04-17 05:24

设置

我最近在玩一个脚本,目的是把 tail -f 的输出接入,并高亮显示一些特定的关键词。这个项目不大,但我觉得挺有用的。

目前,主要的循环大致是这样的:

...
line = True
while line:
    line = sys.stdin.readline()
    sys.stdout.write(highlight(line))
...

我想在这个循环中监听某些按键,以便在日志中打印一条标记线。我找到了一种方法,看起来可以用来获取按键输入,具体描述在 http://code.activestate.com/recipes/134892/ 上,但它是一次读取一个字符,而我的主循环是从输入中读取整行,这样就不太适用了。

问题

在Python中,有没有办法在读取管道输入的同时监听按键?

我已经把主函数放在一个 try 块里,这样可以捕获 KeyboardInterrupt,并打印一个友好的告别信息,而不是错误堆栈信息。有没有办法在这个行为上再加一个按键的监听?

我不想使用像pygame或tkinter这样比较复杂的模块(相对于我的小脚本),也不想被迫使用它们的主循环来获取按键输入。(而且我也不太清楚它们在接收管道输入时会有什么表现)

1 个回答

2

最简单的解决办法就是完全不使用tail这个工具。你可以直接用标准输入(stdin)来获取用户输入,然后把你的工具写成可以直接打开并读取你想要的文件,而不需要用到tail。如果你只是想实现tail的某个功能,其实tail并不是一个特别复杂的工具。

另外,你可以创建一个命名管道,这样就可以把tail的输出接入这个管道,然后你的程序就可以像读取文件一样读取它,同时还能使用自己的标准输入。

如果这两种方法都不行,我觉得使用一个可以直接监测键盘输入的库可能是最好的选择。顺便说一下,pygame实际上并没有一个“主循环”,它有一个事件处理函数,你应该尽可能频繁地调用(在你的主循环中),但如果不调用,它也不会崩溃。它可以在事件处理之间缓存很多事件,你还可以设置过滤器,丢弃那些你不关心的事件,只保留你需要的(比如按键事件)。不过,pygame的问题在于它是一个比较重的库,对于这么简单的工具来说,使用它可能有点过于复杂,所以我理解你对此的犹豫。

最后,还有一个选择(我认为是最不理想的),就是利用键盘中断机制来创建“类似Emacs”的按键组合。比如,先按Ctrl-C,然后再按Ctrl-(其他键)。Ctrl-C会触发键盘中断,然后在处理这个中断的函数里读取下一个字符并进行相应的操作。完成后再回到你的循环中。但这种方法比较难看且不太稳定,我也不确定它是否能可靠地工作。

撰写回答