2016-05-22 9 views
4

Ich habe den folgenden Code, der genau wie beabsichtigt funktioniert:Wie reproduziere ich `stdin = sys.stdin` mit` stdin = PIPE`?

from subprocess import Popen 

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

ich interaktiv bash, Registerkarte Werke usw.

jedoch verwenden können, ich will kontrollieren, was ich zu stdin senden, so dass ich D wie das Folgende zu arbeiten:

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() 

Aber das Verhalten ist einfach nicht das gleiche. Ich habe einen anderen Ansatz versucht (gehen durch eine 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) 

Und das Verhalten ist ziemlich nah dran, außer Tab funktioniert immer noch nicht. Nun, Tab wird korrekt gesendet, aber mein Terminal zeigt nicht die Vervollständigung, obwohl es von bash gemacht wurde. Pfeile zeigen auch ^[[A, anstatt durch die Geschichte zu gehen.

Irgendeine Idee?

+0

wissen Sie, dass (in der Regel) Sie Ihren ersten Codebeispiel ersetzen könnte mit: 'subprocess.call ([ '/ bin/bash'])'? Sie können auch direkt verwenden (oder sehen, wie es implementiert wird) 'pty.spawn (['/ bin/bash'], master_read, stdin_read)' (müssen Sie '-i',' -l', etc) übergeben ? – jfs

Antwort

1

Alles, was ich brauchte, war meine sys.stdout auf roh zu setzen. I 3 Dinge auch herausgefunden:

  • ich die Terminal-Einstellungen auf sys.stdout
  • subprocess.Popen hat ein start_new_session Argument wiederherstellen müssen, das tut, was meine _preexec Funktion tut.
  • select.select akzeptiert ein viertes Argument, das eine Zeitüberschreitung vor dem Aufgeben ist. Dadurch kann ich vermeiden, nach dem Beenden in der Auswahlschleife stecken zu bleiben.

Schlusscode:

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)