【问题标题】:Send exactly one char to a subprocess' stdin将一个字符准确发送到子进程的标准输入
【发布时间】:2016-02-21 22:35:13
【问题描述】:

我正在尝试unittest 一个从标准输入获取单个按键的模块。获取密钥的代码完美运行,但是将一个字符(字节?)准确地写入子进程的标准输入给我带来了一些问题。

我基本上使用的是推荐的 here,并根据文档和其他 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 只是运行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”(以及其中的变体)不会/不会改变任何内容。

【问题讨论】:

    标签: python subprocess


    【解决方案1】:

    最后一次打印调用应该是Flush=True。

    【讨论】:

      【解决方案2】:

      如果您想发送单个字节 (ignoring the data corruption that Windows may introduce for the redirected data),p.communicate() 代码是正确的。问题是你的helptest.py。

      Ctrl+C 行为表明helptest.py 尝试直接从控制台读取,而不是使用stdin。您似乎在 Windows 上使用 _Getch() from here 使用 msvcrt.getch(),即它可能从控制台读取而不是 stdin。

      另外,sys.stdin.read(1) 可以读取多个字节——sys.stdin 默认处于文本模式。要从标准输入读取一个字节,您可以使用b = os.read(0, 1) 或以二进制模式重新打开sys.stdin,例如,在Python 3 上调用sys.stdin.detach()。

      如果您的意图是读取 key,则可以使用 use readchar package。最好在一处解决可能的(微妙的)问题。

      无关:如果你使用.communicate(),缓冲无关紧要,即你可以删除-u、bufsize。除非子进程被破坏,input=s 的行为应该类似于 input=s + newline(即 EOF 是隐式 EOL)。

      【讨论】:

      • @cat:没有。你很可能也改变了其他东西。我看不出您将 Flush=True 放在哪里以及它如何解释观察到的行为。
      • 我用对write_and_flush 的调用替换了对print 的调用,修复了它。 write_and_flush == print(Flush=True)
      • @cat : 1- 你在问题中的代码已经使用了write_and_flush() 2- flush 应该是小写 3- 它不能解释 Ctrl+C 的行为。我看到的唯一print() 位于父级的末尾,除非有其他问题,否则它不会挂起。
      【解决方案3】:

      您的代码建议您希望 bufsize=1 将缓冲区大小设置为 1,但它会打开行缓冲模式。在写入 EOL 之前,通信会阻塞。

      【讨论】:

      • 这还没有解决,bufsize 与 -unbuffered 选项的组合不会改变任何东西
      猜你喜欢
      • 1970-01-01
      • 2012-12-29
      • 1970-01-01
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      相关资源
      最近更新 更多