2012-03-12 117 views
11

我一直在玩Python的subprocess模块,我想用python的bash做一个“交互式会话”。我希望能够像Python那样在终端模拟器上读取Python的bash输出/写命令。我想一个代码示例更好的解释它:与Python的bash交互

>>> proc = subprocess.Popen(['/bin/bash']) 
>>> proc.communicate() 
('[email protected]:~/','') 
>>> proc.communicate('ls\n') 
('file1 file2 file3','') 

(很明显,它不工作的方式)是这样的可能,以及如何?

非常感谢

回答

10

尝试用这个例子:

import subprocess 

proc = subprocess.Popen(['/bin/bash'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
stdout = proc.communicate('ls -lash') 

print stdout 

你要了解更多关于标准输入,输出和错误。这看起来像好的演讲:http://www.doughellmann.com/PyMOTW/subprocess/

编辑:

又如:

>>> process = subprocess.Popen(['/bin/bash'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
>>> process.stdin.write('echo it works!\n') 
>>> process.stdout.readline() 
'it works!\n' 
>>> process.stdin.write('date\n') 
>>> process.stdout.readline() 
'wto, 13 mar 2012, 17:25:35 CET\n' 
>>> 
+1

第一个.communicate()调用很好,但是如果我尝试再次通信,会发生这种情况:'ValueError:关闭文件上的I/O操作。有没有办法让它继续运行? – justinas 2012-03-13 14:07:35

+0

看第二个例子。 – Adam 2012-03-13 16:28:51

+0

1-第一个代码示例可写为'stdout = subprocess.check_output(['ls','-lash'])''。要运行'bash'命令,你可以'check_output(“some && command $( jfs 2016-02-04 14:27:35

10

pexpect该类型的任务而设计的。这是纯粹的Python,它的灵感来源于老旧的TCL工具expect

+0

谢谢,似乎是一个很好的工具,但有没有办法实现这一点,没有pexpect? – justinas 2012-03-13 14:10:57

3

交互式bash进程预计将与TTY进行交互。要创建一个伪终端,请使用os.openpty()。这将返回一个slave_fd文件描述符,您可以使用它来打开stdin,stdout和stderr的文件。然后,您可以写入master_fd并从中读取,以与您的流程进行交互。请注意,如果您正在进行轻度复杂的交互,您还需要使用选择模块来确保您不会发生死锁。

3

我写了一个模块来促进* nix shell和python之间的交互。

def execute(cmd): 
if not _DEBUG_MODE: 
    ## Use bash; the default is sh 
    print 'Output of command ' + cmd + ' :' 
    subprocess.call(cmd, shell=True, executable='/bin/bash') 
    print '' 
else: 
    print 'The command is ' + cmd 
    print '' 

退房在GitHub上的整个东西:https://github.com/jerryzhujian9/ez.py/blob/master/ez/easyshell.py

+0

你现在可以通过点安装:pip install ez – 2015-05-22 02:41:23

1

用这个例子我在其他答案:https://stackoverflow.com/a/43012138/3555925

你可以在这个问题的答案更多的细节。

#!/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)