2013-01-12 78 views
1

我有一個簡單的C程序,它的工作方式如下: 請輸入 打印 向另一個輸入 重新打印讀/寫同時蟒蛇subprocess.Popen

現在IAM的使用Python來調用這個程序。

import subprocess 

sobj = subprocess.Popen("./cprog", stdin = subprocess.PIPE, stdout = subprocess.PIPE) 
sobj.stdin.write("2 3\n") 
sobj.stdin.close() 
sobj.stdout.read() 

這工作正常。同溝通工作一樣好。

但是,當我嘗試做這樣的事情,它不會工作

sobj = subprocess.Popen("./cprog", stdin = subprocess.PIPE, stdout = subprocess.PIPE) 
sobj.stdout.readline() 
sobj.stdin.write("2 3\n") 
sobj.stdin.close() 
sobj.stdout.read() 

這裏有幾件事情: 我看見Pexpect的,但我認爲我們應該給予提前問什麼程序。 2.我可以重新打開關閉的子流程管道嗎?使用

蔭上面的腳本作爲CGI,我不知道爲什麼,但subprocess.call不會在工作。誰能解釋爲什麼?

編輯:

蔭做一個基於Web的項目,用戶使用C,C++或Java編寫代碼並執行他們的瀏覽器。所以我首先想到了使用PHP,但我找不到一種方法來調用程序並以交互方式運行它們。然後我看到了python子進程模塊。當我使用subprocess.call時,在解釋器中一切正常。但是,將它保存爲.cgi並在瀏覽器中打開它時,它不起作用。然後我開始查看subprocess.popen。但是,我需要在開始時輸入所有輸入,然後運行代碼。我想要做的是在瀏覽器中運行一個交互式會話。

編輯2: 所以我想要的是用戶在瀏覽器中運行程序,並在需要時輸入文本框中輸入,並將輸入重定向到子進程的標準輸入和基於它的輸出。

編輯3:cprog.c

#include <stdio.h> 
int main() { 
    int x; 
    printf("Enter value of x: \n"); 
    scanf("%d", &x); 
    printf("Value of x: %d\n", x); 
    return 0; 
} 
+0

只說 「是行不通的」,是永遠不會有幫助;你必須告訴我們你想要發生什麼,以及實際發生了什麼。 (兩次)。另外,這似乎是介於3到6個不同的問題之間,並且不清楚他們大多數都在問什麼,所以我不確定你期望得到的答案是什麼。 – abarnert

+2

清楚的答案是一個明確的問題:「我可以重新打開關閉的子流程管道嗎?」沒有。在Python中沒有「重新打開」API文件和文件類對象,或者它基於C或POSIX標準。如果關閉文件,則無法再使用它。所以,只有在完成之前不要關閉它。 (如果你已經關閉它,因爲你可能會立即提前退出函數,但問題是你可能不會退出,正確的解決方案是使用'finally(f)'或'try' /'finally '在罕見的情況下,這是不適當的。) – abarnert

+0

