来自带有管道的 termios 的“不适合设备的 ioctl”

问题描述

我昨天在做一个项目,遇到了一个我以前没有遇到过的问题。我目前正在使用 argparse 来请求输入文件名,并且我正在添加对通过 stdin 将文件管道传输到我的程序的支持。我已经完成了所有设置和工作,除了我在将文件通过管道传输到我的程序时遇到的这个 termios 问题,我想知道是否有人知道解决方案。我得到的确切错误

    old_settings = self.termios.tcgetattr(fd)
termios.error: (25,'Inappropriate ioctl for device')

这特别来自 getkey 模块,因为我需要一些非阻塞输入的东西(请随时告诉我更好的选择)。我假设它正在发生是因为它的标准 I/O 流没有连接到终端,因为管道,但我不知道是否有办法解决这个问题,我真的找不到任何解决方案堆栈溢出或谷歌。这是一个最小的可重现示例:

# Assuming filename is test.py,running
# python3 test.py
# works,but running
# cat test.py | python3 test.py
# or
# python3 test.py < test.py
# results in an error
import sys
import termios

termios.tcgetattr(sys.stdin.fileno())

解决方法

我使用之前不知道的 pty 模块找到了一个解决方案。我给出的例子可以通过使用 pty.fork() 将孩子连接到一个新的伪终端来修复。这个程序似乎有效:

import pty
import sys,termios

pid = pty.fork()
if not pid:
    # is child
    termios.tcgetattr(sys.stdin.fileno())

如果错误来自使用 subprocess 创建的新 Python 进程,我还找到了解决方案。此版本利用 pty.openpty() 创建新的伪终端对。

import subprocess,pty,sys

# Create new tty to handle ioctl errors in termios
master_fd,slave_fd = pty.openpty()

proc = subprocess.Popen([sys.executable,'my_program.py'],stdin=slave_fd)

希望这能帮助其他人。