在Unix中使用Python读取单个字符(getch风格)不起作用

4 投票
2 回答
9515 浏览
提问于 2025-04-15 12:31

每次我使用这个链接里的代码 http://code.activestate.com/recipes/134892/ 时,总是无法正常工作。它总是报以下错误:

Traceback (most recent call last):
    ...
    old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)

我猜可能是因为我在Eclipse这个软件里运行它,所以 termios 对文件描述符有问题。

2 个回答

5

把终端设置成原始模式并不总是个好主意。其实,只需要清除ICANON这个位就可以了。下面是一个支持超时的getch()函数的另一个版本:

import tty, sys, termios
import select

def setup_term(fd, when=termios.TCSAFLUSH):
    mode = termios.tcgetattr(fd)
    mode[tty.LFLAG] = mode[tty.LFLAG] & ~(termios.ECHO | termios.ICANON)
    termios.tcsetattr(fd, when, mode)

def getch(timeout=None):
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        setup_term(fd)
        try:
            rw, wl, xl = select.select([fd], [], [], timeout)
        except select.error:
            return
        if rw:
            return sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

if __name__ == "__main__":
    print getch()
9

这个在 Ubuntu 8.04.1 和 Python 2.5.2 上是可以正常工作的,我没有遇到那种错误。也许你可以试试从命令行运行,可能是因为 Eclipse 使用了它自己的输入方式。如果我在 Wing IDE 里运行,也会出现同样的错误,但从命令行运行就没问题。原因是像 Wing 这样的开发环境使用了自己的类 netserver.CDbgInputStream 来代替系统的输入,所以 sys.stdin.fileno 返回的是零,这就是错误的原因。简单来说,IDE 的输入不是一个终端(你可以通过打印 sys.stdin.isatty() 来验证,结果是 False)。

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


getch = _GetchUnix()

print getch()

撰写回答