2011-11-27 66 views
4

我想從子進程獲取輸出,然後根據前面的輸出爲該進程提供命令。當程序需要進一步輸入時,我需要做這個可變的次數。 (如果可能,我還需要能夠隱藏子進程命令提示符)。從python子進程獲取輸出並給出命令

我覺得這將是一件容易的事,因爲我已經看到這個問題在2003年的帖子中討論過,它接近2012年,它似乎是一個相當普遍的需求,真的好像它應該是一個基本的部分任何編程語言。顯然,我錯了,不知何故,差不多9年後,仍然沒有以穩定,非破壞性,獨立於平臺的方式完成這項任務的標準方法!

我對文件I/O和緩衝或線程並不十分了解,所以我寧願選擇一個儘可能簡單的解決方案。如果有一個模塊實現了與python 3.x兼容的模塊,我將非常樂意下載它。我意識到有多個問題基本上提出了同樣的問題,但我還沒有找到解決我試圖完成的簡單任務的答案。

這是我迄今爲止基於各種來源的代碼;但是我絕對不知道下一步該怎麼做。我所有的嘗試都以失敗告終,有些設法使用了我CPU的100%(基本上什麼都不做),並且不會退出。

import subprocess 
from subprocess import Popen, PIPE 
p = Popen(r'C:\postgis_testing\shellcomm.bat',stdin=PIPE,stdout=PIPE,stderr=subprocess.STDOUT shell=True) 
stdout,stdin = p.communicate(b'command string') 

如果我的問題是不清楚我張貼的樣本批處理文件中,我展示了一種情況,即有必要多個命令發送給子進程(如果你輸入了不正確的命令字符串程序文字循環)。

@echo off 
:looper 
set INPUT= 
set /P INPUT=Type the correct command string: 
if "%INPUT%" == "command string" (echo you are correct) else (goto looper) 

如果有人可以幫助我,我會非常感激,我相信很多人也會這樣!這裏

編輯是使用eryksun的代碼(下一篇文章)的功能代碼:

import subprocess 
import threading 
import time 
import sys 

try: 
    import queue 
except ImportError: 
    import Queue as queue 

def read_stdout(stdout, q, p): 
    it = iter(lambda: stdout.read(1), b'') 
    for c in it: 
     q.put(c) 
     if stdout.closed: 
      break 

_encoding = getattr(sys.stdout, 'encoding', 'latin-1') 
def get_stdout(q, encoding=_encoding): 
    out = [] 
    while 1: 
     try: 
      out.append(q.get(timeout=0.2)) 
     except queue.Empty: 
      break 
    return b''.join(out).rstrip().decode(encoding) 

def printout(q): 
    outdata = get_stdout(q) 
    if outdata: 
     print('Output: %s' % outdata) 

if __name__ == '__main__': 
    #setup 
    p = subprocess.Popen(['shellcomm.bat'], stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, 
        bufsize=0, shell=True) # I put shell=True to hide prompt 
    q = queue.Queue() 
    encoding = getattr(sys.stdin, 'encoding', 'utf-8') 

    #for reading stdout 
    t = threading.Thread(target=read_stdout, args=(p.stdout, q, p)) 
    t.daemon = True 
    t.start() 

    #command loop 
    while p.poll() is None: 
     printout(q) 
     cmd = input('Input: ') 
     cmd = (cmd + '\n').encode(encoding) 
     p.stdin.write(cmd) 
     time.sleep(0.1) # I added this to give some time to check for closure (otherwise it doesn't work) 

    #tear down 
    for n in range(4): 
     rc = p.poll() 
     if rc is not None: 
      break 
     time.sleep(0.25) 
    else: 
     p.terminate() 
     rc = p.poll() 
     if rc is None: 
      rc = 1 

    printout(q) 
    print('Return Code: %d' % rc) 

然而,當腳本是從命令運行提示會發生以下情況:

C:\Users\username>python C:\postgis_testing\shellcomm7.py 
Input: sth 
Traceback (most recent call last): 
File "C:\postgis_testing\shellcomm7.py", line 51, in <module> 
    p.stdin.write(cmd) 
IOError: [Errno 22] Invalid argument 

看來,程序在從命令提示符運行時關閉。有任何想法嗎?

回答

3

此演示使用專用線程從stdout讀取。如果你四處搜索,我相信你可以在面向對象的界面中找到更完整的實現。至少我可以這麼說,這對於我在Python 2.7.2和3.2.2中提供的批處理文件都適用。

shellcomm.bat:

@echo off 
echo Command Loop Test 
echo. 
:looper 
set INPUT= 
set /P INPUT=Type the correct command string: 
if "%INPUT%" == "command string" (echo you are correct) else (goto looper) 

這是我得到的輸出基於命令序列 「錯了」, 「還是錯的」,和 「字符串命令」:

Output: 
Command Loop Test 

Type the correct command string: 
Input: wrong 
Output: 
Type the correct command string: 
Input: still wrong 
Output: 
Type the correct command string: 
Input: command string 
Output: 
you are correct 

Return Code: 0 

對於讀取管道輸出,readline有時可能會有效,但批處理文件中的set /P INPUT自然不會寫行結束。所以相反,我用lambda: stdout.read(1)一次讀取一個字節(效率不高,但工作正常)。閱讀功能將數據放入隊列中。主線程在寫入命令後從隊列中獲取輸出。在get調用中使用超時會使其等待一小段時間,以確保程序正在等待輸入。相反,您可以檢查輸出的提示,以瞭解程序何時需要輸入。所有這一切,你不能指望這樣的設置通用,因爲你試圖與之交互的控制檯程序可能會在管道輸出時緩衝其輸出。在Unix系統中,有一些實用程序命令可用,您可以將其插入到管道中,以將緩衝區修改爲非緩衝區,行緩衝區或給定大小 - 例如stdbuf。還有一些方法可以讓程序認爲它已連接到一個pty(請參閱pexpect)。但是,如果您無法訪問程序的源代碼以使用setvbuf明確設置緩衝區,則我無法在Windows上解決此問題。

import subprocess 
import threading 
import time 
import sys 

if sys.version_info.major >= 3: 
    import queue 
else: 
    import Queue as queue 
    input = raw_input 

def read_stdout(stdout, q): 
    it = iter(lambda: stdout.read(1), b'') 
    for c in it: 
     q.put(c) 
     if stdout.closed: 
      break 

_encoding = getattr(sys.stdout, 'encoding', 'latin-1') 
def get_stdout(q, encoding=_encoding): 
    out = [] 
    while 1: 
     try: 
      out.append(q.get(timeout=0.2)) 
     except queue.Empty: 
      break 
    return b''.join(out).rstrip().decode(encoding) 

def printout(q): 
    outdata = get_stdout(q) 
    if outdata: 
     print('Output:\n%s' % outdata) 

if __name__ == '__main__': 

    ARGS = ["shellcomm.bat"] ### Modify this 

    #setup 
    p = subprocess.Popen(ARGS, bufsize=0, stdin=subprocess.PIPE, 
         stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    q = queue.Queue() 
    encoding = getattr(sys.stdin, 'encoding', 'utf-8') 

    #for reading stdout 
    t = threading.Thread(target=read_stdout, args=(p.stdout, q)) 
    t.daemon = True 
    t.start() 

    #command loop 
    while 1: 
     printout(q) 
     if p.poll() is not None or p.stdin.closed: 
      break 
     cmd = input('Input: ') 
     cmd = (cmd + '\n').encode(encoding) 
     p.stdin.write(cmd) 

    #tear down 
    for n in range(4): 
     rc = p.poll() 
     if rc is not None: 
      break 
     time.sleep(0.25) 
    else: 
     p.terminate() 
     rc = p.poll() 
     if rc is None: 
      rc = 1 

    printout(q) 
    print('\nReturn Code: %d' % rc) 
+0

非常感謝你爲這個它完全處於空閒,但是當我運行修改後的代碼(見我在原來的職位編輯)它給了我下面的錯誤 C:\ Users \用戶名>蟒蛇Ç :\ postgis_testing \ shellcomm7.py 輸入:某物 回溯(最近通話最後一個): 文件 「C:\ postgis_testing \ shellcomm7.py」 51行,在 p.stdin.write(CMD) IO錯誤: [Errno 22]無效的參數 從命令提示符運行時,程序似乎關閉。有任何想法嗎? – THX1138

+0

@ THX1138:我修改了循環以查詢進程並在請求輸入之前檢查stdin。如果流程退出,它會中斷。我也從'read_stdout'參數中刪除了過程對象;它在我的初稿中錯誤地留下了。 – eryksun

+0

當我從命令提示符運行新代碼時,出現以下錯誤: C:\ Users \ username> python C:\ postgis_testing \ shellcomm8.py Traceback(最近調用最後一次): 文件「C:\ postgis_testing \ shellcomm8.py 「第38行,在 BUFSIZE = 0) 文件 」C:\ Python32 \ lib中\ subprocess.py「,線路741,在__init__ restore_signals,start_new_session) 文件」 C:\ Python32 \ lib中\ subprocess.py「,第960行,在_execute_child startupinfo) WindowsError:[錯誤2]系統找不到指定的文件 – THX1138

相關問題