2012-03-12 114 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)