Строка чтения подпроцесса застревает

Я пытаюсь создать терминал, работающий на основе пользовательского ввода. Мне удалось получить вывод, но readline() зависает после того, как вывод сгенерирован. Есть ли способ остановить readline после выполнения пользовательской команды. Я добавил небольшой пример функции, чтобы продемонстрировать проблему:


def communicate_with_subprocess(process, command):
    try:
        # Send the command to the subprocess
        process.stdin.write(command + '\n')
        process.stdin.flush()

        lines = []

        # Read the output from the subprocess
        for _line in iter(process.stdout.readline, b''):
            if _line == '':
                break
            lines.append(_line)

        return lines

    except Exception as e:
        print(f"Error executing command '{command}': {e}")
        return None

# Example usage:
subprocess_instance = subprocess.Popen(["/bin/bash"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

try:
    while True:
        user_input = input("Enter a command (type 'exit' to quit): ")

        if user_input.lower() == 'exit':
            print("Exiting...")
            break

        output = communicate_with_subprocess(subprocess_instance, user_input)
        print("Output:", output)

finally:
    subprocess_instance.terminate()
    subprocess_instance.wait()

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

ОК, я покопался в этом подробнее. То, что вы хотите сделать, невозможно.

Проблема заключается в строке process.stdout.readline. Она ожидает неопределенное время (как вы и предполагали), если в вашем подпроцессе больше нечего читать. Обычно это не проблема, поскольку вы закрываете подпроцесс после того, как отдали свои команды. Но вы хотите держать его открытым, следовательно, как ваш процесс чтения (основной скрипт/Django) должен узнать, что больше нечего выводить из подпроцесса? Возможно, вы запустили длинную операцию, которая выдает результаты только каждые 10 минут. Механика проста: если вы вызываете readline, он бесконечно долго ждет, пока ему что-нибудь вернут.

ОК..., что теперь?

У вас есть несколько вариантов:

  • Закрывайте свой подпроцесс, после каждого вызова (@see communicate) и создавайте каждый раз новый.
  • Вы можете держать один открытый shell-процесс, но общаться с ним придется по-другому, из-за описанной выше проблемы.
    • Вы можете делать это асинхронно
    • .
    • Вы сохраняете свой текущий код, но запускаете второй поток, который просто опрашивает все из вашего shell-процесса и помещает это в список, который вы, наконец, можете прочитать (@see A non-blocking read on a subprocess.PIPE in Python)
    • .

Последние мысли

Я все же настоятельно рекомендую рассмотреть другой подход. Почти нет шансов, что вы сможете "продезинфицировать" произвольный пользовательский ввод, чтобы предотвратить создание вредных действий/команд. Если только вы не доверяете своим пользователям на 110%, вам следует подумать о жестком кодировании команд и предоставить пользователю такой ограниченный выбор.

Вернуться на верх