[這是答案的基礎](http://stackoverflow.com/a/11729467/4279)*「我想要做的是運行交互式會話在瀏覽器中。「*問題。 'client.py'只是將其輸入文本轉換爲大寫,而不是使用某種編程語言對其進行解釋。 – jfs

回答

0

我假設你的C應用程序顯示一個提示,希望用戶輸入在同一行他們的意見,並在您的通話readline()上面你試圖獲得提示。

如果是這樣的話,readline()永遠阻塞,因爲它在等待一個換行符,從來沒有看到它。如果此調用轉換爲一個簡單的read(X)(其中X是一個字節數一氣呵成讀),那麼你可能有更好的運氣,但你應該部分輸入處理(即循環各地蒐羅輸入,直到你看到整個提示)。你可能會看到其他唯一的問題是,如果C應用程序不提示用戶刷新之前的輸出,但是我希望你看到這個問題在互動環節,以及如果是這樣的話。

當一個像Apache的Web的環境下運行,那麼它通常是一個壞主意,用之類的東西subprocess因爲它們涉及分叉額外的過程,這就是通常相當管理一個棘手的事情。這是因爲fork過程重複了父項的大部分狀態,有時這可能會導致問題。我並不是說,它不會工作,我只是說你可以做一些細微的問題,爲自己,如果你不小心,如果這就是爲什麼您在使用subprocess麻煩它不會讓我感到吃驚。

但是,爲了提供更多有用的建議,您需要詳細描述調用子流程時看到的錯誤。例如,很可能會拋出異常,這可能會出現在您的Web服務器日誌中 - 重現此處將是一個好的開始。

+0

我之前沒有添加\ n。但現在即使在做同樣的問題之後。一旦檢查了第二和第三代碼片段。 Iam使用第二個Python程序作爲CGI腳本並在瀏覽器中運行。現在每當我運行這個腳本時,它都會繼續加載。當我在終端中運行時,相同的python腳本沒有顯示。我猜想它會進入無限循環。 – Murali

0

當我直接通過終端運行C程序時,它的工作正常。但是當我用第二代碼運行相同的程序時,我提供了上面沒有的任何打印。

您看不到任何輸出的原因在於,當程序以非交互模式運行時,C stdio使用塊緩衝。請參閱my answer that demonstrate several solutions: pty, stdbuf, pexpect。如果你可以更改C代碼,那麼你也可以fflush the output explicitly or make it unbuffered

如果您可以提供所有一次投入和產出是有界的,那麼你可以使用.communicate()

from subprocess import Popen, PIPE 

p = Popen(["./cprog"], stdin=PIPE, stdout=PIPE, stderr=PIPE, 
      universal_newlines=True) 
out, err = p.communicate("2\n") 

所以,我要的是用戶在瀏覽器中運行的程序,並在文本框中輸入輸入在需要時提供,並且輸入被重定向到子進程的標準輸入和基於它的輸出。

基於ws-cli example

#!/usr/bin/python 
"""WebSocket CLI interface. 

Install: pip install twisted txws 

Run: twistd -ny wscli.py 

Visit http://localhost:8080/ 
""" 
import sys 
from twisted.application import strports # pip install twisted 
from twisted.application import service 
from twisted.internet import protocol 
from twisted.python  import log 
from twisted.web.resource import Resource 
from twisted.web.server import Site 
from twisted.web.static import File 

from txws import WebSocketFactory # pip install txws 


class Protocol(protocol.Protocol): 
    def connectionMade(self): 
     from twisted.internet import reactor 
     log.msg("launch a new process on each new connection") 
     self.pp = ProcessProtocol() 
     self.pp.factory = self 
     reactor.spawnProcess(self.pp, command, command_args) 
    def dataReceived(self, data): 
     log.msg("redirect received data to process' stdin: %r" % data) 
     self.pp.transport.write(data) 
    def connectionLost(self, reason): 
     self.pp.transport.loseConnection() 

    def _send(self, data): 
     self.transport.write(data) # send back 


class ProcessProtocol(protocol.ProcessProtocol): 
    def connectionMade(self): 
     log.msg("connectionMade") 
    def outReceived(self, data): 
     log.msg("send stdout back %r" % data) 
     self._sendback(data) 
    def errReceived(self, data): 
     log.msg("send stderr back %r" % data) 
     self._sendback(data) 
    def processExited(self, reason): 
     log.msg("processExited") 
     self._sendback('program exited') 
    def processEnded(self, reason): 
     log.msg("processEnded") 

    def _sendback(self, data): 
     self.factory._send(data) 

command = './cprog' 
command_args = [command] 

application = service.Application("ws-cli") 

echofactory = protocol.Factory() 
echofactory.protocol = Protocol 
strports.service("tcp:8076:interface=127.0.0.1", 
       WebSocketFactory(echofactory)).setServiceParent(application) 

resource = Resource() 
resource.putChild('', File('index.html')) 
strports.service("tcp:8080:interface=127.0.0.1", 
       Site(resource)).setServiceParent(application) 

其中index.html

<!doctype html> 
<title>Send input to subprocess using websocket and echo the response</title> 
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"> 
</script> 
<script> 
// send keys to websocket and echo the response 
$(document).ready(function() { 
    // create websocket 
    if (! ("WebSocket" in window)) WebSocket = MozWebSocket; // firefox 
    var socket = new WebSocket("ws://localhost:8076"); 

    // open the socket 
    socket.onopen = function(event) { 
    // show server response 
    socket.onmessage = function(e) { 
     $("#output").text(e.data); 
    } 

    // sent input 
    $("#entry").keyup(function (e) { 
     socket.send($("#entry").attr("value")+"\n"); 
    }); 
    } 
}); 
</script> 
<pre id=output>Here you should see the output from the command</pre> 
<input type=text id=entry value="123"> 

而且cprog.c

#include <stdio.h> 
int main() { 
    int x = -1; 
    setbuf(stdout, NULL); // make stdout unbuffered 

    while (1) { 
    printf("Enter value of x: \n"); 
    if (scanf("%d", &x) != 1) 
     return 1; 
    printf("Value of x: %d\n", x); 
    } 
    return 0; 
} 
+0

將嘗試此代碼。 – Murali