Ответ 1
Вам не нужно писать новый инструмент для пересылки stdin
- он уже написан (cat
):
(echo "initial command" && cat) | some_tool
У этого есть недостаток подключения трубы к some_tool
, а не к терминалу.
Я хотел бы иметь возможность ввести начальную команду в запуск интерактивного процесса, чтобы я мог сделать что-то вроде этого:
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 - я хочу открыть интерактивную оболочку на телефоне, запустить команду автоматически, а затем провести интерактивный сеанс)
Вам не нужно писать новый инструмент для пересылки stdin
- он уже написан (cat
):
(echo "initial command" && cat) | some_tool
У этого есть недостаток подключения трубы к some_tool
, а не к терминалу.
Принятый ответ прост и в основном хорош.
Но у него есть недостаток: программы получают в качестве своего входного канала, а не терминал. Это означает, что автозаполнение не будет работать. Во многих случаях это также отключает хороший вывод, и я слышал, что некоторые программы просто отказываются работать, если 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 маленькими кусками, чтобы избежать этого)
Возможно, вы можете использовать здесь документ, чтобы передать свой ввод в abd
. E. g. (используя bc
для простого вычисления в качестве примера).
[[email protected] ~]$ bc <<END
> 3 + 4
> END
7
Сеанс bc
остается открытым после этого, поэтому то, что предусмотрено между начальным и конечным маркерами (между "< END" и "END" ), будет передано команде.