Получить пользовательский ввод при постоянном чтении стандартного ввода из канала в Python? - PullRequest
2 голосов
/ 08 февраля 2020

Учитывая программу с именем 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 не позволяет go. Есть ли способ взять входные данные из того, что мы передаем по трубопроводу, и переместить их в отдельный канал, чтобы мы все еще могли использовать stdin для получения пользовательского ввода?

1 Ответ

0 голосов
/ 10 февраля 2020

Что касается статуса файловых потоков, то 1002 * ничего особенного в stdin/stdout/stderr 1 нет. После запуска программы вы можете обращаться с ними как с любым обычным файловым дескриптором. Так что идея повторного связывания stdin не нужна. Вместо этого, это действительно больше похоже на вопрос о том, как настроить одновременные входные потоки.

Мое первое желание - использовать пару потоков, но я уверен, что это можно сделать с помощью async/await и возможно, есть и другие способы. В этом случае с потоками должно быть все в порядке, так как оба потока будут в конечном итоге связаны с вводом-выводом и должны освободить 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, а другой читает из оригинального stdin, который должен быть присоединен к stdout команды tail -f. Оба потока тратят большую часть своего времени на блокировку ввода. Как я уже сказал, это могло бы быть лучше, но это показывает общую идею.


1: Они все еще являются особенными по нескольким причинам: вы получаете их бесплатно; они предварительно установлены в определенной стандартизированной конфигурации; они получают специальную обработку как устройства в /dev; дочерние процессы наследуют их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...