簡體   English   中英

同時讀取/寫入python subprocess.Popen

[英]Read / Write simultaneously python subprocess.Popen

我有一個簡單的C程序,其工作方式如下:要求輸入打印它要求另一個輸入重新打印

現在,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()

以下是幾件事情:1.我看到了期望,但是我認為我們應該提前給出程序要求的內容。 2.我可以重新打開封閉的子過程管道嗎?

我使用上面的腳本作為CGI,我不知道為什么,但是subprocess.call不能在其中工作。 誰能解釋為什么?

編輯:

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

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

編輯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;
 }

我假設您的C應用程序顯示一個提示,並希望用戶在同一行上輸入他們的輸入,並且在您上面的readline()調用中,您試圖獲取該提示。

如果是這種情況, readline()將永遠阻塞,因為它正在等待換行符,再也看不到它了。 如果將此調用轉換為簡單的read(X) (其中X是一次讀取的字節數),則可能會遇到更好的運氣,盡管您應該處理部分輸入(即循環收集輸入直到已經看到了整個提示)。 您可能會看到的唯一其他問題是C應用程序是否在提示用戶之前沒有刷新輸出,但是我希望您也能在交互式會話中看到該問題。

當在像Apache這樣的Web服務器的上下文中運行時,使用subprocess東西通常是一個壞主意,因為它們涉及派生其他流程,而這通常是一件非常棘手的事情。 這是因為fork過程復制了許多父級狀態,有時這可能會引起問題。 我並不是說這行不通,我只是說如果您不小心會為自己帶來一些細微的問題,如果這就是為什么您在使用subprocess遇到麻煩,這也不會令我感到驚訝。

但是,要提供更多有用的建議,您需要准確描述調用子流程時看到的錯誤。 例如,很可能引發異常,該異常可能會出現在您的Web服務器日志中-重現這將是一個好的開始。

當我直接通過終端運行C程序時,它的工作正常。 但是,當我用第二個代碼運行同一程序時,上面沒有顯示任何內容。

您看不到任何輸出的原因是,當程序以非交互模式運行時,C stdio使用塊緩沖。 請參閱我的答案,該答案演示了幾種解決方案: ptystdbufpexpect 如果可以更改C代碼,則還可以顯式刷新輸出或使輸出不緩沖

如果您可以一次提供所有輸入並且輸出是有界的,則可以使用.communicate()

from subprocess import Popen, PIPE

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

所以我想要的是用戶在瀏覽器中運行程序,並在需要時在提供的文本框中輸入輸入,然后將輸入重定向到子流程的stdin並基於該輸出進行輸出。

基於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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM