2015-08-19 3 views
0

A: Почему это блокируется?Интерактивный, неблокирующий подпроцесс. Открытый сценарий без использования связи или pexpect

B: Как я могу массировать этот немного так, чтобы он работал без блокировки?

#!/usr/bin/env python 
import subprocess as sp 
import os 

kwds = dict(
    stdin=sp.PIPE, 
    stdout=sp.PIPE, 
    stderr=sp.PIPE, 
    cwd=os.path.abspath(os.getcwd()), 
    shell=True, 
    executable='/bin/bash', 
    bufsize=1, 
    universal_newlines=True, 
) 
cmd = '/bin/bash' 
proc = sp.Popen(cmd, **kwds) 
proc.stdin.write('ls -lashtr\n') 
proc.stdin.flush() 

# This blocks and never returns 
proc.stdout.read() 

Мне нужно это для интерактивного запуска.

Это упрощенный пример, но на самом деле у меня длительный процесс, и я бы хотел запустить сценарий оболочки, который может более или менее запускать произвольный код (потому что это сценарий установки).

EDIT: Я хотел бы эффективно использовать .bash_history для нескольких разных логинов, очистить его, чтобы это был единственный скрипт, а затем выполнить вновь созданный сценарий оболочки по очереди в оболочке, хранящейся в Python.

Например:

> ... ssh to remote aws system ... 
> sudo su - 
> apt-get install stuff 
> su - $USERNAME 
> ... create and enter a docker snapshot ... 
> ... install packages, update configurations 
> ... install new services, update service configurations ... 
> ... drop out of snapshot ... 
> ... commit the snapshot ... 
> ... remove the snapshot ... 
> ... update services ... 
> ... restart services ... 
> ... drop into a tmux within the new docker ... 

Это занимает несколько часов вручную; он должен быть автоматизирован.

+0

ваш пример кода, который может быть заменен на 'check_output ([«LS»,«-lashtr»])' не соответствует вопросу (это слишком упрощенно, чтобы иметь смысл для вопроса, описанного в тексте). Нелегко использовать «подпроцесс» для диалогового взаимодействия с дочерним процессом. Забудьте о * «слегка» * в общем случае. Текст вопроса (а не код) слишком широк: что * * «расщепляет ошибки» * ([вы хотите удалить stdout/stderr отдельно?] (Http://stackoverflow.com/questions/31926470)) , Как вы находите границы между выводами нескольких команд? Что такое повторный запуск? – jfs

+0

Я хочу открыть процесс и оставить его открытым. Общение не может помочь мне в этом, так как он закрывает процесс. Если вам нужна дополнительная информация, рассмотрите что-то вроде: docker run -i -t ubuntu/bin/bash; su - ; запустить сценарий; проверить выход; <- хранить все это в журнале на локальной машине. –

+0

Не говорите нам, что, по вашему мнению, не сработает: (1) вы можете ошибаться, например, вы можете запускать несколько команд с помощью '.communicate()' (2), это не говорит нам, какова ваша фактическая проблема. Ответьте на вопросы из моего предыдущего комментария. – jfs

ответ

0

A: Почему он блокируется?

Он блокирует, потому что это то, что делает .read(): он считывает все байты до индикации конца файла. Поскольку процесс никогда не указывает конец файла, .read() никогда не возвращается.

B: Как я могу немного массировать это (акцент на слегка), чтобы он работал без блокировки?

Одна вещь, чтобы сделать это, чтобы заставить процесс указывать конец файла. Небольшое изменение приведет к выходу подпроцесса.

proc.stdin.write('ls -lashtr; exit\n') 
+0

Я хочу держать раковину открытой. –

-1

Это пример формирования мой другой ответ: https://stackoverflow.com/a/43012138/3555925, который не использовал pexpect. Вы можете увидеть больше деталей в этом ответе.

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

import os 
import sys 
import select 
import termios 
import tty 
import pty 
from subprocess import Popen 

command = 'bash' 
# command = 'docker run -it --rm centos /bin/bash'.split() 

# save original tty setting then set it to raw mode 
old_tty = termios.tcgetattr(sys.stdin) 
tty.setraw(sys.stdin.fileno()) 

# open pseudo-terminal to interact with subprocess 
master_fd, slave_fd = pty.openpty() 

# use os.setsid() make it run in a new process group, or bash job control will not be enabled 
p = Popen(command, 
      preexec_fn=os.setsid, 
      stdin=slave_fd, 
      stdout=slave_fd, 
      stderr=slave_fd, 
      universal_newlines=True) 

while p.poll() is None: 
    r, w, e = select.select([sys.stdin, master_fd], [], []) 
    if sys.stdin in r: 
     d = os.read(sys.stdin.fileno(), 10240) 
     os.write(master_fd, d) 
    elif master_fd in r: 
     o = os.read(master_fd, 10240) 
     if o: 
      os.write(sys.stdout.fileno(), o) 

# restore tty settings back 
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)