恰好将一个字符发送到子进程的标准输入
Send exactly one char to a subprocess' stdin
我正在尝试 unittest
一个从标准输入获取单个按键的模块。获取密钥的代码工作得很好,但是将一个字符(字节?)准确地写入子进程的标准输入给我带来了一些问题。
我基本上使用的是 推荐的内容,并根据文档和其他 SO 答案进行了修改:
for ch in range(0, 128):
p = sp.Popen(
[py, "-u", TEST_HELP, "getch"],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
bufsize=1
)
out, err = p.communicate(input=bytes(chr(ch), "ascii"))
print(out, ",", err)
我想要的是 p
接收标准输入的一个 ASCII 字符然后退出。 ch
有时是 NUL
、EOF
和其他控制字符这一事实不是问题;这正是我想要的。
问题是,在我按下 CTRL - C 之前,这似乎什么都不做,然后它会因键盘中断而退出。堆栈跟踪的最后一行是 in selectors.py: fd_event_list = self._poll.poll(timeout)
,它告诉我它正在等待超时(?),但我没有提供 timeout=int
kwarg。
我正在使用的命令解析为 python3 -u helptest.py getch
,它看起来像这样,当我自己从命令行 运行 它时它可以正常工作。
这是helptest
的相关部分:
def getch():
write_and_flush(ord(_ic._Getch()))
(write_and_flush
只是 运行s stdout.write; stdout.flush
)
和_ic._Getch()
是:
def _Getch():
if sys.stdin.isatty():
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
else:
return sys.stdin.read(1)
我在 subprocess
调用中哪里做错了?
将调用更改为:
p = sp.Popen(
[py, TEST_HELP, "getch"],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
)
out, err = p.communicate(input=bytes(chr(ch) + "\n", "ascii"))
通过省略 bufsize
、删除 --unbuffered
选项并添加 "EOL"(以及其中的变体)不会t/don改变任何内容。
您的代码表明您希望 bufsize=1
将缓冲区大小正好设置为 1,但它却打开了行缓冲模式。通信阻塞,直到写入 EOL。
最后一次打印调用应该有 Flush=True
。
如果要发送单个字节 (),p.communicate()
代码是正确的。问题是你的 helptest.py
.
Ctrl+C 行为表明 helptest.py
尝试直接从控制台读取而不是使用 stdin
。似乎您使用 _Getch()
from here 在 Windows 上使用 msvcrt.getch()
即,它可能从控制台读取而不是 stdin
。
此外,sys.stdin.read(1)
可能会读取一个以上的字节 -- 默认情况下 sys.stdin
处于文本模式。要从标准输入读取一个字节,您可以使用 b = os.read(0, 1)
或以二进制模式重新打开 sys.stdin
,例如,在 Python 上调用 sys.stdin.detach()
3.
如果您的目的是阅读 key,那么您可以 use readchar
package。最好在一个地方解决可能的(微妙的)问题。
无关:如果您使用 .communicate()
,缓冲无关紧要,即,您可以删除 -u
、bufsize
。除非子进程被破坏 input=s
应该表现得类似于 input=s + newline
(即 EOF 是隐式 EOL)。
我正在尝试 unittest
一个从标准输入获取单个按键的模块。获取密钥的代码工作得很好,但是将一个字符(字节?)准确地写入子进程的标准输入给我带来了一些问题。
我基本上使用的是
for ch in range(0, 128):
p = sp.Popen(
[py, "-u", TEST_HELP, "getch"],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
bufsize=1
)
out, err = p.communicate(input=bytes(chr(ch), "ascii"))
print(out, ",", err)
我想要的是 p
接收标准输入的一个 ASCII 字符然后退出。 ch
有时是 NUL
、EOF
和其他控制字符这一事实不是问题;这正是我想要的。
问题是,在我按下 CTRL - C 之前,这似乎什么都不做,然后它会因键盘中断而退出。堆栈跟踪的最后一行是 in selectors.py: fd_event_list = self._poll.poll(timeout)
,它告诉我它正在等待超时(?),但我没有提供 timeout=int
kwarg。
我正在使用的命令解析为 python3 -u helptest.py getch
,它看起来像这样,当我自己从命令行 运行 它时它可以正常工作。
这是helptest
的相关部分:
def getch():
write_and_flush(ord(_ic._Getch()))
(write_and_flush
只是 运行s stdout.write; stdout.flush
)
和_ic._Getch()
是:
def _Getch():
if sys.stdin.isatty():
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
else:
return sys.stdin.read(1)
我在 subprocess
调用中哪里做错了?
将调用更改为:
p = sp.Popen(
[py, TEST_HELP, "getch"],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
)
out, err = p.communicate(input=bytes(chr(ch) + "\n", "ascii"))
通过省略 bufsize
、删除 --unbuffered
选项并添加 "EOL"(以及其中的变体)不会t/don改变任何内容。
您的代码表明您希望 bufsize=1
将缓冲区大小正好设置为 1,但它却打开了行缓冲模式。通信阻塞,直到写入 EOL。
最后一次打印调用应该有 Flush=True
。
p.communicate()
代码是正确的。问题是你的 helptest.py
.
Ctrl+C 行为表明 helptest.py
尝试直接从控制台读取而不是使用 stdin
。似乎您使用 _Getch()
from here 在 Windows 上使用 msvcrt.getch()
即,它可能从控制台读取而不是 stdin
。
此外,sys.stdin.read(1)
可能会读取一个以上的字节 -- 默认情况下 sys.stdin
处于文本模式。要从标准输入读取一个字节,您可以使用 b = os.read(0, 1)
或以二进制模式重新打开 sys.stdin
,例如,在 Python 上调用 sys.stdin.detach()
3.
如果您的目的是阅读 key,那么您可以 use readchar
package。最好在一个地方解决可能的(微妙的)问题。
无关:如果您使用 .communicate()
,缓冲无关紧要,即,您可以删除 -u
、bufsize
。除非子进程被破坏 input=s
应该表现得类似于 input=s + newline
(即 EOF 是隐式 EOL)。