我有以下代码完全按预期工作:
from subprocess import Popen process = Popen( ["/bin/bash"],stdin=sys.stdin,stdout=sys.stdout,stderr=sys.stderr,) process.wait()
我可以交互式地使用bash,tab works等.
但是,我想控制我发送到stdin的内容,所以我希望以下工作:
import os import sys from subprocess import Popen,PIPE from select import select process = Popen( ["/bin/bash"],stdin=PIPE,) 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,close_fds=True,) while True: if process.poll() is not None: break r,[]) if sys.stdin in r: os.write(master,os.read(stdin,1024)) finally: os.close(master) os.close(slave)
并且行为非常接近,除了tab仍然不起作用.好吧,tab已正确发送,但我的终端没有显示完成,即使它是由bash完成的.箭头也显示^ [[而不是通过历史记录.
任何的想法?
解决方法
我只需要将我的sys.stdout设置为raw.我还发现了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"],start_new_session=True,_ = select.select([sys.stdin],0.2) if sys.stdin in r: os.write(master,1024)) finally: termios.tcsetattr(sys.stdout,termios.TCSADRAIN,old_settings) os.close(master) os.close(slave)