Подтвердить что ты не робот

Как я могу направить исходный вход в процесс, который затем будет интерактивным?

Я хотел бы иметь возможность ввести начальную команду в запуск интерактивного процесса, чтобы я мог сделать что-то вроде этого:

echo "initial command" | INSERT_MAGIC_HERE some_tool

tool> initial command 

[result of initial command] 

tool> [now I type an interactive command]

Что не работает:

  • Простое соединение исходной команды не работает, так как это приводит к тому, что stdin не подключается к терминалу

  • Запись в/dev/pts/[число] отправляет вывод на терминал, а не вход в процесс, как если бы он был с терминала

Что было бы с недостатками:

  • Сделайте команду, которая разворачивает ребенка, записывает его в stdin, а затем перенаправляет все из своего собственного stdin. Даунсайд - элементы управления терминалом (например, режим линии против символьного) не будут работать. Может быть, я мог бы сделать что-то с проксированием псевдотерминалов?

  • Создайте модифицированную версию xterm (в любом случае я запускаю ее для этой задачи) с параметром командной строки, чтобы вставлять дополнительные команды после получения нужной строки приглашения. Некрасиво.

  • Сделайте модифицированную версию инструмента, который я пытаюсь запустить, чтобы он принял начальную команду в командной строке. Нарушает стандартную установку.

(Инструмент текущего интереса, кстати, является оболочкой adroid adb - я хочу открыть интерактивную оболочку на телефоне, запустить команду автоматически, а затем провести интерактивный сеанс)

4b9b3361

Ответ 1

Вам не нужно писать новый инструмент для пересылки stdin - он уже написан (cat):

(echo "initial command" && cat) | some_tool

У этого есть недостаток подключения трубы к some_tool, а не к терминалу.

Ответ 2

Принятый ответ прост и в основном хорош.

Но у него есть недостаток: программы получают в качестве своего входного канала, а не терминал. Это означает, что автозаполнение не будет работать. Во многих случаях это также отключает хороший вывод, и я слышал, что некоторые программы просто отказываются работать, если stdin не является терминалом.

Следующая программа решает проблему. Он создает псевдотерминал, запускает программу, связанную с этим псевдотерминалом. Сначала он кормит дополнительный вход, переданный через командную строку, а затем подает его ввод данных пользователем через stdin.

Например, ptypipe "import this" python3 заставляет Python выполнять сначала "импортировать это", а затем он переносит вас в интерактивную командную строку с помощью рабочее завершение и прочее.

Аналогично, ptypipe "date" bash запускает Bash, который выполняет date, а затем дает вам оболочку. Опять же, с завершением работы, цветовой подсказкой и т.д.

#!/usr/bin/env python3

import sys
import os
import pty
import tty
import select
import subprocess

STDIN_FILENO = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2

def _writen(fd, data):
    while data:
        n = os.write(fd, data)
        data = data[n:]

def main_loop(master_fd, extra_input):
    fds = [master_fd, STDIN_FILENO]

    _writen(master_fd, extra_input)

    while True:
        rfds, _, _ = select.select(fds, [], [])
        if master_fd in rfds:
            data = os.read(master_fd, 1024)
            if not data:
                fds.remove(master_fd)
            else:
                os.write(STDOUT_FILENO, data)
        if STDIN_FILENO in rfds:
            data = os.read(STDIN_FILENO, 1024)
            if not data:
                fds.remove(STDIN_FILENO)
            else:
                _writen(master_fd, data)

def main():
    extra_input = sys.argv[1]
    interactive_command = sys.argv[2]

    if hasattr(os, "fsencode"):
        # convert them back to bytes
        # http://bugs.python.org/issue8776
        interactive_command = os.fsencode(interactive_command)
        extra_input = os.fsencode(extra_input)

    # add implicit newline
    if extra_input and extra_input[-1] != b'\n':
        extra_input += b'\n'

    # replace LF with CR (shells like CR for some reason)
    extra_input = extra_input.replace(b'\n', b'\r')

    pid, master_fd = pty.fork()

    if pid == 0:
        os.execlp("sh", "/bin/sh", "-c", interactive_command)

    try:
        mode = tty.tcgetattr(STDIN_FILENO)
        tty.setraw(STDIN_FILENO)
        restore = True
    except tty.error:    # This is the same as termios.error
        restore = False

    try:
        main_loop(master_fd, extra_input)
    except OSError:
        if restore:
            tty.tcsetattr(0, tty.TCSAFLUSH, mode)

    os.close(master_fd)
    return os.waitpid(pid, 0)[1]

if __name__ == "__main__":
    main()

(Примечание: я боюсь, что это решение содержит возможный тупик. Возможно, вы захотите подать extra_input маленькими кусками, чтобы избежать этого)

Ответ 3

Возможно, вы можете использовать здесь документ, чтобы передать свой ввод в abd. E. g. (используя bc для простого вычисления в качестве примера).

[[email protected] ~]$ bc <<END
> 3 + 4
> END
7

Сеанс bc остается открытым после этого, поэтому то, что предусмотрено между начальным и конечным маркерами (между "< END" и "END" ), будет передано команде.