Python交互式shell在子进程中运行时不响应输入。
Python交互式shell在子进程中运行时不响应输入。
我正在做一个终端命令行界面程序,作为一个更大项目的一部分。我希望用户能够运行任意命令(就像在cmd中一样)。问题是,当我使用subprocess启动一个python进程时,python不会写入任何内容到stdout。我甚至不确定它是否读取了我在stdin中写的内容。这是我的代码:
from os import pipe, read, write from subprocess import Popen from time import sleep # 创建stdin/stdout管道 out_read_pipe_fd, out_write_pipe_fd = pipe() in_read_pipe_fd, in_write_pipe_fd = pipe() # 启动进程 proc = Popen("python", stdin=in_read_pipe_fd, stdout=out_write_pipe_fd, close_fds=True, shell=True) # 确保进程已启动 sleep(2) # 向stdin写入内容 write(in_write_pipe_fd, b"print(\"hello world\")\n") # 逐字节读取写入到stdout的所有数据 print("Reading:") while True: print(repr(read(out_read_pipe_fd, 1)))
当我将上述代码中的"python"更改为"myexe.exe"时,代码就能正常工作,其中"myexe.exe"是我用MinGW编译的C++编写的hello world程序。为什么会发生这种情况?这是完整的代码,但上面的示例展示了我的问题。当我将"python"更改为"cmd"时,它也能正常工作。
附:当我在命令提示符中运行python时,它会给我返回:
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>
这意味着应该有内容写入到stdout中。
Python交互式shell在子进程中运行时无法响应输入的原因是,解释器的交互元素通常在命令行中用于运行脚本,因此它们不会被写入到stdout中,否则会干扰脚本输出。为了方便起见,当与用户交互时,解释器使用sys.displayhook方法有意地将输出发送到stdout,否则就不会有输出到stdout。其余部分(例如引导文本和>>>提示符)根据文档写入到stderr中:
stdin用于所有交互输入(包括对input()的调用);
stdout用于print()和表达式语句的输出以及input()的提示符;
解释器自己的提示和错误消息都会被发送到stderr。
这看起来是一个合理的解释,但为什么Python不响应写入到stdin的print("Hello world")呢?而且stderr也是空的。
我想知道交互界面是否使用与底层解释器不同的流?subprocess.run("python", input=b"print('hello')")按预期工作。
这是可能的,但这是否意味着我不能从cmd运行python?我认为cmd只监听stdout/stderr,所以如果python使用它自己的流,cmd不应该在屏幕上显示任何内容。另外,IDLE会捕获进程的stdout和stderr。我将尝试阅读IDLE的源代码。
在这篇文章中,我们要讨论的问题是Python交互式shell在以子进程运行时无法响应输入的问题。问题的原因是连接Python到非tty标准输入,这导致它的行为与在终端中直接运行命令`python`时不同。实际上,它的行为类似于使用命令`cat script | python`,也就是它会等待标准输入关闭,然后将所有内容作为一个脚本执行。这种行为在Python文档中有所描述。为了解决这个问题,我们可以采取两种解决方法。
解决方法一是强制Python以交互模式运行。我们可以使用`-i`标志来忽略Python非交互式运行的事实。在运行`python --help`命令时,可以注意到这个标志。只需要将`Popen`函数的调用改为`Popen("python -i", stdin=in_read_pipe_fd, stdout=out_write_pipe_fd, close_fds=True, shell=True)`,问题就可以得到解决。
解决方法二是模拟终端。通过使用伪终端设备(pty),我们可以将管道连接到tty驱动程序,从而可以自己编写终端仿真器。我们可以使用Python的`pty`模块来打开一个伪终端设备,并将其连接到子进程,而不是普通的管道。这样可以欺骗Python,使其认为它连接到了一个真正的tty设备,还可以模拟按下Ctrl-C、上箭头/下箭头等操作。然而,这种方法也会带来一些问题,因为一些程序在连接到tty时会相应地改变其输出。例如,在许多Linux发行版中,`grep`命令会在输出中使用颜色来标记匹配的模式。如果你不能正确处理程序的输出中的颜色,或者配置了tty以声明不支持颜色(和其他tty特性),那么你将在某些命令的输出中得到垃圾内容。
无论哪种解决方法,都可以解决Python交互式shell在子进程中无法响应输入的问题。解决方法一通过强制Python以交互模式运行来解决,而解决方法二通过模拟终端来解决。根据具体情况选择合适的解决方法即可解决问题。