2016-05-22 2 views
4

У меня есть следующий код, который работает точно так же, как предполагалось:Как воспроизвести `stdin = sys.stdin` с` stdin = PIPE`?

from subprocess import Popen 

process = Popen(
    ["/bin/bash"], 
    stdin=sys.stdin, 
    stdout=sys.stdout, 
    stderr=sys.stderr, 
) 
process.wait() 

я могу интерактивно использовать Баш, вкладка работы и т.д.

Однако я хочу, чтобы контролировать то, что я посылаю стандартного ввода, так что я d:

import os 
import sys 
from subprocess import Popen, PIPE 
from select import select 

process = Popen(
    ["/bin/bash"], 
    stdin=PIPE, 
    stdout=sys.stdout, 
    stderr=sys.stderr, 
) 

while True: 
    if process.poll() is not None: 
     break 

    r, _, _ = select([sys.stdin], [], []) 

    if sys.stdin in r: 
     stdin = os.read(sys.stdin.fileno(), 1024) 
     # Do w/e I want with stdin 
     os.write(process.stdin.fileno(), stdin) 

process.wait() 

Но поведение просто не то же самое. Я попробовал другой подход (идя через PTY):

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

master, slave = os.openpty() 
stdin = sys.stdin.fileno() 

try: 
    tty.setraw(master) 
    ttyname = os.ttyname(slave) 

    def _preexec(): 
     os.setsid() 
     open(ttyname, "r+") 

    process = Popen(
     args=["/bin/bash"], 
     preexec_fn=_preexec, 
     stdin=slave, 
     stdout=sys.stdout, 
     stderr=sys.stderr, 
     close_fds=True, 
    ) 

    while True: 
     if process.poll() is not None: 
      break 

     r, _, _ = select([sys.stdin], [], []) 

     if sys.stdin in r: 
      os.write(master, os.read(stdin, 1024)) 
finally: 
    os.close(master) 
    os.close(slave) 

И поведение довольно близко, за исключением вкладки по-прежнему не работает. Ну, вкладка правильно отправлена, но мой терминал не показывает завершение, даже если это было сделано bash. Стрелки также показывают ^[[A вместо того, чтобы проходить историю.

Любая идея?

+0

вы знаете, что (обычно) вы могли бы заменить первый пример кода с: 'subprocess.call ([ '/ бен/Баш'])'? Также вы можете использовать напрямую (или посмотреть, как оно реализовано) 'pty.spawn (['/ bin/bash'], master_read, stdin_read)' (Вам нужно передать '-i',' -l' и т. Д.) ? – jfs

ответ

1

Все, что мне было нужно, это установить мой sys.stdout в сырье. Я также обнаружил 3 вещи:

  • Мне нужно восстановить настройки терминала на sys.stdout
  • subprocess.Popen имеет start_new_session аргумент, который делает то, что моя _preexec функция делает.
  • select.select принимает 4-й аргумент, который является таймаутом перед сдачей. Это позволяет мне избежать застревания в цикле выбора после выхода.

Окончательный код:

import os 
import sys 
import tty 
import termios 
import select 
import subprocess 

master, slave = os.openpty() 
stdin = sys.stdin.fileno() 

try: 
    old_settings = termios.tcgetattr(sys.stdout) 
    tty.setraw(sys.stdout) 

    process = subprocess.Popen(
     args=["/bin/bash"], 
     stdin=slave, 
     stdout=sys.stdout, 
     stderr=sys.stderr, 
     close_fds=True, 
     start_new_session=True, 
    ) 

    while True: 
     if process.poll() is not None: 
      break 

     r, _, _ = select.select([sys.stdin], [], [], 0.2) 

     if sys.stdin in r: 
      os.write(master, os.read(stdin, 1024)) 
finally: 
    termios.tcsetattr(sys.stdout, termios.TCSADRAIN, old_settings) 
    os.close(master) 
    os.close(slave) 
Смежные вопросы