Python的select()行为很奇怪

我在理解select.select的行为时遇到了一些麻烦。 请考虑下面的Python程序:

def str_to_hex(s): def dig(n): if n > 9: return chr(65-10+n) else: return chr(48+n) r = '' while len(s) > 0: c = s[0] s = s[1:] a = ord(c) / 16 b = ord(c) % 16 r = r + dig(a) + dig(b) return r while True: ans,_,_ = select.select([sys.stdin],[],[]) print ans s = ans[0].read(1) if len(s) == 0: break print str_to_hex(s) 

我把它保存到一个文件“test.py”。 如果调用它如下:

 echo 'hello' | ./test.py 

然后我得到预期的行为:select永不阻止,并打印所有的数据; 该程序然后终止。

但是如果我交互式地运行这个程序,我会得到一个非常不理想的行为。 请考虑以下控制台会话:

 $ ./test.py hello [<open file '<stdin>', mode 'r' at 0xb742f020>] 68 

程序然后挂在那里; select.select现在再次被阻塞。 直到我提供更多的input或closuresinputstream,即使已经有字符在等待下一个字符(以及所有其余的字符)被打印出来。 任何人都可以向我解释这种行为? 在我写的一个stream隧道程序中,我看到了类似的东西,它正在破坏整个事情。

谢谢阅读!

sys.stdinread方式比select更高的抽象层次。 当你执行ans[0].read(1) ,python实际上会从操作系统中读取更多的字节并在内部进行缓冲。 select不知道这个额外的缓冲; 它只会看到所有的东西都被读取了,所以会被阻塞,直到EOF或者更多的输入到达。 你可以通过运行诸如strace -e read,select python yourprogram.py类的东西来观察这个行为strace -e read,select python yourprogram.py

一种解决方法是用os.read(ans[0].fileno(), 1)替换ans[0].read(1) os.read(ans[0].fileno(), 1)os.read是一个较低级别的界面,它与操作系统之间没有任何缓冲,所以它是一个更好的select匹配。

或者,使用-u命令行选项运行python也似乎禁用额外的缓冲。

它正在等待您发送EOF信号(交互式使用时,您可以使用Ctrl + D进行此操作)。 您可以使用sys.stdin.isatty()来检查脚本是否以交互方式运行,并相应地使用say raw_input来处理它。 我也怀疑你需要使用select.select ,为什么不使用sys.stdin.read

 if sys.stdin.isatty(): while True: for s in raw_input(): print str_to_hex(s) else: while True: for s in sys.stdin.read(1): print str_to_hex(s) 

这将使其适合交互式使用和流处理。