获取用户输入,同时不断从 Python 中的管道读取 stdin?



给定一个名为interactive的程序,其启动方式如下:

% tail -f logfile | interactive

在 Python 中,我们可以通过覆盖引用将stdin重置为stdin以外的其他内容:

import sys 
text = sys.stdin.read() 
# rebind sys.stdin to tty 
sys.stdin = open("/dev/tty") 
user_in = input("Selection? : ") 

但是,如果我们需要继续从我们永远管道中读取文件,这是行不通的,因为原始stdin管道不会松开。有没有办法从我们正在管道中获取输入并将其移动到单独的管道中,以便我们仍然可以使用stdin来获取用户输入?

就它们作为文件流的状态而言,stdin/stdout/stderr1没有什么特别之处。程序启动后,您可以像对待任何常规文件描述符一样对待它们。因此,重新绑定stdin的想法是不必要的。相反,这似乎更像是一个关于如何设置并发输入流的问题。

我的第一个倾向是使用几个线程,但我相信这可以使用async/await和其他一些方式来完成。在这种情况下,线程应该没问题,因为两个线程最终都会受到 IO 绑定,并且应该释放 GIL(每个人都喜欢对 GIL 感到兴奋(。这非常接近(不完美,但在球场内...

#!/usr/bin/env python3
import sys
import threading
from time import sleep
user_input = ""
def tty_reader():
global user_input
read_fd = open("/dev/tty", 'r')
while 1:
print("interactive-% ", end="", flush=True)
user_input = read_fd.readline() # block for input
print("nUser Input: " + str(user_input), end="", flush=True)
def pipe_reader():
while 1:
stdin_text = sys.stdin.readline() # block for input
if stdin_text != user_input:
print("nPipe Message: " + str(stdin_text), end="", flush=True)
t_tty_reader = threading.Thread(target=tty_reader)
t_pipe_reader = threading.Thread(target=pipe_reader)
t_pipe_reader.start()
sleep(3) # to let the existing 'tail' lines finish printing
t_tty_reader.start()

将其保存到文件中,chmod 755它,然后像这样运行:

tail -f <some file> | ./<python script>

这将启动 2 个线程。一个从/dev/tty读取,另一个从应附加到tail -f命令stdout的原始stdin读取。两个线程都花费大部分时间阻塞输入。正如我所说,这可能会更好,但它显示了总体思路。


1:它们仍然有点特殊,有几个原因:你免费获得它们;它们在一定的标准化配置中预先安装;它们作为/dev中的设备得到一些特殊处理;子进程继承它们。

最新更新