Как я могу направить первоначальный ввод в процесс, который затем будет интерактивным? - PullRequest
37 голосов
/ 30 апреля 2011

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

echo "initial command" | INSERT_MAGIC_HERE some_tool

tool> initial command 

[result of initial command] 

tool> [now I type an interactive command]

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

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

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

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

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

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

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

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

Ответы [ 4 ]

35 голосов
/ 02 мая 2011

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

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

Недостатком является подключение трубы к some_tool, а не к терминалу.

5 голосов
/ 04 июня 2015

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

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

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

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

Аналогично, 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 небольшими порциями, чтобы избежать его)

2 голосов
/ 21 июля 2018

Это легко сделать с помощью программы «ожидаемо», которая предназначена для того, чтобы вы могли писать сценарии для взаимодействия с программами.

Я проверил это, написав сценарий ожидания bc.exp для запуска калькулятора »bc"и отправьте команду" obase = 16 ", чтобы перевести ее в шестнадцатеричный режим вывода, а затем передайте управление мне.

Сценарий (в файле с именем bc.exp):

spawn bc
send "obase=16\n"
interact {
 \003 exit
}

Один запускает его с

expect bc.exp
0 голосов
/ 30 апреля 2011

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

[axe@gromp ~]$ bc <<END
> 3 + 4
> END
7

Сеанс bc впоследствии остается открытым, так что обеспечивается между маркерами начала и конца (между "<

...