问题描述
我想找出一种以编程方式避免内置 input() 方法停止并等待用户输入的方法。 这是一个片段,显示了我正在尝试做的事情:
import sys
from threading import Thread
def kill_input():
sys.stdout.write('\n')
sys.stdout.flush() # just to make sure the output is really written to stdout and not bufferized
t = Thread(target=kill_input)
t.start()
foo = input('Press some key')
print('input() method has been bypassed')
预期行为:脚本执行并终止,无需等待按下 Enter 键。
相反,发生的事情是程序停止等待用户输入一些输入。
在我看来 input() 应该读取由另一个线程打印在 stdout 上的 换行符 ('\n'
) 并通过执行最终的打印语句终止。该线程应该模拟用户按下回车键。我不明白后面发生了什么
也许另一种可能的方法是从非主线程关闭 stdin 文件描述符并在主线程上捕获异常。
def kill_input():
sys.stdin.close()
可能我想避免这个选项,而是了解这个逻辑背后发生了什么,并找到一种方法来强制主线程从标准输入读取一些模拟字符。
编辑 - 使用 subprocess 模块
基于这些 related posts,我查看了 subprocess 模块。我认为这是 Popen 类派上用场的情况,所以我修改了我的脚本以利用 管道
import sys
from subprocess import Popen,PIPE
def kill_input():
proc = Popen(['python3','-c','pass'],stdin=PIPE)
proc.stdin.write('some text just to force parent proc to read'.encode())
proc.stdin.flush()
proc.stdin.close()
t = Thread(target=kill_input)
t.start()
sys.stdin.read()
print('input() method has been bypassed')
根据我的理解,这应该创建一个带有 Popen 的进程(推荐 python3 -c 'pass' 就像一个占位符),其(应该?)stdin 是一个与父进程一起打开的 unix 管道过程。
我所期望的是写入子进程标准输入的任何内容,以直接进入父进程的标准输入,以便sys.stdin.read()
读取。所以程序不应该停下来等待任何用户输入,它应该立即终止。不幸的是,它没有发生,脚本仍在等待我按 enter。我真的找不到解决方法。
[Python 版本:3.8.5]
解决方法
在您的第一段代码中,您正在写入 sys.stdout
,默认情况下不会影响 sys.stdin
的内容。此外,默认情况下,您不能直接写入 sys.stdin
,但您可以将其更改为不同的文件。为此,您可以使用 os.pipe()
,它将返回一个用于从新管道读取的文件描述符和一个用于写入管道的文件描述符的元组。
然后我们可以在这些文件描述符上使用 os.fdopen
,并将 sys.stdin
分配给管道的读取端,而在另一个线程中我们写入管道的另一端。
import sys
import os
from threading import Thread
fake_stdin_read_fd,fake_stdin_write_fd = os.pipe()
fake_stdin_read = os.fdopen(fake_stdin_read_fd,'r')
fake_stdin_write = os.fdopen(fake_stdin_write_fd,'w')
sys.stdin = fake_stdin_read
def kill_input():
fake_stdin_write.write('hello\n')
fake_stdin_write.flush()
thread = Thread(target=kill_input)
thread.start()
input()
print('input() method has been bypassed!')