2016-05-22 44 views
4

我有以下的代碼,工程完全按照預期:如何用`stdin = PIPE`重現`stdin = sys.stdin`?

from subprocess import Popen 

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

我可以交互使用bash,標籤作品等

不過,我想控制我發送到標準輸入,所以我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接受第四個參數,這是放棄之前的超時。它可以避免在退出後卡在選擇循環中。

最終代碼:

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)