Предыстория . У меня есть подпроцесс Python, который подключается к оболочкообразному приложению, которое использует библиотеку readline для обработки ввода, и в этом приложении есть подпрограмма, полная TAB для ввода команд, подобно bash. Дочерний процесс создается так:

def get_cli_subprocess_handle():
    return subprocess.Popen(
                            '/bin/myshell',
                            shell=False,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            )

Все отлично работает, кроме Tab-Complete. Всякий раз, когда моя Python-программа передает символ табуляции '\t' в подпроцесс, я получаю 5 пробелов в STDIN вместо того, чтобы запускать процедуру завершения табуляции библиотеки readline. :(

Вопрос . Что я могу отправить в STDIN подпроцесса, чтобы активировать функцию завершения табуляции у ребенка? Может быть, спросили по-другому: как отправить ключ в TAB, а не в символ , если это даже возможно?

Связанные, но без ответа и сорванные.

завершение вкладки триггера для пакетного процесса Python, построенного на readline

2
Trevor 28 Янв 2013 в 19:07

3 ответа

Лучший ответ

Оболочкообразное приложение, вероятно, различает терминал, подключенный к stdin, и канал, подключенный к нему. Многие утилиты Unix делают именно это для оптимизации своей буферизации (строка против блока), а утилиты, подобные оболочке, могут отключать средства завершения команд на пакетном вводе (то есть PIPE), чтобы избежать неожиданных результатов. Завершение команд - это действительно интерактивная функция, которая требует ввода с терминала.

Посмотрите модуль pty и попробуйте использовать пару ведущий / ведомый в качестве канала для вашего подпроцесса.

3
isedev 28 Янв 2013 в 15:52

На самом деле нет такой вещи, как отправка вкладки ключ в канал. Канал может принимать только строки битов, и, если символ табуляции не делает этого, решение может не быть.

Есть проект, который делает нечто похожее, называется pexpect. Просто глядя на его interact() код, я не вижу ничего очевидного, что делает его работающим, а ваше - нет. Учитывая это, наиболее вероятным объяснением является то, что pexpect действительно выполняет некоторую работу, чтобы сделать себя похожим на псевдотерминал. Возможно, вы могли бы включить его код для этого?

2
Ken Kinder 28 Янв 2013 в 15:46

Основываясь на ответе isedev, я изменил свой код следующим образом:

import os, pty

def get_cli_subprocess_handle():
    masterPTY, slaveTTY = pty.openpty()
    return masterPTY, slaveTTY, subprocess.Popen(
                                                 '/bin/myshell',
                                                 shell=False,
                                                 stdin=slaveTTY,
                                                 stdout=slaveTTY,
                                                 stderr=slaveTTY,
                                                 )

Используя этот возвращенный кортеж, я смог выполнить select.select([masterPTY],[],[]) и os.read(masterPTY, 1024) по мере необходимости, и я написал в master-pty функцию, которая очень похожа на закрытый метод в источнике модуля pty:

def write_all(masterPTY, data):
    """Successively write all of data into a file-descriptor."""
    while data:
        chars_written = os.write(masterPTY, data)
        data = data[chars_written:]
    return data

Спасибо всем за хорошие решения. Надеюсь, этот пример поможет кому-то еще. :)

1
Community 23 Май 2017 в 11:56