我在理解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
然后我得到预期的行为:选择never块并打印所有数据;程序然后终止。
但如果我以交互方式运行程序,我会得到一个最不受欢迎的行为。请考虑以下控制台会话:
$ ./test.py
hello
[<open file '<stdin>', mode 'r' at 0xb742f020>]
68
程序然后挂在那里; select.select现在再次阻止。直到我提供更多输入或关闭输入流,即使已经有字符在等待,也会打印下一个字符(以及其余所有字符)!任何人都可以向我解释这种行为吗?我在一个我编写的流隧道程序中看到了类似的东西,它破坏了整个事件。
感谢阅读!
答案 0 :(得分:9)
read
sys.stdin
方法的工作抽象级别高于select
。执行ans[0].read(1)
时,python实际上从操作系统中读取了大量字节并在内部缓冲它们。 select
不知道这个额外的缓冲;它只会看到所有内容都已被读取,因此会阻止,直到EOF或更多输入到达。您可以通过运行类似strace -e read,select python yourprogram.py
的内容来观察此行为。
一种解决方案是将ans[0].read(1)
替换为os.read(ans[0].fileno(), 1)
。 os.read
是一个较低级别的界面,它与操作系统之间没有任何缓冲,因此它与select
更匹配。
或者,使用python
命令行选项运行-u
似乎也会禁用额外的缓冲。
答案 1 :(得分:1)
正在等待您发出EOF信号(交互使用时,您可以使用Ctrl + D执行此操作)。您可以使用sys.stdin.isatty()
检查脚本是否以交互方式运行,并相应地使用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)
这将使其适用于交互式使用和流处理。