提供从Python脚本到PHP的反馈

时间:2013-05-23 14:38:03

标签: php python linux command-line-interface interprocess

假设一个python脚本执行大量操作,并在用户运行时向用户输出进度报告:

$ script.py
Doing 1 of 100 operations...
Operation 1 succeeded with output "023948"
Doing 2 of 100 operations...
Operation 2 succeeded with output "893232"
Doing 3 of 100 operations...
Operation 3 succeeded with output "580217"
Doing 4 of 100 operations...
Operation 4 succeeded with output "228906"

每行输出显示在前一行后约2-3秒,因此整个脚本运行可能需要超过300秒。我想从PHP生成的网页运行此脚本,并在用户运行时将输出显示给用户。我知道我可以从AJAX轮询PHP脚本并获取最新消息以更新到屏幕,但如何在运行时从Python脚本获取实际输出exec()shell_exec()等函数仅在脚本运行完毕后返回输出

在最糟糕的情况下,我可以修改Python脚本以输出到文件,并让AJAX脚本连续ping一个PHP脚本,该脚本将读取文件并将其区分到上次读取(也存储在文件系统中) 。但我宁愿不出于各种原因修改Python脚本,另外我并不特别喜欢在文件系统上维护两个额外文件的想法。

2 个答案:

答案 0 :(得分:1)

使用unbuffered stdout,将参数-u传递给守护程序开头的python脚本(某些内容为python -u (your python script)

并且,在PHP中,使用诸如proc_open之类的东西来实时读取Python脚本打印的内容。

修改

正如评论中所指出的,我可以建议:

的Python:

import sys, atexit
sys.stdout = open(sys.argv.pop(), "w+") #Replaces stdout with a file returned from sys.argv (command line arguments)
def saveClose():
    sys.stdout.write("--%s--"%sys.stdout.name) #Just to indicate if the script closed
atexit.register(saveClose) #Register with atexit to execute the function at...exit

PHP :(名为daemon.php)

<?php
function execInBackground($cmd) {  // Put the program in background in Windows and *nix
    if (substr(php_uname(), 0, 7) == "Windows"){ // Detect if Windows 
        pclose(popen("start /B ". $cmd, "r")); // Use start /B (windows only) to open a background program in Windows
    } 
    else { 
        exec($cmd . " > /dev/null &");  // Open program as a daemon using & in *nix.
    } 
} 
if(isset($_GET["verify_id"])){ // We have ID?
  $content = file_get_contents($_GET["verify_id"]); // If yes, just load the file here (this is a security problem, but you can fix easily)
  echo $content; // Simply echoes the content of the file
}
else if(isset($_GET["daemon"])){
  $id = md5(uniqid(rand(), true)); // Create a unique hash
  execInBackground($_GET["daemon"]." ".$id); // Execute in the background passing the hash as a argument
  echo $id; // Echoes the hash
}
?>

Javascript :(命名为daemon.js并使用jQuery)

var cmds = {}
function receiveResult(cmd, id, callback){ // This function effectively receives the result from the execution of the program.
   var reg = new RegExp("--"+id+"--$");
   cmds_interval[id] = setInterval(function(){
       $.ajax({
         url:"daemon.php",
         dataType: "text",
         data: {"verify_id":id},
         success: function(msg){
           if(reg.test(msg)){ // Is the script closed?
              msg = msg.replace(reg, ""); // If yes, removes it from the end of the string
              clearInterval(cmds_interval[id]); // And clear the interval
           }
           callback(msg, id, cmd); // Callback with the message from the stdout 
         }
      });
   }, 1000); // refreshes with a interval of 1 second
   return cmds_interval[id];
}

function exec(cmd, callback){
  $.ajax({
    url:"daemon.php",
    dataType: "text",
    data: {"daemon":cmd},
    success: function(id){
       receiveResult(cmd, id, callback);
    }
  });
}

使用示例

在HTML中:

<pre id="console"></pre>
<script language="javascript" type="text/javascript" src="path/to/jquery.js"></script>
<script language="javascript" type="text/javascript" src="path/to/daemon.js"></script>
<script language="javascript" type="text/javascript" src="path/to/demo.js"></script>

在demo.js中:( Javascript,也使用jQuery):

exec("python script.py", function(msg){ 
    $("#console").html(msg);
});

这应该有效。如果它不起作用,等待明天,因为我现在退出。祝好运。

PS:如果代码不起作用,您可以将代码视为您想要的算法示例。

PS 2:execInBackground功能来自此处:http://www.php.net/manual/en/function.exec.php#86329

答案 1 :(得分:1)

您可以使用proc_open执行此操作。

但是如果你想使用文件备选方案,则无需修改python脚本,只需输出到文件即可。

$execution = shell_exec("script.py > /ouput/file/path &");

然后通过ajax读取文件。