2013-01-12 81 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