1

我有一个简单的 C 程序,它的工作方式如下: 请求输入 Print it 再次请求另一个输入 Print

现在我正在使用 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()

以下是几件事: 1. 我看到了 pexpect,但我认为我们应该提前给出程序要求的内容。2. 我可以重新打开封闭的子流程管道吗?

我将上面的脚本用作 CGI,我不知道为什么,但 subprocess.call 不起作用。谁能解释为什么?

编辑:

我正在做一个基于 Web 的项目,用户使用 C、C++ 或 JAVA 编写代码并在浏览器上执行它们。所以首先我想到了使用 PHP,但我找不到调用程序并以交互方式运行它们的方法。然后我看到了python子进程模块。当我使用 subprocess.call 时,解释器中的一切工作正常。但是相同的 python 程序将其保存为 .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;
 }
4

2 回答 2

0

我假设您的 C 应用程序显示一个提示并希望用户在同一行输入他们的输入,并且在您上面的readline()调用中您试图获得提示。

如果是这种情况,readline()将永远阻塞,因为它正在等待换行符并且永远不会看到它。如果您将此调用转换为简单的read(X)X一次读取的字节数是多少),那么您可能会有更好的运气,尽管您应该处理部分输入(即循环收集输入,直到您看到整个迅速的)。您可能会看到的唯一其他问题是 C 应用程序是否在提示用户之前没有刷新输出,但如果是这种情况,我希望您在交互式会话中也能看到该问题。

当在像 Apache 这样的网络服务器的上下文中运行时,使用类似这样的东西通常是一个坏主意,subprocess因为它们涉及分叉额外的进程,而这通常是一件很难管理的事情。这是因为 fork 进程复制了父进程的大部分状态,有时这会导致问题。我并不是说它不起作用,我只是说如果你不小心,你可能会给自己制造一些微妙的问题,如果这就是你使用subprocess.

但是,要提供更多有用的建议,您需要准确描述调用 subprocess 时看到的错误。例如,很可能会抛出一个异常,该异常可能会出现在您的网络服务器日志中——在此处重现该异常将是一个好的开始。

于 2013-01-12T10:24:56.737 回答
0

当我直接通过终端运行 C 程序时,它工作正常。但是当我使用上面提供的第二个代码运行相同的程序时,没有任何打印。

您看不到任何输出的原因是 C stdio 在程序以非交互模式运行时使用块缓冲。请参阅我的答案,该答案演示了几种解决方案:pty, stdbuf,pexpect . 如果您可以更改 C 代码,那么您也可以明确地 fflush 输出或使其成为 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 示例

#!/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;
}
于 2013-01-15T10:53:43.797 回答